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
qwaylandwindow.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
// Qt-Security score:significant reason:default
4
5
#
include
"qwaylandsessionmanager_p.h"
6
#
include
"qwaylandwindow_p.h"
7
8
#
include
"qwaylandbuffer_p.h"
9
#
include
"qwaylandcursor_p.h"
10
#
include
"qwaylanddisplay_p.h"
11
#
include
"qwaylandsurface_p.h"
12
#
include
"qwaylandinputdevice_p.h"
13
#
include
"qwaylandfractionalscale_p.h"
14
#
include
"qwaylandscreen_p.h"
15
#
include
"qwaylandshellsurface_p.h"
16
#
include
"qwaylandsubsurface_p.h"
17
#
include
"qwaylandabstractdecoration_p.h"
18
#
include
"qwaylandplatformservices_p.h"
19
#
include
"qwaylandnativeinterface_p.h"
20
#
include
"qwaylanddecorationfactory_p.h"
21
#
include
"qwaylandshmbackingstore_p.h"
22
#
include
"qwaylandshellintegration_p.h"
23
#
include
"qwaylandviewport_p.h"
24
#
include
"qwaylandcolormanagement_p.h"
25
26
#
include
<
QtCore
/
QFileInfo
>
27
#
include
<
QtCore
/
QPointer
>
28
#
include
<
QtCore
/
QRegularExpression
>
29
#
include
<
QtGui
/
QWindow
>
30
31
#
include
<
QGuiApplication
>
32
#
include
<
qpa
/
qwindowsysteminterface
.
h
>
33
#
include
<
QtGui
/
private
/
qguiapplication_p
.
h
>
34
#
include
<
QtGui
/
private
/
qwindow_p
.
h
>
35
36
#
include
<
QtCore
/
QDebug
>
37
#
include
<
QtCore
/
QThread
>
38
39
#
include
<
QtWaylandClient
/
private
/
qwayland
-
fractional
-
scale
-
v1
.
h
>
40
41
QT_BEGIN_NAMESPACE
42
43
using
namespace
Qt::StringLiterals;
44
45
namespace
QtWaylandClient
{
46
47
Q_LOGGING_CATEGORY
(lcWaylandBackingstore,
"qt.qpa.wayland.backingstore"
)
48
49
QWaylandWindow
*
QWaylandWindow
::
mMouseGrab
=
nullptr
;
50
QWaylandWindow
*
QWaylandWindow
::
mTopPopup
=
nullptr
;
51
52
/*!
53
\class QtWaylandClient::QWaylandWindow
54
\internal
55
*/
56
QWaylandWindow
::
QWaylandWindow
(
QWindow
*
window
,
QWaylandDisplay
*
display
)
57
:
QPlatformWindow
(
window
)
58
,
mDisplay
(
display
)
59
,
mSurfaceLock
(
QReadWriteLock
::
Recursive
)
60
,
mShellIntegration
(
display
->
shellIntegration
())
61
,
mSurfaceFormat
(
window
->
requestedFormat
())
62
{
63
{
64
bool
ok
;
65
int
frameCallbackTimeout
=
qEnvironmentVariableIntValue
(
"QT_WAYLAND_FRAME_CALLBACK_TIMEOUT"
, &
ok
);
66
if
(
ok
)
67
mFrameCallbackTimeout
=
frameCallbackTimeout
;
68
}
69
70
mSurfaceFormat
.
setColorSpace
(
QColorSpace
{});
71
initializeWlSurface
();
72
mFlags
=
window
->
flags
();
73
74
setWindowIcon
(
window
->
icon
());
75
76
connect
(
this
, &
QWaylandWindow
::
wlSurfaceCreated
,
this
,
77
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceCreated
);
78
connect
(
this
, &
QWaylandWindow
::
wlSurfaceDestroyed
,
this
,
79
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceDestroyed
);
80
}
81
82
QWaylandWindow
::~
QWaylandWindow
()
83
{
84
mWindowDecoration
.
reset
();
85
reset
();
86
87
const
QWindow
*
parent
=
window
();
88
const
auto
tlw
=
QGuiApplication
::
topLevelWindows
();
89
for
(
QWindow
*
w
:
tlw
) {
90
if
(
w
->
transientParent
() ==
parent
)
91
QWindowSystemInterface
::
handleCloseEvent
(
w
);
92
}
93
94
if
(
mMouseGrab
==
this
) {
95
mMouseGrab
=
nullptr
;
96
}
97
}
98
99
void
QWaylandWindow
::
ensureSize
()
100
{
101
if
(
mBackingStore
) {
102
setBackingStore
(
mBackingStore
);
103
mBackingStore
->
recreateBackBufferIfNeeded
();
104
}
105
}
106
107
void
QWaylandWindow
::
initWindow
()
108
{
109
resetFrameCallback
();
110
111
if
(
shouldCreateSubSurface
()) {
112
Q_ASSERT
(!
mSubSurfaceWindow
);
113
114
auto
*
parent
=
static_cast
<
QWaylandWindow
*>(
QPlatformWindow
::
parent
());
115
if
(!
parent
->
mSurface
)
116
parent
->
initializeWlSurface
();
117
if
(
parent
->
wlSurface
()) {
118
if
(::
wl_subsurface
*
subsurface
=
mDisplay
->
createSubSurface
(
this
,
parent
))
119
mSubSurfaceWindow
=
new
QWaylandSubSurface
(
this
,
parent
,
subsurface
);
120
}
121
}
else
if
(
shouldCreateShellSurface
()) {
122
Q_ASSERT
(!
mShellSurface
);
123
Q_ASSERT
(
mShellIntegration
);
124
mTransientParent
=
guessTransientParent
();
125
if
(
mTransientParent
) {
126
if
(
window
()->
type
() ==
Qt
::
Popup
) {
127
if
(
mTopPopup
&&
mTopPopup
!=
mTransientParent
) {
128
qCWarning
(
lcQpaWayland
) <<
"Creating a popup with a parent,"
<<
mTransientParent
->
window
()
129
<<
"which does not match the current topmost grabbing popup,"
130
<<
mTopPopup
->
window
() <<
"With some shell surface protocols, this"
131
<<
"is not allowed. The wayland QPA plugin is currently handling"
132
<<
"it by setting the parent to the topmost grabbing popup."
133
<<
"Note, however, that this may cause positioning errors and"
134
<<
"popups closing unxpectedly. Please fix the transient parent of the popup."
;
135
mTransientParent
=
mTopPopup
;
136
}
137
mTopPopup
=
this
;
138
}
139
}
140
141
mShellSurface
=
mShellIntegration
->
createShellSurface
(
this
);
142
if
(
mShellSurface
) {
143
if
(
mTransientParent
) {
144
if
(
window
()->
type
() ==
Qt
::
ToolTip
||
window
()->
type
() ==
Qt
::
Popup
||
window
()->
type
() ==
Qt
::
Tool
)
145
mTransientParent
->
addChildPopup
(
this
);
146
}
147
148
// Set initial surface title
149
setWindowTitle
(
window
()->
title
());
150
mShellSurface
->
setIcon
(
mWindowIcon
);
151
152
// The appId is the desktop entry identifier that should follow the
153
// reverse DNS convention (see
154
// http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
155
// to xdg-shell the appId is only the name, without the .desktop suffix.
156
//
157
// If the application specifies the desktop file name use that,
158
// otherwise fall back to the executable name and prepend the
159
// reversed organization domain when available.
160
if
(!
QGuiApplication
::
desktopFileName
().
isEmpty
()) {
161
mShellSurface
->
setAppId
(
QGuiApplication
::
desktopFileName
());
162
}
else
{
163
QFileInfo
fi
=
QFileInfo
(
QCoreApplication
::
instance
()->
applicationFilePath
());
164
QStringList
domainName
=
165
QCoreApplication
::
instance
()->
organizationDomain
().
split
(
QLatin1Char
(
'.'
),
166
Qt
::
SkipEmptyParts
);
167
168
if
(
domainName
.
isEmpty
()) {
169
mShellSurface
->
setAppId
(
fi
.
baseName
());
170
}
else
{
171
QString
appId
;
172
for
(
int
i
= 0;
i
<
domainName
.
size
(); ++
i
)
173
appId
.
prepend
(
QLatin1Char
(
'.'
)).
prepend
(
domainName
.
at
(
i
));
174
appId
.
append
(
fi
.
baseName
());
175
mShellSurface
->
setAppId
(
appId
);
176
}
177
}
178
// the user may have already set some window properties, so make sure to send them out
179
for
(
auto
it
=
m_properties
.
cbegin
();
it
!=
m_properties
.
cend
(); ++
it
)
180
mShellSurface
->
sendProperty
(
it
.
key
(),
it
.
value
());
181
182
emit
surfaceRoleCreated
();
183
}
else
{
184
qWarning
(
"Could not create a shell surface object."
);
185
}
186
}
187
188
createDecoration
();
189
updateInputRegion
();
190
191
// Enable high-dpi rendering. Scale() returns the screen scale factor and will
192
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
193
// to inform the compositor that high-resolution buffers will be provided.
194
if
(
mViewport
)
195
updateViewport
();
196
else
if
(
mSurface
->
version
() >= 3)
197
mSurface
->
set_buffer_scale
(
std
::
ceil
(
scale
()));
198
199
QRect
geometry
=
windowGeometry
();
200
QRect
defaultGeometry
=
this
->
defaultGeometry
();
201
if
(
geometry
.
width
() <= 0)
202
geometry
.
setWidth
(
defaultGeometry
.
width
());
203
if
(
geometry
.
height
() <= 0)
204
geometry
.
setHeight
(
defaultGeometry
.
height
());
205
206
setGeometry_helper
(
geometry
);
207
setMask
(
window
()->
mask
());
208
if
(
mShellSurface
) {
209
mShellSurface
->
requestWindowStates
(
window
()->
windowStates
());
210
mShellSurface
->
setWindowFlags
(
mFlags
);
211
}
212
handleContentOrientationChange
(
window
()->
contentOrientation
());
213
214
if
(
mShellSurface
&&
mShellSurface
->
commitSurfaceRole
())
215
mSurface
->
commit
();
216
}
217
218
void
QWaylandWindow
::
setPendingImageDescription
()
219
{
220
mColorManagementSurface
->
setImageDescription
(
mPendingImageDescription
.
get
());
221
}
222
223
void
QWaylandWindow
::
initializeWlSurface
(
bool
colorSpace
)
224
{
225
Q_ASSERT
(!
mSurface
);
226
{
227
QWriteLocker
lock
(&
mSurfaceLock
);
228
mSurface
.
reset
(
new
QWaylandSurface
(
mDisplay
));
229
connect
(
mSurface
.
get
(), &
QWaylandSurface
::
screensChanged
,
230
this
, &
QWaylandWindow
::
handleScreensChanged
);
231
connect
(
mSurface
.
get
(), &
QWaylandSurface
::
preferredBufferScaleChanged
,
232
this
, &
QWaylandWindow
::
updateScale
);
233
connect
(
mSurface
.
get
(), &
QWaylandSurface
::
preferredBufferTransformChanged
,
234
this
, &
QWaylandWindow
::
updateBufferTransform
);
235
mSurface
->
m_window
=
this
;
236
}
237
emit
wlSurfaceCreated
();
238
239
if
(
mDisplay
->
fractionalScaleManager
() &&
qApp
->
highDpiScaleFactorRoundingPolicy
() ==
Qt
::
HighDpiScaleFactorRoundingPolicy
::
PassThrough
) {
240
mFractionalScale
.
reset
(
new
QWaylandFractionalScale
(
mDisplay
->
fractionalScaleManager
()->
get_fractional_scale
(
mSurface
->
object
())));
241
242
connect
(
mFractionalScale
.
get
(), &
QWaylandFractionalScale
::
preferredScaleChanged
,
243
this
, &
QWaylandWindow
::
updateScale
);
244
}
245
// The fractional scale manager check is needed to work around Gnome < 36 where viewports don't work
246
// Right now viewports are only necessary when a fractional scale manager is used
247
if
(
display
()->
viewporter
() &&
display
()->
fractionalScaleManager
()) {
248
mViewport
.
reset
(
new
QWaylandViewport
(
display
()->
createViewport
(
this
)));
249
}
250
251
if
(
colorSpace
) {
252
initializeColorSpace
();
253
}
254
}
255
256
void
QWaylandWindow
::
initializeColorSpace
()
257
{
258
QColorSpace
requestedColorSpace
=
window
()->
requestedFormat
().
colorSpace
();
259
if
(
requestedColorSpace
!=
QColorSpace
{} &&
mDisplay
->
colorManager
()) {
260
// TODO try a similar (same primaries + supported transfer function) color space if this fails?
261
mPendingImageDescription
=
mDisplay
->
colorManager
()->
createImageDescription
(
requestedColorSpace
);
262
if
(
mPendingImageDescription
) {
263
if
(!
mColorManagementSurface
)
264
mColorManagementSurface
=
std
::
make_unique
<
ColorManagementSurface
>(
mDisplay
->
colorManager
()->
get_surface
(
surface
()));
265
connect
(
mPendingImageDescription
.
get
(), &
ImageDescription
::
ready
,
this
, &
QWaylandWindow
::
setPendingImageDescription
,
Qt
::
SingleShotConnection
);
266
mSurfaceFormat
.
setColorSpace
(
requestedColorSpace
);
267
}
else
{
268
qCWarning
(
lcQpaWayland
) <<
"couldn't create image description for requested color space"
<<
requestedColorSpace
;
269
}
270
}
271
}
272
273
void
QWaylandWindow
::
setFormat
(
const
QSurfaceFormat
&
format
)
274
{
275
const
auto
colorSpace
=
mSurfaceFormat
.
colorSpace
();
276
mSurfaceFormat
=
format
;
277
mSurfaceFormat
.
setColorSpace
(
colorSpace
);
278
}
279
280
void
QWaylandWindow
::
setShellIntegration
(
QWaylandShellIntegration
*
shellIntegration
)
281
{
282
Q_ASSERT
(
shellIntegration
);
283
if
(
mShellSurface
) {
284
qCWarning
(
lcQpaWayland
) <<
"Cannot set shell integration while there's already a shell surface created"
;
285
return
;
286
}
287
mShellIntegration
=
shellIntegration
;
288
}
289
290
bool
QWaylandWindow
::
shouldCreateShellSurface
()
const
291
{
292
if
(!
shellIntegration
())
293
return
false
;
294
295
if
(
shouldCreateSubSurface
())
296
return
false
;
297
298
if
(
window
()->
inherits
(
"QShapedPixmapWindow"
))
299
return
false
;
300
301
if
(
qEnvironmentVariableIsSet
(
"QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"
))
302
return
!(
window
()->
flags
() &
Qt
::
BypassWindowManagerHint
);
303
304
return
true
;
305
}
306
307
bool
QWaylandWindow
::
shouldCreateSubSurface
()
const
308
{
309
return
QPlatformWindow
::
parent
() !=
nullptr
;
310
}
311
312
void
QWaylandWindow
::
beginFrame
()
313
{
314
mSurfaceLock
.
lockForRead
();
315
mInFrameRender
=
true
;
316
}
317
318
void
QWaylandWindow
::
endFrame
()
319
{
320
mSurfaceLock
.
unlock
();
321
mInFrameRender
=
false
;
322
}
323
324
void
QWaylandWindow
::
reset
()
325
{
326
resetSurfaceRole
();
327
328
if
(
mSurface
) {
329
{
330
QWriteLocker
lock
(&
mSurfaceLock
);
331
invalidateSurface
();
332
mColorManagementSurface
.
reset
();
333
mPendingImageDescription
.
reset
();
334
mFractionalScale
.
reset
();
335
mViewport
.
reset
();
336
337
// Destroy surface last.
338
// Some compositors (e.g. ChromeOS Sommelier) crash if they receive
339
// destroy requests for these objects after the associated surface
340
// has already been destroyed.
341
mSurface
.
reset
();
342
}
343
emit
wlSurfaceDestroyed
();
344
}
345
346
347
mScale
=
std
::
nullopt
;
348
mOpaqueArea
=
QRegion
();
349
mMask
=
QRegion
();
350
351
mInputRegion
=
QRegion
();
352
mTransparentInputRegion
=
false
;
353
354
mDisplay
->
handleWindowDestroyed
(
this
);
355
}
356
357
void
QWaylandWindow
::
resetSurfaceRole
()
358
{
359
// Old Reset
360
closeChildPopups
();
361
362
if
(
mTopPopup
==
this
)
363
mTopPopup
=
mTransientParent
&& (
mTransientParent
->
window
()->
type
() ==
Qt
::
Popup
) ?
mTransientParent
:
nullptr
;
364
if
(
mTransientParent
)
365
mTransientParent
->
removeChildPopup
(
this
);
366
mTransientParent
=
nullptr
;
367
delete
std
::
exchange
(
mShellSurface
,
nullptr
);
368
delete
std
::
exchange
(
mSubSurfaceWindow
,
nullptr
);
369
emit
surfaceRoleDestroyed
();
370
371
resetFrameCallback
();
372
mInFrameRender
=
false
;
373
mWaitingToApplyConfigure
=
false
;
374
mExposed
=
false
;
375
}
376
377
void
QWaylandWindow
::
resetFrameCallback
()
378
{
379
{
380
QMutexLocker
lock
(&
mFrameSyncMutex
);
381
if
(
mFrameCallback
) {
382
wl_callback_destroy
(
mFrameCallback
);
383
mFrameCallback
=
nullptr
;
384
}
385
mFrameCallbackElapsedTimer
.
invalidate
();
386
mFrameSyncWait
.
wakeAll
();
387
}
388
if
(
mFrameCallbackCheckIntervalTimerId
!= -1) {
389
killTimer
(
mFrameCallbackCheckIntervalTimerId
);
390
mFrameCallbackCheckIntervalTimerId
= -1;
391
}
392
mFrameCallbackTimedOut
=
false
;
393
}
394
395
QWaylandWindow
*
QWaylandWindow
::
fromWlSurface
(::
wl_surface
*
surface
)
396
{
397
if
(
auto
*
s
=
QWaylandSurface
::
fromWlSurface
(
surface
))
398
return
s
->
m_window
;
399
return
nullptr
;
400
}
401
402
WId
QWaylandWindow
::
winId
()
const
403
{
404
return
reinterpret_cast
<
WId
>(
wlSurface
());
405
}
406
407
void
QWaylandWindow
::
setParent
(
const
QPlatformWindow
*
parent
)
408
{
409
if
(
lastParent
==
parent
)
410
return
;
411
412
if
(
mSubSurfaceWindow
&&
parent
) {
// new parent, but we were a subsurface already
413
delete
mSubSurfaceWindow
;
414
QWaylandWindow
*
p
=
const_cast
<
QWaylandWindow
*>(
static_cast
<
const
QWaylandWindow
*>(
parent
));
415
mSubSurfaceWindow
=
new
QWaylandSubSurface
(
this
,
p
,
mDisplay
->
createSubSurface
(
this
,
p
));
416
}
else
if
((!
lastParent
&&
parent
) || (
lastParent
&& !
parent
)) {
417
// we're changing role, need to make a new wl_surface
418
reset
();
419
initializeWlSurface
();
420
if
(
window
()->
isVisible
()) {
421
initWindow
();
422
}
423
}
424
lastParent
=
parent
;
425
}
426
427
QString
QWaylandWindow
::
windowTitle
()
const
428
{
429
return
mWindowTitle
;
430
}
431
432
void
QWaylandWindow
::
setWindowTitle
(
const
QString
&
title
)
433
{
434
const
QString
separator
=
QString
::
fromUtf8
(
" \xe2\x80\x94 "
);
// unicode character U+2014, EM DASH
435
const
QString
formatted
=
formatWindowTitle
(
title
,
separator
);
436
437
const
int
libwaylandMaxBufferSize
= 4096;
438
// Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
439
// Also, QString is in utf-16, which means that in the worst case each character will be
440
// three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
441
const
int
maxLength
=
libwaylandMaxBufferSize
/ 3 - 100;
442
443
auto
truncated
=
QStringView
{
formatted
}.
left
(
maxLength
);
444
if
(
truncated
.
size
() <
formatted
.
size
()) {
445
qCWarning
(
lcQpaWayland
) <<
"Window titles longer than"
<<
maxLength
<<
"characters are not supported."
446
<<
"Truncating window title (from"
<<
formatted
.
size
() <<
"chars)"
;
447
}
448
449
mWindowTitle
=
truncated
.
toString
();
450
451
if
(
mShellSurface
)
452
mShellSurface
->
setTitle
(
mWindowTitle
);
453
454
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
455
mWindowDecoration
->
update
();
456
}
457
458
void
QWaylandWindow
::
setWindowIcon
(
const
QIcon
&
icon
)
459
{
460
mWindowIcon
=
icon
;
461
462
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
463
mWindowDecoration
->
update
();
464
if
(
mShellSurface
)
465
mShellSurface
->
setIcon
(
icon
);
466
}
467
468
QRect
QWaylandWindow
::
defaultGeometry
()
const
469
{
470
return
QRect
(
QPoint
(),
QSize
(500,500));
471
}
472
473
void
QWaylandWindow
::
setGeometry_helper
(
const
QRect
&
rect
)
474
{
475
QPlatformWindow
::
setGeometry
(
rect
);
476
if
(
mViewport
)
477
updateViewport
();
478
479
if
(
mSubSurfaceWindow
) {
480
QMargins
m
=
static_cast
<
QWaylandWindow
*>(
QPlatformWindow
::
parent
())->
clientSideMargins
();
481
mSubSurfaceWindow
->
set_position
(
rect
.
x
() +
m
.
left
(),
rect
.
y
() +
m
.
top
());
482
483
QWaylandWindow
*
parentWindow
=
mSubSurfaceWindow
->
parent
();
484
if
(
parentWindow
&&
parentWindow
->
isExposed
()) {
485
QRect
parentExposeGeometry
(
QPoint
(),
parentWindow
->
geometry
().
size
());
486
parentWindow
->
sendExposeEvent
(
parentExposeGeometry
);
487
}
488
}
489
}
490
491
void
QWaylandWindow
::
setGeometry
(
const
QRect
&
r
)
492
{
493
auto
rect
=
r
;
494
if
(
fixedToplevelPositions
&& !
QPlatformWindow
::
parent
() &&
window
()->
type
() !=
Qt
::
Popup
495
&&
window
()->
type
() !=
Qt
::
ToolTip
&&
window
()->
type
() !=
Qt
::
Tool
) {
496
rect
.
moveTo
(
screen
()->
geometry
().
topLeft
());
497
}
498
setGeometry_helper
(
rect
);
499
500
if
(
mShellSurface
&& !
mInResizeFromApplyConfigure
) {
501
const
QRect
frameGeometry
=
r
.
marginsAdded
(
clientSideMargins
()).
marginsRemoved
(
windowContentMargins
());
502
if
(
qt_window_private
(
window
())->
positionAutomatic
||
m_popupInfo
.
parentControlGeometry
.
isValid
())
503
mShellSurface
->
setWindowSize
(
frameGeometry
.
size
());
504
505
else
506
mShellSurface
->
setWindowGeometry
(
frameGeometry
);
507
}
508
509
if
(
mShellSurface
)
510
mShellSurface
->
setContentGeometry
(
windowContentGeometry
());
511
512
if
(
isOpaque
() &&
mMask
.
isEmpty
())
513
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
rect
.
size
()));
514
515
516
if
(
window
()->
isVisible
() &&
rect
.
isValid
()) {
517
ensureSize
();
518
if
(
mWindowDecorationEnabled
)
519
mWindowDecoration
->
update
();
520
521
QWindowSystemInterface
::
handleGeometryChange
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
geometry
());
522
}
523
524
// Wayland has no concept of areas being exposed or not, only the entire window, when our geometry changes, we need to flag the new area as exposed
525
// On other platforms (X11) the expose event would be received deferred from the X server
526
// we want our behaviour to match, and only run after control has returned to the event loop
527
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
synthesizeExposeOnGeometryChange
,
Qt
::
QueuedConnection
);
528
}
529
530
void
QWaylandWindow
::
synthesizeExposeOnGeometryChange
()
531
{
532
if
(!
isExposed
())
533
return
;
534
QRect
exposeGeometry
(
QPoint
(),
geometry
().
size
());
535
if
(
exposeGeometry
==
mLastExposeGeometry
)
536
return
;
537
538
sendExposeEvent
(
exposeGeometry
);
539
}
540
541
void
QWaylandWindow
::
updateInputRegion
()
542
{
543
if
(!
mSurface
)
544
return
;
545
546
const
bool
transparentInputRegion
=
mFlags
.
testFlag
(
Qt
::
WindowTransparentForInput
);
547
548
QRegion
inputRegion
;
549
if
(!
transparentInputRegion
)
550
inputRegion
=
mMask
;
551
552
if
(
mInputRegion
==
inputRegion
&&
mTransparentInputRegion
==
transparentInputRegion
)
553
return
;
554
555
mInputRegion
=
inputRegion
;
556
mTransparentInputRegion
=
transparentInputRegion
;
557
558
if
(
mInputRegion
.
isEmpty
() && !
mTransparentInputRegion
) {
559
mSurface
->
set_input_region
(
nullptr
);
560
}
else
{
561
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
mInputRegion
);
562
mSurface
->
set_input_region
(
region
);
563
wl_region_destroy
(
region
);
564
}
565
}
566
567
void
QWaylandWindow
::
updateViewport
()
568
{
569
if
(!
surfaceSize
().
isEmpty
())
570
mViewport
->
setDestination
(
surfaceSize
());
571
}
572
573
void
QWaylandWindow
::
setGeometryFromApplyConfigure
(
const
QPoint
&
globalPosition
,
const
QSize
&
sizeWithMargins
)
574
{
575
QMargins
margins
=
clientSideMargins
();
576
577
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
578
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
579
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
580
581
QRect
geometry
(
positionWithoutMargins
,
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
582
583
mInResizeFromApplyConfigure
=
true
;
584
setGeometry
(
geometry
);
585
mInResizeFromApplyConfigure
=
false
;
586
}
587
588
void
QWaylandWindow
::
repositionFromApplyConfigure
(
const
QPoint
&
globalPosition
)
589
{
590
QMargins
margins
=
clientSideMargins
();
591
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
592
593
QRect
geometry
(
positionWithoutMargins
,
windowGeometry
().
size
());
594
mInResizeFromApplyConfigure
=
true
;
595
setGeometry
(
geometry
);
596
mInResizeFromApplyConfigure
=
false
;
597
}
598
599
void
QWaylandWindow
::
resizeFromApplyConfigure
(
const
QSize
&
sizeWithMargins
,
const
QPoint
&
offset
)
600
{
601
QMargins
margins
=
clientSideMargins
();
602
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
603
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
604
QRect
geometry
(
windowGeometry
().
topLeft
(),
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
605
606
mOffset
+=
offset
;
607
mInResizeFromApplyConfigure
=
true
;
608
setGeometry
(
geometry
);
609
mInResizeFromApplyConfigure
=
false
;
610
}
611
612
void
QWaylandWindow
::
sendExposeEvent
(
const
QRect
&
rect
)
613
{
614
static
bool
sQtTestMode
=
qEnvironmentVariableIsSet
(
"QT_QTESTLIB_RUNNING"
);
615
mLastExposeGeometry
=
rect
;
616
617
if
(
sQtTestMode
) {
618
mExposeEventNeedsAttachedBuffer
=
true
;
619
}
620
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
rect
);
621
622
/**
623
* If an expose is not handled by application code, explicitly attach a buffer
624
* This primarily is a workaround for Qt unit tests using QWindow directly and
625
* wanting focus.
626
*/
627
if
(
mExposed
&&
mExposeEventNeedsAttachedBuffer
&& !
rect
.
isNull
()) {
628
QWaylandShmBuffer
buffer
(
mDisplay
,
rect
.
size
(),
QImage
::
Format_ARGB32
);
629
buffer
.
image
()->
fill
(
Qt
::
transparent
);
630
commit
(&
buffer
,
QRegion
());
631
}
632
}
633
634
QPlatformScreen
*
QWaylandWindow
::
calculateScreenFromSurfaceEvents
()
const
635
{
636
QReadLocker
lock
(&
mSurfaceLock
);
637
if
(
mSurface
) {
638
if
(
auto
*
screen
=
mSurface
->
oldestEnteredScreen
())
639
return
screen
;
640
}
641
return
QPlatformWindow
::
screen
();
642
}
643
644
void
QWaylandWindow
::
setVisible
(
bool
visible
)
645
{
646
// Workaround for issue where setVisible may be called with the same value twice
647
if
(
lastVisible
==
visible
)
648
return
;
649
lastVisible
=
visible
;
650
651
if
(
visible
) {
652
setGeometry
(
windowGeometry
());
653
initWindow
();
654
updateExposure
();
655
if
(
mShellSurface
)
656
mShellSurface
->
requestActivateOnShow
();
657
}
else
{
658
// make sure isExposed is false during the next event dispatch
659
mExposed
=
false
;
660
sendExposeEvent
(
QRect
());
661
resetSurfaceRole
();
662
mSurface
->
attach
(
nullptr
, 0, 0);
663
mSurface
->
commit
();
664
}
665
}
666
667
668
void
QWaylandWindow
::
raise
()
669
{
670
if
(
mShellSurface
)
671
mShellSurface
->
raise
();
672
}
673
674
675
void
QWaylandWindow
::
lower
()
676
{
677
if
(
mShellSurface
)
678
mShellSurface
->
lower
();
679
}
680
681
void
QWaylandWindow
::
setMask
(
const
QRegion
&
mask
)
682
{
683
QReadLocker
locker
(&
mSurfaceLock
);
684
if
(!
mSurface
)
685
return
;
686
687
if
(
mMask
==
mask
)
688
return
;
689
690
mMask
=
mask
;
691
692
updateInputRegion
();
693
694
if
(
isOpaque
()) {
695
if
(
mMask
.
isEmpty
())
696
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
697
else
698
setOpaqueArea
(
mMask
);
699
}
700
}
701
702
void
QWaylandWindow
::
setAlertState
(
bool
enabled
)
703
{
704
if
(
mShellSurface
)
705
mShellSurface
->
setAlertState
(
enabled
);
706
}
707
708
bool
QWaylandWindow
::
isAlertState
()
const
709
{
710
if
(
mShellSurface
)
711
return
mShellSurface
->
isAlertState
();
712
713
return
false
;
714
}
715
716
void
QWaylandWindow
::
applyConfigureWhenPossible
()
717
{
718
if
(!
mWaitingToApplyConfigure
) {
719
mWaitingToApplyConfigure
=
true
;
720
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
applyConfigure
,
Qt
::
QueuedConnection
);
721
}
722
}
723
724
void
QWaylandWindow
::
applyConfigure
()
725
{
726
if
(!
mWaitingToApplyConfigure
)
727
return
;
728
729
Q_ASSERT_X
(
QThread
::
isMainThread
(),
730
"QWaylandWindow::applyConfigure"
,
"not called from main thread"
);
731
732
// If we're mid paint, use an exposeEvent to flush the current frame.
733
// When this completes we know that no other frames will be rendering.
734
// This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
735
if
(
mInFrameRender
)
736
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
737
if
(
mShellSurface
)
738
mShellSurface
->
applyConfigure
();
739
740
mWaitingToApplyConfigure
=
false
;
741
if
(
mExposed
)
742
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
743
else
744
// we still need to commit the configured ack for a hidden surface
745
commit
();
746
}
747
748
void
QWaylandWindow
::
attach
(
QWaylandBuffer
*
buffer
,
int
x
,
int
y
)
749
{
750
QReadLocker
locker
(&
mSurfaceLock
);
751
if
(
mSurface
==
nullptr
)
752
return
;
753
754
if
(
buffer
) {
755
Q_ASSERT
(!
buffer
->
committed
());
756
handleUpdate
();
757
buffer
->
setBusy
(
true
);
758
if
(
mSurface
->
version
() >=
WL_SURFACE_OFFSET_SINCE_VERSION
) {
759
mSurface
->
offset
(
x
,
y
);
760
mSurface
->
attach
(
buffer
->
buffer
(), 0, 0);
761
}
else
{
762
mSurface
->
attach
(
buffer
->
buffer
(),
x
,
y
);
763
}
764
}
else
{
765
mSurface
->
attach
(
nullptr
, 0, 0);
766
}
767
}
768
769
void
QWaylandWindow
::
attachOffset
(
QWaylandBuffer
*
buffer
)
770
{
771
attach
(
buffer
,
mOffset
.
x
(),
mOffset
.
y
());
772
mOffset
=
QPoint
();
773
}
774
775
void
QWaylandWindow
::
damage
(
const
QRect
&
rect
)
776
{
777
QReadLocker
locker
(&
mSurfaceLock
);
778
if
(
mSurface
==
nullptr
)
779
return
;
780
781
const
qreal
s
=
scale
();
782
if
(
mSurface
->
version
() >= 4) {
783
const
QRect
bufferRect
=
784
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
785
.
toAlignedRect
();
786
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
787
bufferRect
.
height
());
788
}
else
{
789
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
790
}
791
}
792
793
void
QWaylandWindow
::
safeCommit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
794
{
795
if
(
isExposed
()) {
796
commit
(
buffer
,
damage
);
797
}
else
{
798
buffer
->
setBusy
(
false
);
799
}
800
}
801
802
bool
QWaylandWindow
::
allowsIndependentThreadedRendering
()
const
803
{
804
return
!
mWaitingToApplyConfigure
;
805
}
806
807
void
QWaylandWindow
::
commit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
808
{
809
Q_ASSERT
(
isExposed
());
810
if
(
buffer
->
committed
()) {
811
mSurface
->
commit
();
812
qCDebug
(
lcWaylandBackingstore
) <<
"Buffer already committed, not attaching."
;
813
return
;
814
}
815
816
QReadLocker
locker
(&
mSurfaceLock
);
817
if
(!
mSurface
)
818
return
;
819
820
attachOffset
(
buffer
);
821
if
(
mSurface
->
version
() >= 4) {
822
const
qreal
s
=
scale
();
823
for
(
const
QRect
&
rect
:
damage
) {
824
const
QRect
bufferRect
=
825
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
826
.
toAlignedRect
();
827
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
828
bufferRect
.
height
());
829
}
830
}
else
{
831
for
(
const
QRect
&
rect
:
damage
)
832
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
833
}
834
Q_ASSERT
(!
buffer
->
committed
());
835
buffer
->
setCommitted
();
836
mSurface
->
commit
();
837
}
838
839
void
QWaylandWindow
::
commit
()
840
{
841
QReadLocker
locker
(&
mSurfaceLock
);
842
if
(
mSurface
!=
nullptr
)
843
mSurface
->
commit
();
844
}
845
846
const
wl_callback_listener
QWaylandWindow
::
callbackListener
= {
847
[](
void
*
data
,
wl_callback
*
callback
,
uint32_t
time
) {
848
Q_UNUSED
(
time
);
849
auto
*
window
=
static_cast
<
QWaylandWindow
*>(
data
);
850
window
->
handleFrameCallback
(
callback
);
851
}
852
};
853
854
void
QWaylandWindow
::
handleFrameCallback
(
wl_callback
*
callback
)
855
{
856
QMutexLocker
locker
(&
mFrameSyncMutex
);
857
if
(
mFrameCallback
!=
callback
) {
858
// This means the callback is already unset by QWaylandWindow::reset.
859
// The wl_callback object will be destroyed there too.
860
return
;
861
}
862
wl_callback_destroy
(
mFrameCallback
);
863
mFrameCallback
=
nullptr
;
864
mFrameCallbackElapsedTimer
.
invalidate
();
865
866
// The rest can wait until we can run it on the correct thread
867
if
(
mWaitingForUpdateDelivery
.
testAndSetAcquire
(
false
,
true
)) {
868
// Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
869
// in the single-threaded case.
870
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
doHandleFrameCallback
,
Qt
::
QueuedConnection
);
871
}
872
mFrameSyncWait
.
notify_all
();
873
}
874
875
void
QWaylandWindow
::
doHandleFrameCallback
()
876
{
877
mWaitingForUpdateDelivery
.
storeRelease
(
false
);
878
bool
wasExposed
=
isExposed
();
879
mFrameCallbackTimedOut
=
false
;
880
// Did setting mFrameCallbackTimedOut make the window exposed?
881
updateExposure
();
882
if
(
wasExposed
&&
hasPendingUpdateRequest
())
883
deliverUpdateRequest
();
884
885
}
886
887
bool
QWaylandWindow
::
waitForFrameSync
(
int
timeout
)
888
{
889
QMutexLocker
locker
(&
mFrameSyncMutex
);
890
891
QDeadlineTimer
deadline
(
timeout
);
892
while
(
mFrameCallback
&&
mFrameSyncWait
.
wait
(&
mFrameSyncMutex
,
deadline
)) { }
893
894
if
(
mFrameCallback
) {
895
qCDebug
(
lcWaylandBackingstore
) <<
"Didn't receive frame callback in time, window should now be inexposed"
;
896
mFrameCallbackTimedOut
=
true
;
897
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
updateExposure
,
Qt
::
QueuedConnection
);
898
}
899
900
return
!
mFrameCallback
;
901
}
902
903
QMargins
QWaylandWindow
::
frameMargins
()
const
904
{
905
if
(
mWindowDecorationEnabled
)
906
return
mWindowDecoration
->
margins
();
907
else
if
(
mShellSurface
)
908
return
mShellSurface
->
serverSideFrameMargins
();
909
else
910
return
QPlatformWindow
::
frameMargins
();
911
}
912
913
QMargins
QWaylandWindow
::
clientSideMargins
()
const
914
{
915
return
mWindowDecorationEnabled
?
mWindowDecoration
->
margins
() :
QMargins
{};
916
}
917
918
void
QWaylandWindow
::
setCustomMargins
(
const
QMargins
&
margins
) {
919
const
QMargins
oldMargins
=
mCustomMargins
;
920
mCustomMargins
=
margins
;
921
propagateSizeHints
();
922
setGeometry
(
geometry
().
marginsRemoved
(
oldMargins
).
marginsAdded
(
margins
));
923
}
924
925
/*!
926
* Size, with decorations (including including eventual shadows) in wl_surface coordinates
927
*/
928
QSize
QWaylandWindow
::
surfaceSize
()
const
929
{
930
return
geometry
().
marginsAdded
(
clientSideMargins
()).
size
();
931
}
932
933
QMargins
QWaylandWindow
::
windowContentMargins
()
const
934
{
935
QMargins
shadowMargins
;
936
937
if
(
mWindowDecorationEnabled
)
938
shadowMargins
=
mWindowDecoration
->
margins
(
QWaylandAbstractDecoration
::
ShadowsOnly
);
939
940
if
(!
mCustomMargins
.
isNull
())
941
shadowMargins
+=
mCustomMargins
;
942
943
return
shadowMargins
;
944
}
945
946
/*!
947
* Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
948
* topLeft is where the shadow stops and the decorations border start.
949
*/
950
QRect
QWaylandWindow
::
windowContentGeometry
()
const
951
{
952
const
QMargins
margins
=
windowContentMargins
();
953
return
QRect
(
QPoint
(
margins
.
left
(),
margins
.
top
()),
surfaceSize
().
shrunkBy
(
margins
));
954
}
955
956
/*!
957
* Converts from wl_surface coordinates to Qt window coordinates. Qt window
958
* coordinates start inside (not including) the window decorations, while
959
* wl_surface coordinates start at the first pixel of the buffer. Potentially,
960
* this should be in the window shadow, although we don't have those. So for
961
* now, it's the first pixel of the decorations.
962
*/
963
QPointF
QWaylandWindow
::
mapFromWlSurface
(
const
QPointF
&
surfacePosition
)
const
964
{
965
const
QMargins
margins
=
clientSideMargins
();
966
return
QPointF
(
surfacePosition
.
x
() -
margins
.
left
(),
surfacePosition
.
y
() -
margins
.
top
());
967
}
968
969
wl_surface
*
QWaylandWindow
::
wlSurface
()
const
970
{
971
QReadLocker
locker
(&
mSurfaceLock
);
972
return
mSurface
?
mSurface
->
object
() :
nullptr
;
973
}
974
975
QWaylandShellSurface
*
QWaylandWindow
::
shellSurface
()
const
976
{
977
return
mShellSurface
;
978
}
979
980
std
::
any
QWaylandWindow
::
_surfaceRole
()
const
981
{
982
if
(
mSubSurfaceWindow
)
983
return
mSubSurfaceWindow
->
object
();
984
if
(
mShellSurface
)
985
return
mShellSurface
->
surfaceRole
();
986
return
{};
987
}
988
989
QWaylandSubSurface
*
QWaylandWindow
::
subSurfaceWindow
()
const
990
{
991
return
mSubSurfaceWindow
;
992
}
993
994
QWaylandScreen
*
QWaylandWindow
::
waylandScreen
()
const
995
{
996
auto
*
platformScreen
=
QPlatformWindow
::
screen
();
997
Q_ASSERT
(
platformScreen
);
998
if
(
platformScreen
->
isPlaceholder
())
999
return
nullptr
;
1000
return
static_cast
<
QWaylandScreen
*>(
platformScreen
);
1001
}
1002
1003
void
QWaylandWindow
::
handleContentOrientationChange
(
Qt
::
ScreenOrientation
orientation
)
1004
{
1005
mLastReportedContentOrientation
=
orientation
;
1006
updateBufferTransform
();
1007
}
1008
1009
void
QWaylandWindow
::
updateBufferTransform
()
1010
{
1011
QReadLocker
locker
(&
mSurfaceLock
);
1012
if
(
mSurface
==
nullptr
||
mSurface
->
version
() < 2)
1013
return
;
1014
1015
wl_output_transform
transform
;
1016
Qt
::
ScreenOrientation
screenOrientation
=
Qt
::
PrimaryOrientation
;
1017
1018
if
(
mSurface
->
version
() >= 6) {
1019
const
auto
transform
=
mSurface
->
preferredBufferTransform
().
value_or
(
WL_OUTPUT_TRANSFORM_NORMAL
);
1020
if
(
auto
screen
=
waylandScreen
())
1021
screenOrientation
=
screen
->
toScreenOrientation
(
transform
,
Qt
::
PrimaryOrientation
);
1022
}
else
{
1023
if
(
auto
screen
=
window
()->
screen
())
1024
screenOrientation
=
screen
->
primaryOrientation
();
1025
}
1026
1027
const
bool
isPortrait
= (
screenOrientation
==
Qt
::
PortraitOrientation
);
1028
1029
switch
(
mLastReportedContentOrientation
) {
1030
case
Qt
::
PrimaryOrientation
:
1031
transform
=
WL_OUTPUT_TRANSFORM_NORMAL
;
1032
break
;
1033
case
Qt
::
LandscapeOrientation
:
1034
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_270
:
WL_OUTPUT_TRANSFORM_NORMAL
;
1035
break
;
1036
case
Qt
::
PortraitOrientation
:
1037
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_NORMAL
:
WL_OUTPUT_TRANSFORM_90
;
1038
break
;
1039
case
Qt
::
InvertedLandscapeOrientation
:
1040
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_90
:
WL_OUTPUT_TRANSFORM_180
;
1041
break
;
1042
case
Qt
::
InvertedPortraitOrientation
:
1043
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_180
:
WL_OUTPUT_TRANSFORM_270
;
1044
break
;
1045
default
:
1046
Q_UNREACHABLE
();
1047
}
1048
mSurface
->
set_buffer_transform
(
transform
);
1049
}
1050
1051
void
QWaylandWindow
::
setOrientationMask
(
Qt
::
ScreenOrientations
mask
)
1052
{
1053
if
(
mShellSurface
)
1054
mShellSurface
->
setContentOrientationMask
(
mask
);
1055
}
1056
1057
void
QWaylandWindow
::
setWindowState
(
Qt
::
WindowStates
states
)
1058
{
1059
if
(
mShellSurface
)
1060
mShellSurface
->
requestWindowStates
(
states
);
1061
}
1062
1063
void
QWaylandWindow
::
setWindowFlags
(
Qt
::
WindowFlags
flags
)
1064
{
1065
const
bool
wasPopup
=
mFlags
.
testFlag
(
Qt
::
Popup
);
1066
const
bool
isPopup
=
flags
.
testFlag
(
Qt
::
Popup
);
1067
1068
mFlags
=
flags
;
1069
// changing role is not allowed on XdgShell on the same wl_surface
1070
if
(
wasPopup
!=
isPopup
) {
1071
reset
();
1072
initializeWlSurface
();
1073
if
(
window
()->
isVisible
()) {
1074
initWindow
();
1075
}
1076
}
else
{
1077
if
(
mShellSurface
)
1078
mShellSurface
->
setWindowFlags
(
flags
);
1079
}
1080
1081
createDecoration
();
1082
1083
QReadLocker
locker
(&
mSurfaceLock
);
1084
updateInputRegion
();
1085
}
1086
1087
Qt
::
WindowFlags
QWaylandWindow
::
windowFlags
()
const
1088
{
1089
return
mFlags
;
1090
}
1091
1092
bool
QWaylandWindow
::
createDecoration
()
1093
{
1094
Q_ASSERT_X
(
QThread
::
isMainThread
(),
1095
"QWaylandWindow::createDecoration"
,
"not called from main thread"
);
1096
// TODO: client side decorations do not work with Vulkan backend.
1097
if
(
window
()->
surfaceType
() ==
QSurface
::
VulkanSurface
)
1098
return
false
;
1099
if
(!
mDisplay
->
supportsWindowDecoration
())
1100
return
false
;
1101
1102
static
bool
decorationPluginFailed
=
false
;
1103
bool
decoration
=
false
;
1104
switch
(
window
()->
type
()) {
1105
case
Qt
::
Window
:
1106
case
Qt
::
Widget
:
1107
case
Qt
::
Dialog
:
1108
case
Qt
::
Tool
:
1109
case
Qt
::
Drawer
:
1110
decoration
=
true
;
1111
break
;
1112
default
:
1113
break
;
1114
}
1115
if
(
mFlags
&
Qt
::
FramelessWindowHint
)
1116
decoration
=
false
;
1117
if
(
mFlags
&
Qt
::
BypassWindowManagerHint
)
1118
decoration
=
false
;
1119
if
(
mSubSurfaceWindow
)
1120
decoration
=
false
;
1121
if
(!
mShellSurface
|| !
mShellSurface
->
wantsDecorations
())
1122
decoration
=
false
;
1123
1124
bool
hadDecoration
=
mWindowDecorationEnabled
;
1125
if
(
decoration
&& !
decorationPluginFailed
) {
1126
if
(!
mWindowDecorationEnabled
) {
1127
if
(
mWindowDecoration
) {
1128
mWindowDecoration
.
reset
();
1129
}
1130
1131
QStringList
decorations
=
QWaylandDecorationFactory
::
keys
();
1132
if
(
decorations
.
empty
()) {
1133
qWarning
() <<
"No decoration plugins available. Running with no decorations."
;
1134
decorationPluginFailed
=
true
;
1135
return
false
;
1136
}
1137
1138
QString
targetKey
;
1139
QByteArray
decorationPluginName
=
qgetenv
(
"QT_WAYLAND_DECORATION"
);
1140
if
(!
decorationPluginName
.
isEmpty
()) {
1141
targetKey
=
QString
::
fromLocal8Bit
(
decorationPluginName
);
1142
if
(!
decorations
.
contains
(
targetKey
)) {
1143
qWarning
() <<
"Requested decoration "
<<
targetKey
<<
" not found, falling back to default"
;
1144
targetKey
=
QString
();
// fallthrough
1145
}
1146
}
1147
1148
if
(
targetKey
.
isEmpty
()) {
1149
auto
unixServices
=
dynamic_cast
<
QDesktopUnixServices
*>(
1150
QGuiApplicationPrivate
::
platformIntegration
()->
services
());
1151
const
QList
<
QByteArray
>
desktopNames
=
unixServices
->
desktopEnvironment
().
split
(
':'
);
1152
if
(
desktopNames
.
contains
(
"GNOME"
)) {
1153
if
(
decorations
.
contains
(
"adwaita"_L1
))
1154
targetKey
=
"adwaita"_L1
;
1155
else
if
(
decorations
.
contains
(
"gnome"_L1
))
1156
targetKey
=
"gnome"_L1
;
1157
}
else
{
1158
// Do not use Adwaita/GNOME decorations on other DEs
1159
decorations
.
removeAll
(
"adwaita"_L1
);
1160
decorations
.
removeAll
(
"gnome"_L1
);
1161
}
1162
}
1163
1164
if
(
targetKey
.
isEmpty
())
1165
targetKey
=
decorations
.
first
();
// first come, first served.
1166
1167
mWindowDecoration
.
reset
(
QWaylandDecorationFactory
::
create
(
targetKey
,
QStringList
()));
1168
if
(!
mWindowDecoration
) {
1169
qWarning
() <<
"Could not create decoration from factory! Running with no decorations."
;
1170
decorationPluginFailed
=
true
;
1171
return
false
;
1172
}
1173
mWindowDecoration
->
setWaylandWindow
(
this
);
1174
mWindowDecorationEnabled
=
true
;
1175
}
1176
}
else
{
1177
mWindowDecorationEnabled
=
false
;
1178
}
1179
1180
if
(
hadDecoration
!=
mWindowDecorationEnabled
) {
1181
for
(
QWaylandSubSurface
*
subsurf
:
std
::
as_const
(
mChildren
)) {
1182
QPoint
pos
=
subsurf
->
window
()->
geometry
().
topLeft
();
1183
QMargins
m
=
frameMargins
();
1184
subsurf
->
set_position
(
pos
.
x
() +
m
.
left
(),
pos
.
y
() +
m
.
top
());
1185
}
1186
setGeometry
(
geometry
());
1187
1188
// creating a decoration changes our margins which in turn change size hints
1189
propagateSizeHints
();
1190
1191
// This is a special case where the buffer is recreated, but since
1192
// the content rect remains the same, the widgets remain the same
1193
// size and are not redrawn, leaving the new buffer empty. As a simple
1194
// work-around, we trigger a full extra update whenever the client-side
1195
// window decorations are toggled while the window is showing.
1196
window
()->
requestUpdate
();
1197
}
1198
1199
return
mWindowDecoration
.
get
();
1200
}
1201
1202
QWaylandAbstractDecoration
*
QWaylandWindow
::
decoration
()
const
1203
{
1204
return
mWindowDecorationEnabled
?
mWindowDecoration
.
get
() :
nullptr
;
1205
}
1206
1207
static
QWaylandWindow *
closestShellSurfaceWindow
(QWindow *window)
1208
{
1209
while
(window) {
1210
auto
w =
static_cast
<QWaylandWindow *>(window->handle());
1211
if
(w && w->shellSurface())
1212
return
w;
1213
window = window->transientParent() ? window->transientParent() : window->parent();
1214
}
1215
return
nullptr
;
1216
}
1217
1218
QWaylandWindow
*
QWaylandWindow
::
transientParent
()
const
1219
{
1220
return
mTransientParent
;
1221
}
1222
1223
QWaylandWindow
*
QWaylandWindow
::
guessTransientParent
()
const
1224
{
1225
// Take the closest window with a shell surface, since the transient parent may be a
1226
// QWidgetWindow or some other window without a shell surface, which is then not able to
1227
// get mouse events.
1228
if
(
auto
transientParent
=
closestShellSurfaceWindow
(
window
()->
transientParent
()))
1229
return
transientParent
;
1230
1231
if
(
window
()->
type
() ==
Qt
::
Popup
) {
1232
if
(
mTopPopup
)
1233
return
mTopPopup
;
1234
}
1235
1236
if
(
window
()->
type
() ==
Qt
::
ToolTip
||
window
()->
type
() ==
Qt
::
Popup
) {
1237
if
(
auto
lastInputWindow
=
display
()->
lastInputWindow
())
1238
return
closestShellSurfaceWindow
(
lastInputWindow
->
window
());
1239
}
1240
1241
return
nullptr
;
1242
}
1243
1244
void
QWaylandWindow
::
handleMouse
(
QWaylandInputDevice
*
inputDevice
,
const
QWaylandPointerEvent
&
e
)
1245
{
1246
// There's currently no way to get info about the actual hardware device in use.
1247
// At least we get the correct seat.
1248
const
QPointingDevice
*
device
=
QPointingDevice
::
primaryPointingDevice
(
inputDevice
->
seatname
());
1249
if
(
e
.
type
==
QEvent
::
Leave
) {
1250
if
(
mWindowDecorationEnabled
) {
1251
if
(
mMouseEventsInContentArea
)
1252
QWindowSystemInterface
::
handleLeaveEvent
(
window
());
1253
}
else
{
1254
QWindowSystemInterface
::
handleLeaveEvent
(
window
());
1255
}
1256
#
if
QT_CONFIG
(
cursor
)
1257
restoreMouseCursor
(
inputDevice
);
1258
#
endif
1259
return
;
1260
}
1261
1262
#
if
QT_CONFIG
(
cursor
)
1263
if
(
e
.
type
==
QEvent
::
Enter
) {
1264
restoreMouseCursor
(
inputDevice
);
1265
}
1266
#
endif
1267
1268
if
(
mWindowDecorationEnabled
) {
1269
handleMouseEventWithDecoration
(
inputDevice
,
e
);
1270
}
else
{
1271
switch
(
e
.
type
) {
1272
case
QEvent
::
Enter
:
1273
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
e
.
local
,
e
.
global
);
1274
#
if
QT_CONFIG
(
cursor
)
1275
mDisplay
->
waylandCursor
()->
setPosFromEnterEvent
(
e
.
global
.
toPoint
());
1276
#
endif
1277
break
;
1278
case
QEvent
::
MouseButtonPress
:
1279
case
QEvent
::
MouseButtonRelease
:
1280
case
QEvent
::
MouseMove
:
1281
QWindowSystemInterface
::
handleMouseEvent
(
window
(),
e
.
timestamp
,
device
,
e
.
local
,
e
.
global
,
e
.
buttons
,
e
.
button
,
e
.
type
,
e
.
modifiers
);
1282
break
;
1283
case
QEvent
::
Wheel
:
1284
QWindowSystemInterface
::
handleWheelEvent
(
window
(),
e
.
timestamp
,
device
,
e
.
local
,
e
.
global
,
1285
e
.
pixelDelta
,
e
.
angleDelta
,
e
.
modifiers
,
1286
e
.
phase
,
e
.
source
,
e
.
inverted
);
1287
break
;
1288
default
:
1289
Q_UNREACHABLE
();
1290
}
1291
}
1292
}
1293
1294
#
ifndef
QT_NO_GESTURES
1295
void
QWaylandWindow
::
handleSwipeGesture
(
QWaylandInputDevice
*
inputDevice
,
1296
const
QWaylandPointerGestureSwipeEvent
&
e
)
1297
{
1298
switch
(
e
.
state
) {
1299
case
Qt
::
GestureStarted
:
1300
if
(
mGestureState
!=
GestureNotActive
)
1301
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected GestureStarted while already active"
;
1302
1303
if
(
mWindowDecorationEnabled
&& !
mMouseEventsInContentArea
) {
1304
// whole gesture sequence will be ignored
1305
mGestureState
=
GestureActiveInDecoration
;
1306
return
;
1307
}
1308
1309
mGestureState
=
GestureActiveInContentArea
;
1310
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1311
inputDevice
->
mTouchPadDevice
,
1312
Qt
::
BeginNativeGesture
,
1313
e
.
local
,
e
.
global
,
e
.
fingers
);
1314
break
;
1315
case
Qt
::
GestureUpdated
:
1316
if
(
mGestureState
!=
GestureActiveInContentArea
)
1317
return
;
1318
1319
if
(!
e
.
delta
.
isNull
()) {
1320
QWindowSystemInterface
::
handleGestureEventWithValueAndDelta
(
1321
window
(),
e
.
timestamp
,
inputDevice
->
mTouchPadDevice
,
1322
Qt
::
PanNativeGesture
,
1323
0,
e
.
delta
,
e
.
local
,
e
.
global
,
e
.
fingers
);
1324
}
1325
break
;
1326
case
Qt
::
GestureFinished
:
1327
case
Qt
::
GestureCanceled
:
1328
if
(
mGestureState
==
GestureActiveInDecoration
) {
1329
mGestureState
=
GestureNotActive
;
1330
return
;
1331
}
1332
1333
if
(
mGestureState
!=
GestureActiveInContentArea
)
1334
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected"
<< (
e
.
state
==
Qt
::
GestureFinished
?
"GestureFinished"
:
"GestureCanceled"
);
1335
1336
mGestureState
=
GestureNotActive
;
1337
1338
// There's currently no way to expose cancelled gestures to the rest of Qt, so
1339
// this part of information is lost.
1340
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1341
inputDevice
->
mTouchPadDevice
,
1342
Qt
::
EndNativeGesture
,
1343
e
.
local
,
e
.
global
,
e
.
fingers
);
1344
break
;
1345
default
:
1346
break
;
1347
}
1348
}
1349
1350
void
QWaylandWindow
::
handlePinchGesture
(
QWaylandInputDevice
*
inputDevice
,
1351
const
QWaylandPointerGesturePinchEvent
&
e
)
1352
{
1353
switch
(
e
.
state
) {
1354
case
Qt
::
GestureStarted
:
1355
if
(
mGestureState
!=
GestureNotActive
)
1356
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected GestureStarted while already active"
;
1357
1358
if
(
mWindowDecorationEnabled
&& !
mMouseEventsInContentArea
) {
1359
// whole gesture sequence will be ignored
1360
mGestureState
=
GestureActiveInDecoration
;
1361
return
;
1362
}
1363
1364
mGestureState
=
GestureActiveInContentArea
;
1365
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1366
inputDevice
->
mTouchPadDevice
,
1367
Qt
::
BeginNativeGesture
,
1368
e
.
local
,
e
.
global
,
e
.
fingers
);
1369
break
;
1370
case
Qt
::
GestureUpdated
:
1371
if
(
mGestureState
!=
GestureActiveInContentArea
)
1372
return
;
1373
1374
if
(!
e
.
delta
.
isNull
()) {
1375
QWindowSystemInterface
::
handleGestureEventWithValueAndDelta
(
1376
window
(),
e
.
timestamp
,
inputDevice
->
mTouchPadDevice
,
1377
Qt
::
PanNativeGesture
,
1378
0,
e
.
delta
,
e
.
local
,
e
.
global
,
e
.
fingers
);
1379
}
1380
if
(
e
.
rotation_delta
!= 0) {
1381
QWindowSystemInterface
::
handleGestureEventWithRealValue
(
window
(),
e
.
timestamp
,
1382
inputDevice
->
mTouchPadDevice
,
1383
Qt
::
RotateNativeGesture
,
1384
e
.
rotation_delta
,
1385
e
.
local
,
e
.
global
,
e
.
fingers
);
1386
}
1387
if
(
e
.
scale_delta
!= 0) {
1388
QWindowSystemInterface
::
handleGestureEventWithRealValue
(
window
(),
e
.
timestamp
,
1389
inputDevice
->
mTouchPadDevice
,
1390
Qt
::
ZoomNativeGesture
,
1391
e
.
scale_delta
,
1392
e
.
local
,
e
.
global
,
e
.
fingers
);
1393
}
1394
break
;
1395
case
Qt
::
GestureFinished
:
1396
case
Qt
::
GestureCanceled
:
1397
if
(
mGestureState
==
GestureActiveInDecoration
) {
1398
mGestureState
=
GestureNotActive
;
1399
return
;
1400
}
1401
1402
if
(
mGestureState
!=
GestureActiveInContentArea
)
1403
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected"
<< (
e
.
state
==
Qt
::
GestureFinished
?
"GestureFinished"
:
"GestureCanceled"
);
1404
1405
mGestureState
=
GestureNotActive
;
1406
1407
// There's currently no way to expose cancelled gestures to the rest of Qt, so
1408
// this part of information is lost.
1409
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1410
inputDevice
->
mTouchPadDevice
,
1411
Qt
::
EndNativeGesture
,
1412
e
.
local
,
e
.
global
,
e
.
fingers
);
1413
break
;
1414
default
:
1415
break
;
1416
}
1417
}
1418
#
endif
// #ifndef QT_NO_GESTURES
1419
1420
1421
bool
QWaylandWindow
::
touchDragDecoration
(
QWaylandInputDevice
*
inputDevice
,
const
QPointF
&
local
,
const
QPointF
&
global
,
QEventPoint
::
State
state
,
Qt
::
KeyboardModifiers
mods
)
1422
{
1423
if
(!
mWindowDecorationEnabled
)
1424
return
false
;
1425
return
mWindowDecoration
->
handleTouch
(
inputDevice
,
local
,
global
,
state
,
mods
);
1426
}
1427
1428
bool
QWaylandWindow
::
handleTabletEventDecoration
(
QWaylandInputDevice
*
inputDevice
,
1429
const
QPointF
&
local
,
const
QPointF
&
global
,
1430
Qt
::
MouseButtons
buttons
,
1431
Qt
::
KeyboardModifiers
modifiers
)
1432
{
1433
if
(!
mWindowDecorationEnabled
)
1434
return
false
;
1435
return
mWindowDecoration
->
handleMouse
(
inputDevice
,
local
,
global
,
buttons
,
modifiers
);
1436
}
1437
1438
void
QWaylandWindow
::
handleMouseEventWithDecoration
(
QWaylandInputDevice
*
inputDevice
,
const
QWaylandPointerEvent
&
e
)
1439
{
1440
// There's currently no way to get info about the actual hardware device in use.
1441
// At least we get the correct seat.
1442
const
QPointingDevice
*
device
=
QPointingDevice
::
primaryPointingDevice
(
inputDevice
->
seatname
());
1443
if
(
mMousePressedInContentArea
==
Qt
::
NoButton
&&
1444
mWindowDecoration
->
handleMouse
(
inputDevice
,
e
.
local
,
e
.
global
,
e
.
buttons
,
e
.
modifiers
)) {
1445
if
(
mMouseEventsInContentArea
) {
1446
QWindowSystemInterface
::
handleLeaveEvent
(
window
());
1447
mMouseEventsInContentArea
=
false
;
1448
}
1449
return
;
1450
}
1451
1452
QMargins
marg
=
clientSideMargins
();
1453
QRect
windowRect
(0 +
marg
.
left
(),
1454
0 +
marg
.
top
(),
1455
geometry
().
size
().
width
(),
1456
geometry
().
size
().
height
());
1457
if
(
windowRect
.
contains
(
e
.
local
.
toPoint
()) ||
mMousePressedInContentArea
!=
Qt
::
NoButton
) {
1458
const
QPointF
localTranslated
=
mapFromWlSurface
(
e
.
local
);
1459
QPointF
globalTranslated
=
e
.
global
;
1460
globalTranslated
.
setX
(
globalTranslated
.
x
() -
marg
.
left
());
1461
globalTranslated
.
setY
(
globalTranslated
.
y
() -
marg
.
top
());
1462
if
(!
mMouseEventsInContentArea
) {
1463
#
if
QT_CONFIG
(
cursor
)
1464
restoreMouseCursor
(
inputDevice
);
1465
#
endif
1466
QWindowSystemInterface
::
handleEnterEvent
(
window
());
1467
}
1468
1469
switch
(
e
.
type
) {
1470
case
QEvent
::
Enter
:
1471
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
localTranslated
,
globalTranslated
);
1472
#
if
QT_CONFIG
(
cursor
)
1473
mDisplay
->
waylandCursor
()->
setPosFromEnterEvent
(
e
.
global
.
toPoint
());
1474
#
endif
1475
break
;
1476
case
QEvent
::
MouseButtonPress
:
1477
case
QEvent
::
MouseButtonRelease
:
1478
case
QEvent
::
MouseMove
:
1479
QWindowSystemInterface
::
handleMouseEvent
(
window
(),
e
.
timestamp
,
device
,
localTranslated
,
globalTranslated
,
e
.
buttons
,
e
.
button
,
e
.
type
,
e
.
modifiers
);
1480
break
;
1481
case
QEvent
::
Wheel
: {
1482
QWindowSystemInterface
::
handleWheelEvent
(
window
(),
e
.
timestamp
,
device
,
1483
localTranslated
,
globalTranslated
,
1484
e
.
pixelDelta
,
e
.
angleDelta
,
e
.
modifiers
,
1485
e
.
phase
,
e
.
source
,
e
.
inverted
);
1486
break
;
1487
}
1488
default
:
1489
Q_UNREACHABLE
();
1490
}
1491
1492
mMouseEventsInContentArea
=
true
;
1493
mMousePressedInContentArea
=
e
.
buttons
;
1494
}
else
{
1495
if
(
mMouseEventsInContentArea
) {
1496
QWindowSystemInterface
::
handleLeaveEvent
(
window
());
1497
mMouseEventsInContentArea
=
false
;
1498
}
1499
}
1500
}
1501
1502
void
QWaylandWindow
::
handleScreensChanged
()
1503
{
1504
QPlatformScreen
*
newScreen
=
calculateScreenFromSurfaceEvents
();
1505
1506
if
(!
newScreen
||
newScreen
->
screen
() ==
window
()->
screen
())
1507
return
;
1508
1509
QWindowSystemInterface
::
handleWindowScreenChanged
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
newScreen
->
QPlatformScreen
::
screen
());
1510
1511
if
(
fixedToplevelPositions
&& !
QPlatformWindow
::
parent
() &&
window
()->
type
() !=
Qt
::
Popup
1512
&&
window
()->
type
() !=
Qt
::
ToolTip
&&
window
()->
type
() !=
Qt
::
Tool
1513
&&
geometry
().
topLeft
() !=
newScreen
->
geometry
().
topLeft
()) {
1514
auto
geometry
=
this
->
geometry
();
1515
geometry
.
moveTo
(
newScreen
->
geometry
().
topLeft
());
1516
setGeometry
(
geometry
);
1517
}
1518
1519
updateScale
();
1520
updateBufferTransform
();
1521
}
1522
1523
void
QWaylandWindow
::
updateScale
()
1524
{
1525
if
(
mFractionalScale
) {
1526
qreal
preferredScale
=
mFractionalScale
->
preferredScale
().
value_or
(1.0);
1527
preferredScale
=
std
::
max
<
qreal
>(1.0,
preferredScale
);
1528
Q_ASSERT
(
mViewport
);
1529
setScale
(
preferredScale
);
1530
return
;
1531
}
1532
1533
if
(
mSurface
&&
mSurface
->
version
() >= 6) {
1534
auto
preferredScale
=
mSurface
->
preferredBufferScale
().
value_or
(1);
1535
preferredScale
=
std
::
max
(1,
preferredScale
);
1536
setScale
(
preferredScale
);
1537
return
;
1538
}
1539
1540
int
scale
=
screen
()->
isPlaceholder
() ? 1 :
static_cast
<
QWaylandScreen
*>(
screen
())->
scale
();
1541
setScale
(
scale
);
1542
}
1543
1544
void
QWaylandWindow
::
setScale
(
qreal
newScale
)
1545
{
1546
if
(
mScale
.
has_value
() &&
qFuzzyCompare
(
mScale
.
value
(),
newScale
))
1547
return
;
1548
mScale
=
newScale
;
1549
1550
if
(
mSurface
) {
1551
if
(
mViewport
)
1552
updateViewport
();
1553
else
if
(
mSurface
->
version
() >= 3)
1554
mSurface
->
set_buffer_scale
(
std
::
ceil
(
newScale
));
1555
}
1556
ensureSize
();
1557
1558
QWindowSystemInterface
::
handleWindowDevicePixelRatioChanged
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
());
1559
if
(
isExposed
()) {
1560
// redraw at the new DPR
1561
window
()->
requestUpdate
();
1562
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
1563
}
1564
}
1565
1566
#
if
QT_CONFIG
(
cursor
)
1567
void
QWaylandWindow
::
restoreMouseCursor
(
QWaylandInputDevice
*
device
)
1568
{
1569
if
(
const
QCursor
*
overrideCursor
=
QGuiApplication
::
overrideCursor
()) {
1570
applyCursor
(
device
, *
overrideCursor
);
1571
}
else
if
(
mHasStoredCursor
) {
1572
applyCursor
(
device
,
mStoredCursor
);
1573
}
else
{
1574
applyCursor
(
device
,
QCursor
(
Qt
::
ArrowCursor
));
1575
}
1576
}
1577
1578
void
QWaylandWindow
::
resetStoredCursor
()
1579
{
1580
mHasStoredCursor
=
false
;
1581
}
1582
1583
// Keep track of application set cursors on each window
1584
void
QWaylandWindow
::
setStoredCursor
(
const
QCursor
&
cursor
) {
1585
mStoredCursor
=
cursor
;
1586
mHasStoredCursor
=
true
;
1587
}
1588
1589
void
QWaylandWindow
::
applyCursor
(
QWaylandInputDevice
*
device
,
const
QCursor
&
cursor
) {
1590
if
(!
device
|| !
device
->
pointer
() ||
device
->
pointer
()->
focusSurface
() !=
waylandSurface
())
1591
return
;
1592
1593
int
fallbackBufferScale
=
qCeil
(
devicePixelRatio
());
1594
device
->
setCursor
(&
cursor
, {},
fallbackBufferScale
);
1595
}
1596
#
endif
1597
1598
void
QWaylandWindow
::
requestActivateWindow
()
1599
{
1600
if
(
mShellSurface
)
1601
mShellSurface
->
requestActivate
();
1602
}
1603
1604
bool
QWaylandWindow
::
calculateExposure
()
const
1605
{
1606
if
(!
window
()->
isVisible
())
1607
return
false
;
1608
1609
if
(
mFrameCallbackTimedOut
)
1610
return
false
;
1611
1612
if
(
mShellSurface
)
1613
return
mShellSurface
->
isExposed
();
1614
1615
if
(
mSubSurfaceWindow
)
1616
return
mSubSurfaceWindow
->
parent
()->
isExposed
();
1617
1618
return
!(
shouldCreateShellSurface
() ||
shouldCreateSubSurface
());
1619
}
1620
1621
void
QWaylandWindow
::
updateExposure
()
1622
{
1623
bool
exposed
=
calculateExposure
();
1624
if
(
exposed
==
mExposed
)
1625
return
;
1626
1627
mExposed
=
exposed
;
1628
1629
if
(!
exposed
)
1630
sendExposeEvent
(
QRect
());
1631
else
1632
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
1633
1634
for
(
QWaylandSubSurface
*
subSurface
:
std
::
as_const
(
mChildren
)) {
1635
auto
subWindow
=
subSurface
->
window
();
1636
subWindow
->
updateExposure
();
1637
}
1638
}
1639
1640
bool
QWaylandWindow
::
isExposed
()
const
1641
{
1642
return
mExposed
;
1643
}
1644
1645
bool
QWaylandWindow
::
isActive
()
const
1646
{
1647
return
mDisplay
->
isWindowActivated
(
this
);
1648
}
1649
1650
qreal
QWaylandWindow
::
scale
()
const
1651
{
1652
return
devicePixelRatio
();
1653
}
1654
1655
qreal
QWaylandWindow
::
devicePixelRatio
()
const
1656
{
1657
return
mScale
.
value_or
(
waylandScreen
() ?
waylandScreen
()->
scale
() : 1);
1658
}
1659
1660
bool
QWaylandWindow
::
setMouseGrabEnabled
(
bool
grab
)
1661
{
1662
if
(
window
()->
type
() !=
Qt
::
Popup
) {
1663
qWarning
(
"This plugin supports grabbing the mouse only for popup windows"
);
1664
return
false
;
1665
}
1666
1667
mMouseGrab
=
grab
?
this
:
nullptr
;
1668
return
true
;
1669
}
1670
1671
QWaylandWindow
::
ToplevelWindowTilingStates
QWaylandWindow
::
toplevelWindowTilingStates
()
const
1672
{
1673
return
mLastReportedToplevelWindowTilingStates
;
1674
}
1675
1676
void
QWaylandWindow
::
handleToplevelWindowTilingStatesChanged
(
ToplevelWindowTilingStates
states
)
1677
{
1678
mLastReportedToplevelWindowTilingStates
=
states
;
1679
}
1680
1681
Qt
::
WindowStates
QWaylandWindow
::
windowStates
()
const
1682
{
1683
return
mLastReportedWindowStates
;
1684
}
1685
1686
void
QWaylandWindow
::
handleWindowStatesChanged
(
Qt
::
WindowStates
states
)
1687
{
1688
createDecoration
();
1689
Qt
::
WindowStates
statesWithoutActive
=
states
& ~
Qt
::
WindowActive
;
1690
Qt
::
WindowStates
lastStatesWithoutActive
=
mLastReportedWindowStates
& ~
Qt
::
WindowActive
;
1691
QWindowSystemInterface
::
handleWindowStateChanged
(
window
(),
statesWithoutActive
,
1692
lastStatesWithoutActive
);
1693
mLastReportedWindowStates
=
states
;
1694
}
1695
1696
void
QWaylandWindow
::
sendProperty
(
const
QString
&
name
,
const
QVariant
&
value
)
1697
{
1698
m_properties
.
insert
(
name
,
value
);
1699
QWaylandNativeInterface
*
nativeInterface
=
static_cast
<
QWaylandNativeInterface
*>(
1700
QGuiApplication
::
platformNativeInterface
());
1701
nativeInterface
->
emitWindowPropertyChanged
(
this
,
name
);
1702
if
(
mShellSurface
)
1703
mShellSurface
->
sendProperty
(
name
,
value
);
1704
}
1705
1706
void
QWaylandWindow
::
setProperty
(
const
QString
&
name
,
const
QVariant
&
value
)
1707
{
1708
m_properties
.
insert
(
name
,
value
);
1709
QWaylandNativeInterface
*
nativeInterface
=
static_cast
<
QWaylandNativeInterface
*>(
1710
QGuiApplication
::
platformNativeInterface
());
1711
nativeInterface
->
emitWindowPropertyChanged
(
this
,
name
);
1712
}
1713
1714
QVariantMap
QWaylandWindow
::
properties
()
const
1715
{
1716
return
m_properties
;
1717
}
1718
1719
QVariant
QWaylandWindow
::
property
(
const
QString
&
name
)
1720
{
1721
return
m_properties
.
value
(
name
);
1722
}
1723
1724
QVariant
QWaylandWindow
::
property
(
const
QString
&
name
,
const
QVariant
&
defaultValue
)
1725
{
1726
return
m_properties
.
value
(
name
,
defaultValue
);
1727
}
1728
1729
#
ifdef
QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1730
void
QWaylandWindow
::
setBackingStore
(
QPlatformBackingStore
*
store
)
1731
{
1732
mBackingStore
=
dynamic_cast
<
QWaylandShmBackingStore
*>(
store
);
1733
}
1734
#
endif
1735
1736
void
QWaylandWindow
::
timerEvent
(
QTimerEvent
*
event
)
1737
{
1738
if
(
event
->
timerId
() !=
mFrameCallbackCheckIntervalTimerId
)
1739
return
;
1740
1741
{
1742
QMutexLocker
lock
(&
mFrameSyncMutex
);
1743
1744
const
bool
callbackTimerValid
=
mFrameCallbackElapsedTimer
.
isValid
();
1745
const
bool
callbackTimerExpired
=
callbackTimerValid
&&
mFrameCallbackElapsedTimer
.
hasExpired
(
mFrameCallbackTimeout
);
1746
if
(!
callbackTimerValid
||
callbackTimerExpired
) {
1747
killTimer
(
mFrameCallbackCheckIntervalTimerId
);
1748
mFrameCallbackCheckIntervalTimerId
= -1;
1749
}
1750
if
(!
callbackTimerValid
|| !
callbackTimerExpired
) {
1751
return
;
1752
}
1753
mFrameCallbackElapsedTimer
.
invalidate
();
1754
}
1755
1756
qCDebug
(
lcWaylandBackingstore
) <<
"Didn't receive frame callback in time, window should now be inexposed"
;
1757
mFrameCallbackTimedOut
=
true
;
1758
updateExposure
();
1759
}
1760
1761
void
QWaylandWindow
::
requestUpdate
()
1762
{
1763
qCDebug
(
lcWaylandBackingstore
) <<
"requestUpdate"
;
1764
Q_ASSERT
(
hasPendingUpdateRequest
());
// should be set by QPA
1765
1766
// If we have a frame callback all is good and will be taken care of there
1767
{
1768
QMutexLocker
locker
(&
mFrameSyncMutex
);
1769
if
(
mFrameCallback
)
1770
return
;
1771
}
1772
1773
// Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1774
// so use invokeMethod to delay the delivery a bit.
1775
QMetaObject
::
invokeMethod
(
this
, [
this
] {
1776
// Things might have changed in the meantime
1777
{
1778
QMutexLocker
locker
(&
mFrameSyncMutex
);
1779
if
(
mFrameCallback
)
1780
return
;
1781
}
1782
if
(
hasPendingUpdateRequest
())
1783
deliverUpdateRequest
();
1784
},
Qt
::
QueuedConnection
);
1785
}
1786
1787
// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1788
// with eglSwapBuffers) to know when it's time to commit the next one.
1789
// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1790
void
QWaylandWindow
::
handleUpdate
()
1791
{
1792
mExposeEventNeedsAttachedBuffer
=
false
;
1793
qCDebug
(
lcWaylandBackingstore
) <<
"handleUpdate"
<<
QThread
::
currentThread
();
1794
1795
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
1796
QReadLocker
lock
(&
mSurfaceLock
);
1797
if
(!
mSurface
)
1798
return
;
1799
1800
QMutexLocker
locker
(&
mFrameSyncMutex
);
1801
if
(
mFrameCallback
)
1802
return
;
1803
1804
struct
::
wl_surface
*
wrappedSurface
=
reinterpret_cast
<
struct
::
wl_surface
*>(
wl_proxy_create_wrapper
(
mSurface
->
object
()));
1805
wl_proxy_set_queue
(
reinterpret_cast
<
wl_proxy
*>(
wrappedSurface
),
mDisplay
->
frameEventQueue
());
1806
mFrameCallback
=
wl_surface_frame
(
wrappedSurface
);
1807
wl_proxy_wrapper_destroy
(
wrappedSurface
);
1808
wl_callback_add_listener
(
mFrameCallback
, &
QWaylandWindow
::
callbackListener
,
this
);
1809
1810
// Start a timer for handling the case when the compositor stops sending frame callbacks.
1811
if
(
mFrameCallbackTimeout
> 0) {
1812
QMetaObject
::
invokeMethod
(
this
, [
this
] {
1813
QMutexLocker
locker
(&
mFrameSyncMutex
);
1814
1815
if
(
mFrameCallback
) {
1816
if
(
mFrameCallbackCheckIntervalTimerId
< 0)
1817
mFrameCallbackCheckIntervalTimerId
=
startTimer
(
mFrameCallbackTimeout
);
1818
mFrameCallbackElapsedTimer
.
start
();
1819
}
1820
},
Qt
::
QueuedConnection
);
1821
}
1822
}
1823
1824
void
QWaylandWindow
::
deliverUpdateRequest
()
1825
{
1826
qCDebug
(
lcWaylandBackingstore
) <<
"deliverUpdateRequest"
;
1827
QPlatformWindow
::
deliverUpdateRequest
();
1828
}
1829
1830
void
QWaylandWindow
::
addAttachOffset
(
const
QPoint
point
)
1831
{
1832
mOffset
+=
point
;
1833
}
1834
1835
void
QWaylandWindow
::
propagateSizeHints
()
1836
{
1837
if
(
mShellSurface
)
1838
mShellSurface
->
propagateSizeHints
();
1839
}
1840
1841
bool
QWaylandWindow
::
startSystemResize
(
Qt
::
Edges
edges
)
1842
{
1843
if
(
auto
*
seat
=
display
()->
lastInputDevice
()) {
1844
bool
rc
=
mShellSurface
&&
mShellSurface
->
resize
(
seat
,
edges
);
1845
seat
->
handleEndDrag
();
1846
return
rc
;
1847
}
1848
return
false
;
1849
}
1850
1851
bool
QtWaylandClient
::
QWaylandWindow
::
startSystemMove
()
1852
{
1853
if
(
auto
seat
=
display
()->
lastInputDevice
()) {
1854
bool
rc
=
mShellSurface
&&
mShellSurface
->
move
(
seat
);
1855
seat
->
handleEndDrag
();
1856
return
rc
;
1857
}
1858
return
false
;
1859
}
1860
1861
bool
QWaylandWindow
::
isOpaque
()
const
1862
{
1863
return
window
()->
requestedFormat
().
alphaBufferSize
() <= 0;
1864
}
1865
1866
void
QWaylandWindow
::
setOpaqueArea
(
const
QRegion
&
opaqueArea
)
1867
{
1868
const
QRegion
translatedOpaqueArea
=
opaqueArea
.
translated
(
clientSideMargins
().
left
(),
clientSideMargins
().
top
());
1869
1870
if
(
translatedOpaqueArea
==
mOpaqueArea
|| !
mSurface
)
1871
return
;
1872
1873
mOpaqueArea
=
translatedOpaqueArea
;
1874
1875
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
translatedOpaqueArea
);
1876
mSurface
->
set_opaque_region
(
region
);
1877
wl_region_destroy
(
region
);
1878
}
1879
1880
void
QWaylandWindow
::
requestXdgActivationToken
(
uint
serial
)
1881
{
1882
if
(!
mShellSurface
) {
1883
qCWarning
(
lcQpaWayland
) <<
"requestXdgActivationToken is called with no surface role created, emitting synthetic signal"
;
1884
Q_EMIT
xdgActivationTokenCreated
({});
1885
return
;
1886
}
1887
mShellSurface
->
requestXdgActivationToken
(
serial
);
1888
}
1889
1890
void
QWaylandWindow
::
setXdgActivationToken
(
const
QString
&
token
)
1891
{
1892
if
(
mShellSurface
)
1893
mShellSurface
->
setXdgActivationToken
(
token
);
1894
else
1895
qCWarning
(
lcQpaWayland
) <<
"setXdgActivationToken is called with no surface role created, token"
<<
token
<<
"discarded"
;
1896
}
1897
1898
void
QWaylandWindow
::
addChildPopup
(
QWaylandWindow
*
child
)
1899
{
1900
if
(
mShellSurface
)
1901
mShellSurface
->
attachPopup
(
child
->
shellSurface
());
1902
mChildPopups
.
append
(
child
);
1903
}
1904
1905
void
QWaylandWindow
::
removeChildPopup
(
QWaylandWindow
*
child
)
1906
{
1907
if
(
mShellSurface
)
1908
mShellSurface
->
detachPopup
(
child
->
shellSurface
());
1909
mChildPopups
.
removeAll
(
child
);
1910
}
1911
1912
void
QWaylandWindow
::
closeChildPopups
() {
1913
while
(!
mChildPopups
.
isEmpty
()) {
1914
auto
popup
=
mChildPopups
.
takeLast
();
1915
popup
->
resetSurfaceRole
();
1916
}
1917
}
1918
1919
void
QWaylandWindow
::
reinit
()
1920
{
1921
if
(
window
()->
isVisible
()) {
1922
initWindow
();
1923
if
(
hasPendingUpdateRequest
())
1924
deliverUpdateRequest
();
1925
}
1926
}
1927
1928
bool
QWaylandWindow
::
windowEvent
(
QEvent
*
event
)
1929
{
1930
if
(
event
->
type
() ==
QEvent
::
ApplicationPaletteChange
1931
||
event
->
type
() ==
QEvent
::
ApplicationFontChange
) {
1932
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
1933
mWindowDecoration
->
update
();
1934
}
else
if
(
event
->
type
() ==
QEvent
::
WindowUnblocked
) {
1935
// QtGui sends leave event to window under cursor when modal window opens, so we have
1936
// to send enter event when modal closes and window has cursor and gets unblocked.
1937
if
(
auto
*
inputDevice
=
mDisplay
->
lastInputDevice
();
inputDevice
&&
inputDevice
->
pointerFocus
() ==
this
) {
1938
const
auto
pos
=
mDisplay
->
waylandCursor
()->
pos
();
1939
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
mapFromGlobalF
(
pos
),
pos
);
1940
}
1941
}
1942
1943
return
QPlatformWindow
::
windowEvent
(
event
);
1944
}
1945
1946
QSurfaceFormat
QWaylandWindow
::
format
()
const
1947
{
1948
return
mSurfaceFormat
;
1949
}
1950
1951
void
QWaylandWindow
::
setSessionRestoreId
(
const
QString
&
role
)
1952
{
1953
mSessionRestoreId
=
role
;
1954
}
1955
1956
QString
QWaylandWindow
::
sessionRestoreId
()
const
1957
{
1958
return
mSessionRestoreId
;
1959
}
1960
1961
void
QWaylandWindow
::
setExtendedWindowType
(
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
windowType
) {
1962
m_popupInfo
.
extendedWindowType
=
windowType
;
1963
}
1964
1965
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
QWaylandWindow
::
extendedWindowType
()
const
1966
{
1967
return
m_popupInfo
.
extendedWindowType
;
1968
}
1969
1970
void
QWaylandWindow
::
setParentControlGeometry
(
const
QRect
&
parentControlGeometry
) {
1971
m_popupInfo
.
parentControlGeometry
=
parentControlGeometry
;
1972
if
(
mExposed
) {
1973
mShellSurface
->
setWindowPosition
(
window
()->
position
());
1974
}
1975
}
1976
1977
QRect
QWaylandWindow
::
parentControlGeometry
()
const
1978
{
1979
return
m_popupInfo
.
parentControlGeometry
;
1980
}
1981
1982
void
QWaylandWindow
::
handleMousePressActivation
()
1983
{
1984
if
(!
window
() ||
window
()->
flags
() &
Qt
::
WindowDoesNotAcceptFocus
)
1985
return
;
1986
1987
const
QWaylandWindow
*
currentActiveSubSurface
=
mDisplay
->
activeSubSurface
();
1988
const
bool
isSubsurface
= !!
subSurfaceWindow
();
1989
1990
auto
toplevelIsActive
= [&](
QWaylandWindow
*
w
) ->
bool
{
1991
while
(
w
&&
w
->
subSurfaceWindow
())
1992
w
=
static_cast
<
QWaylandWindow
*>(
w
->
QPlatformWindow
::
parent
());
1993
return
mDisplay
->
isWindowActivated
(
w
);
1994
};
1995
1996
if
(
isSubsurface
&&
currentActiveSubSurface
!=
this
&&
toplevelIsActive
(
this
))
1997
mDisplay
->
setActiveSubSurface
(
this
);
1998
else
if
(!
isSubsurface
&&
currentActiveSubSurface
)
1999
mDisplay
->
setActiveSubSurface
(
nullptr
);
2000
}
2001
2002
}
2003
2004
QT_END_NAMESPACE
2005
2006
#
include
"moc_qwaylandwindow_p.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qrandomaccessasyncfile_darwin.mm:17
QtWaylandClient
Definition
qwaylandclientextension.h:16
QtWaylandClient::Q_LOGGING_CATEGORY
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")
QtWaylandClient::closestShellSurfaceWindow
static QWaylandWindow * closestShellSurfaceWindow(QWindow *window)
Definition
qwaylandwindow.cpp:1207
qtbase
src
plugins
platforms
wayland
qwaylandwindow.cpp
Generated on
for Qt by
1.16.1