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