Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandshmbackingstore.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
#
include
"qwaylandshmbackingstore_p.h"
4
#
include
"qwaylandwindow_p.h"
5
#
include
"qwaylandsubsurface_p.h"
6
#
include
"qwaylanddisplay_p.h"
7
#
include
"qwaylandscreen_p.h"
8
#
include
"qwaylandabstractdecoration_p.h"
9
10
#
include
<
QtCore
/
qdebug
.
h
>
11
#
include
<
QtCore
/
qstandardpaths
.
h
>
12
#
include
<
QtCore
/
qtemporaryfile
.
h
>
13
#
include
<
QtGui
/
QPainter
>
14
#
include
<
QtGui
/
QTransform
>
15
#
include
<
QMutexLocker
>
16
17
#
include
<
QtWaylandClient
/
private
/
wayland
-
wayland
-
client
-
protocol
.
h
>
18
19
#
include
<
memory
>
20
21
#
include
<
fcntl
.
h
>
22
#
include
<
unistd
.
h
>
23
#
include
<
sys
/
mman
.
h
>
24
25
#
ifdef
Q_OS_LINUX
26
#
include
<
sys
/
syscall
.
h
>
27
// from linux/memfd.h:
28
#
ifndef
MFD_CLOEXEC
29
#
define
MFD_CLOEXEC
0x0001U
30
#
endif
31
#
ifndef
MFD_ALLOW_SEALING
32
#
define
MFD_ALLOW_SEALING
0x0002U
33
#
endif
34
// from bits/fcntl-linux.h
35
#
ifndef
F_ADD_SEALS
36
#
define
F_ADD_SEALS
1033
37
#
endif
38
#
ifndef
F_SEAL_SEAL
39
#
define
F_SEAL_SEAL
0x0001
40
#
endif
41
#
ifndef
F_SEAL_SHRINK
42
#
define
F_SEAL_SHRINK
0x0002
43
#
endif
44
#
endif
45
46
QT_BEGIN_NAMESPACE
47
48
extern
void
qt_scrollRectInImage
(QImage &,
const
QRect &,
const
QPoint &);
49
50
namespace
QtWaylandClient
{
51
52
QWaylandShmBuffer
::
QWaylandShmBuffer
(
QWaylandDisplay
*
display
,
53
const
QSize
&
size
,
QImage
::
Format
format
,
qreal
scale
)
54
:
mDirtyRegion
(
QRect
(
QPoint
(0, 0),
size
/
scale
))
55
{
56
int
stride
=
size
.
width
() * 4;
57
int
alloc
=
stride
*
size
.
height
();
58
int
fd
= -1;
59
60
#
ifdef
SYS_memfd_create
61
fd
=
syscall
(
SYS_memfd_create
,
"wayland-shm"
,
MFD_CLOEXEC
|
MFD_ALLOW_SEALING
);
62
if
(
fd
>= 0)
63
fcntl
(
fd
,
F_ADD_SEALS
,
F_SEAL_SHRINK
|
F_SEAL_SEAL
);
64
#
endif
65
66
std
::
unique_ptr
<
QFile
>
filePointer
;
67
bool
opened
;
68
69
if
(
fd
== -1) {
70
auto
tmpFile
=
71
std
::
make_unique
<
QTemporaryFile
>(
QStandardPaths
::
writableLocation
(
QStandardPaths
::
RuntimeLocation
) +
72
QLatin1String
(
"/wayland-shm-XXXXXX"
));
73
opened
=
tmpFile
->
open
();
74
filePointer
=
std
::
move
(
tmpFile
);
75
}
else
{
76
auto
file
=
std
::
make_unique
<
QFile
>();
77
opened
=
file
->
open
(
fd
,
QIODevice
::
ReadWrite
|
QIODevice
::
Unbuffered
,
QFile
::
AutoCloseHandle
);
78
filePointer
=
std
::
move
(
file
);
79
}
80
// NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does.
81
if
(!
opened
|| !
filePointer
->
resize
(
alloc
)) {
82
qWarning
(
"QWaylandShmBuffer: failed: %s"
,
qUtf8Printable
(
filePointer
->
errorString
()));
83
return
;
84
}
85
fd
=
filePointer
->
handle
();
86
87
// map ourselves: QFile::map() will unmap when the object is destroyed,
88
// but we want this mapping to persist (unmapping in destructor)
89
uchar
*
data
= (
uchar
*)
90
mmap
(
nullptr
,
alloc
, PROT_READ | PROT_WRITE, MAP_SHARED,
fd
, 0);
91
if
(
data
== (
uchar
*)
MAP_FAILED
) {
92
qErrnoWarning
(
"QWaylandShmBuffer: mmap failed"
);
93
return
;
94
}
95
96
QWaylandShm
*
shm
=
display
->
shm
();
97
wl_shm_format
wl_format
=
shm
->
formatFrom
(
format
);
98
mImage
=
QImage
(
data
,
size
.
width
(),
size
.
height
(),
stride
,
format
);
99
mImage
.
setDevicePixelRatio
(
scale
);
100
101
mShmPool
=
wl_shm_create_pool
(
shm
->
object
(),
fd
,
alloc
);
102
init
(
wl_shm_pool_create_buffer
(
mShmPool
,0,
size
.
width
(),
size
.
height
(),
103
stride
,
wl_format
));
104
}
105
106
QWaylandShmBuffer
::~
QWaylandShmBuffer
(
void
)
107
{
108
delete
mMarginsImage
;
109
if
(
mImage
.
constBits
())
110
munmap
((
void
*)
mImage
.
constBits
(),
mImage
.
sizeInBytes
());
111
if
(
mShmPool
)
112
wl_shm_pool_destroy
(
mShmPool
);
113
}
114
115
QImage
*
QWaylandShmBuffer
::
imageInsideMargins
(
const
QMargins
&
marginsIn
)
116
{
117
QMargins
margins
=
marginsIn
*
mImage
.
devicePixelRatio
();
118
119
if
(!
margins
.
isNull
() &&
margins
!=
mMargins
) {
120
if
(
mMarginsImage
) {
121
delete
mMarginsImage
;
122
}
123
uchar
*
bits
=
const_cast
<
uchar
*>(
mImage
.
constBits
());
124
uchar
*
b_s_data
=
bits
+
margins
.
top
() *
mImage
.
bytesPerLine
() +
margins
.
left
() * 4;
125
int
b_s_width
=
mImage
.
size
().
width
() -
margins
.
left
() -
margins
.
right
();
126
int
b_s_height
=
mImage
.
size
().
height
() -
margins
.
top
() -
margins
.
bottom
();
127
mMarginsImage
=
new
QImage
(
b_s_data
,
b_s_width
,
b_s_height
,
mImage
.
bytesPerLine
(),
mImage
.
format
());
128
mMarginsImage
->
setDevicePixelRatio
(
mImage
.
devicePixelRatio
());
129
}
130
if
(
margins
.
isNull
()) {
131
delete
mMarginsImage
;
132
mMarginsImage
=
nullptr
;
133
}
134
135
mMargins
=
margins
;
136
if
(!
mMarginsImage
)
137
return
&
mImage
;
138
139
return
mMarginsImage
;
140
141
}
142
143
QWaylandShmBackingStore
::
QWaylandShmBackingStore
(
QWindow
*
window
,
QWaylandDisplay
*
display
)
144
:
QPlatformBackingStore
(
window
)
145
,
mDisplay
(
display
)
146
{
147
QObject
::
connect
(
mDisplay
, &
QWaylandDisplay
::
connected
,
window
, [
this
]() {
148
auto
copy
=
mBuffers
;
149
// clear available buffers so we create new ones
150
// actual deletion is deferred till after resize call so we can copy
151
// contents from the back buffer
152
mBuffers
.
clear
();
153
mFrontBuffer
=
nullptr
;
154
// recreateBackBufferIfNeeded always resets mBackBuffer
155
if
(
mRequestedSize
.
isValid
() &&
waylandWindow
())
156
recreateBackBufferIfNeeded
();
157
qDeleteAll
(
copy
);
158
});
159
}
160
161
QWaylandShmBackingStore
::~
QWaylandShmBackingStore
()
162
{
163
if
(
QWaylandWindow
*
w
=
waylandWindow
())
164
w
->
setBackingStore
(
nullptr
);
165
166
// if (mFrontBuffer == waylandWindow()->attached())
167
// waylandWindow()->attach(0);
168
169
qDeleteAll
(
mBuffers
);
170
}
171
172
QPaintDevice
*
QWaylandShmBackingStore
::
paintDevice
()
173
{
174
return
contentSurface
();
175
}
176
177
void
QWaylandShmBackingStore
::
updateDirtyStates
(
const
QRegion
&
region
)
178
{
179
// Update dirty state of buffers based on what was painted. The back buffer will
180
// not be dirty since we already painted on it, while other buffers will become dirty.
181
for
(
QWaylandShmBuffer
*
b
:
std
::
as_const
(
mBuffers
)) {
182
if
(
b
!=
mBackBuffer
)
183
b
->
dirtyRegion
() +=
region
;
184
}
185
}
186
187
void
QWaylandShmBackingStore
::
beginPaint
(
const
QRegion
&
region
)
188
{
189
mPainting
=
true
;
190
waylandWindow
()->
setBackingStore
(
this
);
191
const
bool
bufferWasRecreated
=
recreateBackBufferIfNeeded
();
192
193
const
QMargins
margins
=
windowDecorationMargins
();
194
updateDirtyStates
(
region
.
translated
(
margins
.
left
(),
margins
.
top
()));
195
196
// Although undocumented, QBackingStore::beginPaint expects the painted region
197
// to be cleared before use if the window has a surface format with an alpha.
198
// Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
199
if
(!
bufferWasRecreated
&&
window
()->
format
().
hasAlpha
()) {
200
QPainter
p
(
paintDevice
());
201
p
.
setCompositionMode
(
QPainter
::
CompositionMode_Source
);
202
const
QColor
blank
=
Qt
::
transparent
;
203
for
(
const
QRect
&
rect
:
region
)
204
p
.
fillRect
(
rect
,
blank
);
205
}
206
}
207
208
void
QWaylandShmBackingStore
::
endPaint
()
209
{
210
mPainting
=
false
;
211
if
(
mPendingFlush
)
212
flush
(
window
(),
mPendingRegion
,
QPoint
());
213
}
214
215
// Inspired by QCALayerBackingStore.
216
bool
QWaylandShmBackingStore
::
scroll
(
const
QRegion
&
region
,
int
dx
,
int
dy
)
217
{
218
if
(!
mBackBuffer
)
219
return
false
;
220
221
QImage
*
backBufferImage
=
mBackBuffer
->
image
();
222
const
qreal
devicePixelRatio
=
backBufferImage
->
devicePixelRatio
();
223
224
// On Wayland, the window can have a device pixel ratio different from
225
// the window/screen, therefore we cannot rely on QHighDpi here, cf. QBackingStore::scroll.
226
// With fractional scaling we cannot easily scroll the existing pixels.
227
if
(!
qFuzzyIsNull
(
devicePixelRatio
-
static_cast
<
int
>(
devicePixelRatio
)))
228
return
false
;
229
230
const
QPoint
scrollDelta
(
dx
,
dy
);
231
const
QMargins
margins
=
windowDecorationMargins
();
232
const
QRegion
adjustedRegion
=
region
.
translated
(
margins
.
left
(),
margins
.
top
());
233
234
const
QRect
boundingRect
=
adjustedRegion
.
boundingRect
();
235
const
QPoint
devicePixelDelta
=
scrollDelta
*
devicePixelRatio
;
236
237
qt_scrollRectInImage
(*
backBufferImage
,
238
QRect
(
boundingRect
.
topLeft
() *
devicePixelRatio
,
239
boundingRect
.
size
() *
devicePixelRatio
),
240
devicePixelDelta
);
241
242
// We do not mark the source region as dirty, even though it technically has "moved".
243
// This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
244
updateDirtyStates
(
adjustedRegion
.
translated
(
scrollDelta
));
245
246
return
true
;
247
}
248
249
void
QWaylandShmBackingStore
::
flush
(
QWindow
*
window
,
const
QRegion
&
region
,
const
QPoint
&
offset
)
250
{
251
Q_UNUSED
(
offset
)
252
// Invoked when the window is of type RasterSurface or when the window is
253
// RasterGLSurface and there are no child widgets requiring OpenGL composition.
254
255
// For the case of RasterGLSurface + having to compose, the composeAndFlush() is
256
// called instead. The default implementation from QPlatformBackingStore is sufficient
257
// however so no need to reimplement that.
258
if
(
window
!=
this
->
window
()) {
259
auto
waylandWindow
=
static_cast
<
QWaylandWindow
*>(
window
->
handle
());
260
auto
newBuffer
=
new
QWaylandShmBuffer
(
mDisplay
,
window
->
size
(),
mBackBuffer
->
image
()->
format
(),
mBackBuffer
->
scale
());
261
newBuffer
->
setDeleteOnRelease
(
true
);
262
QRect
sourceRect
(
window
->
position
(),
window
->
size
());
263
QPainter
painter
(
newBuffer
->
image
());
264
painter
.
drawImage
(
QPoint
(0, 0), *
mBackBuffer
->
image
(),
sourceRect
);
265
waylandWindow
->
safeCommit
(
newBuffer
,
region
);
266
return
;
267
}
268
269
if
(
mPainting
) {
270
mPendingRegion
|=
region
;
271
mPendingFlush
=
true
;
272
return
;
273
}
274
275
mPendingFlush
=
false
;
276
mPendingRegion
=
QRegion
();
277
278
if
(
windowDecoration
() &&
windowDecoration
()->
isDirty
())
279
updateDecorations
();
280
281
mFrontBuffer
=
mBackBuffer
;
282
283
QMargins
margins
=
windowDecorationMargins
();
284
waylandWindow
()->
safeCommit
(
mFrontBuffer
,
region
.
translated
(
margins
.
left
(),
margins
.
top
()));
285
}
286
287
void
QWaylandShmBackingStore
::
resize
(
const
QSize
&
size
,
const
QRegion
&)
288
{
289
mRequestedSize
=
size
;
290
}
291
292
QWaylandShmBuffer
*
QWaylandShmBackingStore
::
getBuffer
(
const
QSize
&
size
,
bool
&
bufferWasRecreated
)
293
{
294
static
const
int
MAX_BUFFERS
= 5;
295
static
const
int
MAX_AGE
= 10 *
MAX_BUFFERS
;
296
bufferWasRecreated
=
false
;
297
298
// Prune buffers that have not been used in a while or with different size.
299
for
(
auto
i
=
mBuffers
.
size
() - 1;
i
>= 0; --
i
) {
300
QWaylandShmBuffer
*
buffer
=
mBuffers
[
i
];
301
if
(
buffer
->
age
() >
MAX_AGE
||
buffer
->
size
() !=
size
) {
302
mBuffers
.
removeAt
(
i
);
303
if
(
mBackBuffer
==
buffer
)
304
mBackBuffer
=
nullptr
;
305
delete
buffer
;
306
}
307
}
308
309
QWaylandShmBuffer
*
buffer
=
nullptr
;
310
for
(
QWaylandShmBuffer
*
candidate
:
std
::
as_const
(
mBuffers
)) {
311
if
(
candidate
->
busy
())
312
continue
;
313
314
if
(!
buffer
||
candidate
->
age
() <
buffer
->
age
())
315
buffer
=
candidate
;
316
}
317
318
if
(
buffer
)
319
return
buffer
;
320
321
if
(
mBuffers
.
size
() <
MAX_BUFFERS
) {
322
QImage
::
Format
format
=
QImage
::
Format_ARGB32_Premultiplied
;
323
if
(!
waylandWindow
()->
format
().
hasAlpha
())
324
format
=
QImage
::
Format_RGB32
;
325
QWaylandShmBuffer
*
b
=
new
QWaylandShmBuffer
(
mDisplay
,
size
,
format
,
waylandWindow
()->
scale
());
326
bufferWasRecreated
=
true
;
327
mBuffers
.
push_front
(
b
);
328
return
b
;
329
}
330
return
nullptr
;
331
}
332
333
bool
QWaylandShmBackingStore
::
recreateBackBufferIfNeeded
()
334
{
335
bool
bufferWasRecreated
=
false
;
336
QMargins
margins
=
windowDecorationMargins
();
337
qreal
scale
=
waylandWindow
()->
scale
();
338
const
QSize
sizeWithMargins
= (
mRequestedSize
+
QSize
(
margins
.
left
() +
margins
.
right
(),
margins
.
top
() +
margins
.
bottom
())) *
scale
;
339
340
// We look for a free buffer to draw into. If the buffer is not the last buffer we used,
341
// that is mBackBuffer, and the size is the same we copy the damaged content into the new
342
// buffer so that QPainter is happy to find the stuff it had drawn before. If the new
343
// buffer has a different size it needs to be redrawn completely anyway, and if the buffer
344
// is the same the stuff is there already.
345
// You can exercise the different codepaths with weston, switching between the gl and the
346
// pixman renderer. With the gl renderer release events are sent early so we can effectively
347
// run single buffered, while with the pixman renderer we have to use two.
348
QWaylandShmBuffer
*
buffer
=
getBuffer
(
sizeWithMargins
,
bufferWasRecreated
);
349
while
(!
buffer
) {
350
qCDebug
(
lcWaylandBackingstore
,
"QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor..."
);
351
352
mDisplay
->
blockingReadEvents
();
353
buffer
=
getBuffer
(
sizeWithMargins
,
bufferWasRecreated
);
354
}
355
356
qsizetype
oldSizeInBytes
=
mBackBuffer
?
mBackBuffer
->
image
()->
sizeInBytes
() : 0;
357
qsizetype
newSizeInBytes
=
buffer
->
image
()->
sizeInBytes
();
358
359
// mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
360
if
(
mBackBuffer
!=
buffer
&&
oldSizeInBytes
==
newSizeInBytes
) {
361
Q_ASSERT
(
mBackBuffer
);
362
const
QImage
*
sourceImage
=
mBackBuffer
->
image
();
363
QImage
*
targetImage
=
buffer
->
image
();
364
365
QPainter
painter
(
targetImage
);
366
painter
.
setCompositionMode
(
QPainter
::
CompositionMode_Source
);
367
painter
.
setClipRegion
(
buffer
->
dirtyRegion
());
368
painter
.
drawImage
(
QRectF
(
QPointF
(),
targetImage
->
deviceIndependentSize
()), *
sourceImage
,
sourceImage
->
rect
());
369
}
370
371
mBackBuffer
=
buffer
;
372
373
for
(
QWaylandShmBuffer
*
buffer
:
std
::
as_const
(
mBuffers
)) {
374
if
(
mBackBuffer
==
buffer
) {
375
buffer
->
setAge
(0);
376
}
else
{
377
buffer
->
setAge
(
buffer
->
age
() + 1);
378
}
379
}
380
381
if
(
windowDecoration
() &&
window
()->
isVisible
() &&
oldSizeInBytes
!=
newSizeInBytes
)
382
windowDecoration
()->
update
();
383
384
buffer
->
dirtyRegion
() =
QRegion
();
385
386
return
bufferWasRecreated
;
387
}
388
389
QImage
*
QWaylandShmBackingStore
::
entireSurface
()
const
390
{
391
return
mBackBuffer
->
image
();
392
}
393
394
QImage
*
QWaylandShmBackingStore
::
contentSurface
()
const
395
{
396
return
windowDecoration
() ?
mBackBuffer
->
imageInsideMargins
(
windowDecorationMargins
()) :
mBackBuffer
->
image
();
397
}
398
399
void
QWaylandShmBackingStore
::
updateDecorations
()
400
{
401
QPainter
decorationPainter
(
entireSurface
());
402
decorationPainter
.
setCompositionMode
(
QPainter
::
CompositionMode_Source
);
403
QImage
sourceImage
=
windowDecoration
()->
contentImage
();
404
405
qreal
dp
=
sourceImage
.
devicePixelRatio
();
406
int
dpWidth
=
int
(
sourceImage
.
width
() /
dp
);
407
int
dpHeight
=
int
(
sourceImage
.
height
() /
dp
);
408
QTransform
sourceMatrix
;
409
sourceMatrix
.
scale
(
dp
,
dp
);
410
QRect
target
;
// needs to be in device independent pixels
411
QRegion
dirtyRegion
;
412
413
//Top
414
target
.
setX
(0);
415
target
.
setY
(0);
416
target
.
setWidth
(
dpWidth
);
417
target
.
setHeight
(
windowDecorationMargins
().
top
());
418
decorationPainter
.
drawImage
(
target
,
sourceImage
,
sourceMatrix
.
mapRect
(
target
));
419
dirtyRegion
+=
target
;
420
421
//Left
422
target
.
setWidth
(
windowDecorationMargins
().
left
());
423
target
.
setHeight
(
dpHeight
);
424
decorationPainter
.
drawImage
(
target
,
sourceImage
,
sourceMatrix
.
mapRect
(
target
));
425
dirtyRegion
+=
target
;
426
427
//Right
428
target
.
setX
(
dpWidth
-
windowDecorationMargins
().
right
());
429
target
.
setWidth
(
windowDecorationMargins
().
right
());
430
decorationPainter
.
drawImage
(
target
,
sourceImage
,
sourceMatrix
.
mapRect
(
target
));
431
dirtyRegion
+=
target
;
432
433
//Bottom
434
target
.
setX
(0);
435
target
.
setY
(
dpHeight
-
windowDecorationMargins
().
bottom
());
436
target
.
setWidth
(
dpWidth
);
437
target
.
setHeight
(
windowDecorationMargins
().
bottom
());
438
decorationPainter
.
drawImage
(
target
,
sourceImage
,
sourceMatrix
.
mapRect
(
target
));
439
dirtyRegion
+=
target
;
440
441
updateDirtyStates
(
dirtyRegion
);
442
}
443
444
QWaylandAbstractDecoration
*
QWaylandShmBackingStore
::
windowDecoration
()
const
445
{
446
return
waylandWindow
()->
decoration
();
447
}
448
449
QMargins
QWaylandShmBackingStore
::
windowDecorationMargins
()
const
450
{
451
if
(
windowDecoration
())
452
return
windowDecoration
()->
margins
();
453
return
QMargins
();
454
}
455
456
QWaylandWindow
*
QWaylandShmBackingStore
::
waylandWindow
()
const
457
{
458
return
static_cast
<
QWaylandWindow
*>(
window
()->
handle
());
459
}
460
461
#
if
QT_CONFIG
(
opengl
)
462
QImage
QWaylandShmBackingStore
::
toImage
()
const
463
{
464
// Invoked from QPlatformBackingStore::composeAndFlush() that is called
465
// instead of flush() for widgets that have renderToTexture children
466
// (QOpenGLWidget, QQuickWidget).
467
468
return
*
contentSurface
();
469
}
470
#
endif
// opengl
471
472
}
473
474
QT_END_NAMESPACE
QtWaylandClient
Definition
qwaylandclientextension.h:15
MAP_FAILED
#define MAP_FAILED
Definition
qresource.cpp:1158
qt_scrollRectInImage
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)
Definition
qbackingstore.cpp:300
qtwayland
src
client
qwaylandshmbackingstore.cpp
Generated on Tue Apr 22 2025 17:12:12 for Qt by
1.13.2