Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
"qwaylandshmbackingstore_p.h"
22
#
include
"qwaylandshellintegration_p.h"
23
#
include
"qwaylandviewport_p.h"
24
#
include
"qwaylandcolormanagement_p.h"
25
#
include
"qwaylanddecorationhandler_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
mSurfaceFormat
.
setColorSpace
(
QColorSpace
{});
71
initializeWlSurface
();
72
mWindowDecorationHandler
.
reset
(
new
QWaylandDecorationHandler
(
this
));
73
74
mFlags
=
window
->
flags
();
75
76
setWindowIcon
(
window
->
icon
());
77
78
connect
(
this
, &
QWaylandWindow
::
wlSurfaceCreated
,
this
,
79
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceCreated
);
80
connect
(
this
, &
QWaylandWindow
::
wlSurfaceDestroyed
,
this
,
81
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceDestroyed
);
82
}
83
84
QWaylandWindow
::~
QWaylandWindow
()
85
{
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
(
waylandSurface
(),
parent
->
waylandSurface
()))
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
updateNeedsDecoration
();
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_container
=
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
(
mSurface
.
get
())));
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
mScale
=
std
::
nullopt
;
355
mOpaqueArea
=
QRegion
();
356
mMask
=
QRegion
();
357
358
mInputRegion
=
QRegion
();
359
mTransparentInputRegion
=
false
;
360
mWindowDecorationHandler
->
reset
();
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
->
waylandWindow
();
407
return
nullptr
;
408
}
409
WId
QWaylandWindow
::
winId
()
const
410
{
411
return
reinterpret_cast
<
WId
>(
wlSurface
());
412
}
413
414
void
QWaylandWindow
::
setParent
(
const
QPlatformWindow
*
parent
)
415
{
416
if
(
lastParent
==
parent
)
417
return
;
418
419
if
(
mSubSurfaceWindow
&&
parent
) {
// new parent, but we were a subsurface already
420
delete
mSubSurfaceWindow
;
421
QWaylandWindow
*
p
=
const_cast
<
QWaylandWindow
*>(
static_cast
<
const
QWaylandWindow
*>(
parent
));
422
mSubSurfaceWindow
=
new
QWaylandSubSurface
(
this
,
p
,
mDisplay
->
createSubSurface
(
waylandSurface
(),
p
->
waylandSurface
()));
423
}
else
if
((!
lastParent
&&
parent
) || (
lastParent
&& !
parent
)) {
424
// we're changing role, need to make a new wl_surface
425
reset
();
426
initializeWlSurface
();
427
if
(
window
()->
isVisible
()) {
428
initWindow
();
429
}
430
}
431
lastParent
=
parent
;
432
}
433
434
QString
QWaylandWindow
::
windowTitle
()
const
435
{
436
return
mWindowTitle
;
437
}
438
439
void
QWaylandWindow
::
setWindowTitle
(
const
QString
&
title
)
440
{
441
const
QString
separator
=
QString
::
fromUtf8
(
" \xe2\x80\x94 "
);
// unicode character U+2014, EM DASH
442
const
QString
formatted
=
formatWindowTitle
(
title
,
separator
);
443
444
const
int
libwaylandMaxBufferSize
= 4096;
445
// Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
446
// Also, QString is in utf-16, which means that in the worst case each character will be
447
// three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
448
const
int
maxLength
=
libwaylandMaxBufferSize
/ 3 - 100;
449
450
auto
truncated
=
QStringView
{
formatted
}.
left
(
maxLength
);
451
if
(
truncated
.
size
() <
formatted
.
size
()) {
452
qCWarning
(
lcQpaWayland
) <<
"Window titles longer than"
<<
maxLength
<<
"characters are not supported."
453
<<
"Truncating window title (from"
<<
formatted
.
size
() <<
"chars)"
;
454
}
455
456
mWindowTitle
=
truncated
.
toString
();
457
458
if
(
mShellSurface
)
459
mShellSurface
->
setTitle
(
mWindowTitle
);
460
461
if
(
auto
*
dec
=
decoration
();
dec
&&
window
()->
isVisible
())
462
dec
->
update
();
463
}
464
465
void
QWaylandWindow
::
setWindowIcon
(
const
QIcon
&
icon
)
466
{
467
mWindowIcon
=
icon
;
468
469
if
(
auto
*
dec
=
decoration
();
dec
&&
window
()->
isVisible
())
470
dec
->
update
();
471
if
(
mShellSurface
)
472
mShellSurface
->
setIcon
(
icon
);
473
}
474
475
QRect
QWaylandWindow
::
defaultGeometry
()
const
476
{
477
return
QRect
(
QPoint
(),
QSize
(500,500));
478
}
479
480
void
QWaylandWindow
::
setGeometry_helper
(
const
QRect
&
rect
)
481
{
482
QPlatformWindow
::
setGeometry
(
rect
);
483
if
(
mViewport
)
484
updateViewport
();
485
486
if
(
mSubSurfaceWindow
) {
487
mSubSurfaceWindow
->
set_position
(
rect
.
x
(),
rect
.
y
());
488
489
QWaylandWindow
*
parentWindow
=
mSubSurfaceWindow
->
parent
();
490
if
(
parentWindow
)
491
parentWindow
->
requestForcedUpdate
();
492
}
493
}
494
495
void
QWaylandWindow
::
setGeometry
(
const
QRect
&
r
)
496
{
497
auto
rect
=
r
;
498
if
(
fixedToplevelPositions
&& !
QPlatformWindow
::
parent
() &&
window
()->
type
() !=
Qt
::
Popup
499
&&
window
()->
type
() !=
Qt
::
ToolTip
&&
window
()->
type
() !=
Qt
::
Tool
) {
500
rect
.
moveTo
(
screen
()->
geometry
().
topLeft
());
501
}
502
setGeometry_helper
(
rect
);
503
504
if
(
mShellSurface
&& !
mInResizeFromApplyConfigure
) {
505
const
QRect
frameGeometry
=
r
.
marginsRemoved
(
windowContentMargins
());
506
if
(
qt_window_private
(
window
())->
positionAutomatic
||
m_popupInfo
.
parentControlGeometry
.
isValid
())
507
mShellSurface
->
setWindowSize
(
frameGeometry
.
size
());
508
else
509
mShellSurface
->
setWindowGeometry
(
frameGeometry
);
510
}
511
512
if
(
mShellSurface
)
513
mShellSurface
->
setContentGeometry
(
windowContentGeometry
());
514
515
if
(
isOpaque
() &&
mMask
.
isEmpty
())
516
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
rect
.
size
()));
517
518
519
if
(
window
()->
isVisible
() &&
rect
.
isValid
()) {
520
ensureSize
();
521
if
(
auto
*
dec
=
decoration
())
522
dec
->
update
();
523
524
QWindowSystemInterface
::
handleGeometryChange
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
geometry
());
525
}
526
527
// 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
528
// On other platforms (X11) the expose event would be received deferred from the X server
529
// we want our behaviour to match, and only run after control has returned to the event loop
530
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
synthesizeExposeOnGeometryChange
,
Qt
::
QueuedConnection
);
531
}
532
533
void
QWaylandWindow
::
synthesizeExposeOnGeometryChange
()
534
{
535
if
(!
isExposed
())
536
return
;
537
QRect
exposeGeometry
(
QPoint
(),
geometry
().
size
());
538
if
(
exposeGeometry
==
mLastExposeGeometry
)
539
return
;
540
541
sendExposeEvent
(
exposeGeometry
);
542
}
543
544
void
QWaylandWindow
::
updateInputRegion
()
545
{
546
if
(!
mSurface
)
547
return
;
548
549
const
bool
transparentInputRegion
=
mFlags
.
testFlag
(
Qt
::
WindowTransparentForInput
);
550
551
QRegion
inputRegion
;
552
if
(!
transparentInputRegion
)
553
inputRegion
=
mMask
;
554
555
if
(
mInputRegion
==
inputRegion
&&
mTransparentInputRegion
==
transparentInputRegion
)
556
return
;
557
558
mInputRegion
=
inputRegion
;
559
mTransparentInputRegion
=
transparentInputRegion
;
560
561
if
(
mInputRegion
.
isEmpty
() && !
mTransparentInputRegion
) {
562
mSurface
->
set_input_region
(
nullptr
);
563
}
else
{
564
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
mInputRegion
);
565
mSurface
->
set_input_region
(
region
);
566
wl_region_destroy
(
region
);
567
}
568
}
569
570
void
QWaylandWindow
::
updateViewport
()
571
{
572
const
QSize
size
=
geometry
().
size
();
573
if
(!
size
.
isEmpty
())
574
mViewport
->
setDestination
(
size
);
575
}
576
577
void
QWaylandWindow
::
setGeometryFromApplyConfigure
(
const
QPoint
&
globalPosition
,
const
QSize
&
sizeWithMargins
)
578
{
579
QMargins
margins
=
clientSideMargins
();
580
581
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
582
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
583
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
584
585
QRect
geometry
(
positionWithoutMargins
,
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
586
587
mInResizeFromApplyConfigure
=
true
;
588
setGeometry
(
geometry
);
589
mInResizeFromApplyConfigure
=
false
;
590
}
591
592
void
QWaylandWindow
::
repositionFromApplyConfigure
(
const
QPoint
&
globalPosition
)
593
{
594
QMargins
margins
=
clientSideMargins
();
595
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
596
597
QRect
geometry
(
positionWithoutMargins
,
windowGeometry
().
size
());
598
mInResizeFromApplyConfigure
=
true
;
599
setGeometry
(
geometry
);
600
mInResizeFromApplyConfigure
=
false
;
601
}
602
603
void
QWaylandWindow
::
resizeFromApplyConfigure
(
const
QSize
&
sizeWithMargins
,
const
QPoint
&
offset
)
604
{
605
QMargins
margins
=
clientSideMargins
();
606
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
607
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
608
QRect
geometry
(
windowGeometry
().
topLeft
(),
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
609
610
mOffset
+=
offset
;
611
mInResizeFromApplyConfigure
=
true
;
612
setGeometry
(
geometry
);
613
mInResizeFromApplyConfigure
=
false
;
614
}
615
616
void
QWaylandWindow
::
sendExposeEvent
(
const
QRect
&
rect
)
617
{
618
static
bool
sQtTestMode
=
qEnvironmentVariableIsSet
(
"QT_QTESTLIB_RUNNING"
);
619
mLastExposeGeometry
=
rect
;
620
621
if
(
decoration
())
622
mWindowDecorationHandler
->
update
();
623
624
if
(
sQtTestMode
) {
625
mExposeEventNeedsAttachedBuffer
=
true
;
626
}
627
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
rect
);
628
629
/**
630
* If an expose is not handled by application code, explicitly attach a buffer
631
* This primarily is a workaround for Qt unit tests using QWindow directly and
632
* wanting focus.
633
*/
634
if
(
mExposed
&&
mExposeEventNeedsAttachedBuffer
&& !
rect
.
isNull
()) {
635
QWaylandShmBuffer
buffer
(
mDisplay
,
rect
.
size
(),
QImage
::
Format_ARGB32
);
636
buffer
.
image
()->
fill
(
Qt
::
transparent
);
637
commit
(&
buffer
,
QRegion
());
638
}
639
}
640
641
QPlatformScreen
*
QWaylandWindow
::
calculateScreenFromSurfaceEvents
()
const
642
{
643
QReadLocker
lock
(&
mSurfaceLock
);
644
if
(
mSurface
) {
645
if
(
auto
*
screen
=
mSurface
->
oldestEnteredScreen
())
646
return
screen
;
647
}
648
return
QPlatformWindow
::
screen
();
649
}
650
651
void
QWaylandWindow
::
setVisible
(
bool
visible
)
652
{
653
// Workaround for issue where setVisible may be called with the same value twice
654
if
(
lastVisible
==
visible
)
655
return
;
656
lastVisible
=
visible
;
657
658
if
(
visible
) {
659
setGeometry
(
windowGeometry
());
660
initWindow
();
661
updateExposure
();
662
if
(
mShellSurface
)
663
mShellSurface
->
requestActivateOnShow
();
664
}
else
{
665
// make sure isExposed is false during the next event dispatch
666
mExposed
=
false
;
667
sendExposeEvent
(
QRect
());
668
resetSurfaceRole
();
669
mSurface
->
attach
(
nullptr
, 0, 0);
670
mSurface
->
commit
();
671
}
672
}
673
674
675
void
QWaylandWindow
::
raise
()
676
{
677
if
(
mShellSurface
)
678
mShellSurface
->
raise
();
679
}
680
681
682
void
QWaylandWindow
::
lower
()
683
{
684
if
(
mShellSurface
)
685
mShellSurface
->
lower
();
686
}
687
688
void
QWaylandWindow
::
setMask
(
const
QRegion
&
mask
)
689
{
690
QReadLocker
locker
(&
mSurfaceLock
);
691
if
(!
mSurface
)
692
return
;
693
694
if
(
mMask
==
mask
)
695
return
;
696
697
mMask
=
mask
;
698
699
updateInputRegion
();
700
701
if
(
isOpaque
()) {
702
if
(
mMask
.
isEmpty
())
703
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
704
else
705
setOpaqueArea
(
mMask
);
706
}
707
}
708
709
void
QWaylandWindow
::
setAlertState
(
bool
enabled
)
710
{
711
if
(
mShellSurface
)
712
mShellSurface
->
setAlertState
(
enabled
);
713
}
714
715
bool
QWaylandWindow
::
isAlertState
()
const
716
{
717
if
(
mShellSurface
)
718
return
mShellSurface
->
isAlertState
();
719
720
return
false
;
721
}
722
723
void
QWaylandWindow
::
applyConfigureWhenPossible
()
724
{
725
if
(!
mWaitingToApplyConfigure
) {
726
mWaitingToApplyConfigure
=
true
;
727
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
applyConfigure
,
Qt
::
QueuedConnection
);
728
}
729
}
730
731
void
QWaylandWindow
::
applyConfigure
()
732
{
733
if
(!
mWaitingToApplyConfigure
)
734
return
;
735
736
Q_ASSERT_X
(
QThread
::
isMainThread
(),
737
"QWaylandWindow::applyConfigure"
,
"not called from main thread"
);
738
739
// If we're mid paint, use an exposeEvent to flush the current frame.
740
// When this completes we know that no other frames will be rendering.
741
// This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
742
if
(
mInFrameRender
)
743
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
744
if
(
mShellSurface
)
745
mShellSurface
->
applyConfigure
();
746
747
mWaitingToApplyConfigure
=
false
;
748
if
(
mExposed
)
749
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
750
else
751
// we still need to commit the configured ack for a hidden surface
752
commit
();
753
}
754
755
void
QWaylandWindow
::
attach
(
QWaylandBuffer
*
buffer
,
int
x
,
int
y
)
756
{
757
QReadLocker
locker
(&
mSurfaceLock
);
758
if
(
mSurface
==
nullptr
)
759
return
;
760
761
if
(
buffer
) {
762
Q_ASSERT
(!
buffer
->
committed
());
763
handleUpdate
();
764
buffer
->
setBusy
(
true
);
765
if
(
mSurface
->
version
() >=
WL_SURFACE_OFFSET_SINCE_VERSION
) {
766
mSurface
->
offset
(
x
,
y
);
767
mSurface
->
attach
(
buffer
->
buffer
(), 0, 0);
768
}
else
{
769
mSurface
->
attach
(
buffer
->
buffer
(),
x
,
y
);
770
}
771
}
else
{
772
mSurface
->
attach
(
nullptr
, 0, 0);
773
}
774
}
775
776
void
QWaylandWindow
::
attachOffset
(
QWaylandBuffer
*
buffer
)
777
{
778
attach
(
buffer
,
mOffset
.
x
(),
mOffset
.
y
());
779
mOffset
=
QPoint
();
780
}
781
782
void
QWaylandWindow
::
damage
(
const
QRect
&
rect
)
783
{
784
QReadLocker
locker
(&
mSurfaceLock
);
785
if
(
mSurface
==
nullptr
)
786
return
;
787
788
const
qreal
s
=
scale
();
789
if
(
mSurface
->
version
() >= 4) {
790
const
QRect
bufferRect
=
791
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
792
.
toAlignedRect
();
793
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
794
bufferRect
.
height
());
795
}
else
{
796
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
797
}
798
}
799
800
void
QWaylandWindow
::
safeCommit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
801
{
802
if
(
isExposed
()) {
803
commit
(
buffer
,
damage
);
804
}
else
if
(!
buffer
->
committed
()) {
805
buffer
->
setBusy
(
false
);
806
}
807
}
808
809
bool
QWaylandWindow
::
allowsIndependentThreadedRendering
()
const
810
{
811
return
!
mWaitingToApplyConfigure
;
812
}
813
814
void
QWaylandWindow
::
commit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
815
{
816
Q_ASSERT
(
isExposed
());
817
818
QReadLocker
locker
(&
mSurfaceLock
);
819
if
(
buffer
->
committed
()) {
820
mSurface
->
commit
();
821
qCDebug
(
lcWaylandBackingstore
) <<
"Buffer already committed, not attaching."
;
822
return
;
823
}
824
825
if
(!
mSurface
)
826
return
;
827
828
attachOffset
(
buffer
);
829
if
(
mSurface
->
version
() >= 4) {
830
const
qreal
s
=
scale
();
831
for
(
const
QRect
&
rect
:
damage
) {
832
const
QRect
bufferRect
=
833
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
834
.
toAlignedRect
();
835
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
836
bufferRect
.
height
());
837
}
838
}
else
{
839
for
(
const
QRect
&
rect
:
damage
)
840
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
841
}
842
Q_ASSERT
(!
buffer
->
committed
());
843
buffer
->
setCommitted
();
844
mSurface
->
commit
();
845
}
846
847
void
QWaylandWindow
::
commit
()
848
{
849
QReadLocker
locker
(&
mSurfaceLock
);
850
if
(
mSurface
!=
nullptr
)
851
mSurface
->
commit
();
852
}
853
854
const
wl_callback_listener
QWaylandWindow
::
callbackListener
= {
855
[](
void
*
data
,
wl_callback
*
callback
,
uint32_t
time
) {
856
Q_UNUSED
(
time
);
857
auto
*
window
=
static_cast
<
QWaylandWindow
*>(
data
);
858
window
->
handleFrameCallback
(
callback
);
859
}
860
};
861
862
void
QWaylandWindow
::
handleFrameCallback
(
wl_callback
*
callback
)
863
{
864
QMutexLocker
locker
(&
mFrameSyncMutex
);
865
if
(
mFrameCallback
!=
callback
) {
866
// This means the callback is already unset by QWaylandWindow::reset.
867
// The wl_callback object will be destroyed there too.
868
return
;
869
}
870
wl_callback_destroy
(
mFrameCallback
);
871
mFrameCallback
=
nullptr
;
872
mFrameCallbackElapsedTimer
.
invalidate
();
873
874
// The rest can wait until we can run it on the correct thread
875
if
(
mWaitingForUpdateDelivery
.
testAndSetAcquire
(
false
,
true
)) {
876
// Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
877
// in the single-threaded case.
878
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
doHandleFrameCallback
,
Qt
::
QueuedConnection
);
879
}
880
mFrameSyncWait
.
notify_all
();
881
}
882
883
void
QWaylandWindow
::
doHandleFrameCallback
()
884
{
885
mWaitingForUpdateDelivery
.
storeRelease
(
false
);
886
bool
wasExposed
=
isExposed
();
887
mFrameCallbackTimedOut
=
false
;
888
// Did setting mFrameCallbackTimedOut make the window exposed?
889
updateExposure
();
890
if
(
wasExposed
&&
hasPendingUpdateRequest
())
891
deliverUpdateRequest
();
892
893
}
894
895
bool
QWaylandWindow
::
waitForFrameSync
(
int
timeout
)
896
{
897
QMutexLocker
locker
(&
mFrameSyncMutex
);
898
899
QDeadlineTimer
deadline
(
timeout
);
900
while
(
mFrameCallback
&&
mFrameSyncWait
.
wait
(&
mFrameSyncMutex
,
deadline
)) { }
901
902
if
(
mFrameCallback
) {
903
qCDebug
(
lcWaylandBackingstore
) <<
"Didn't receive frame callback in time, window should now be inexposed"
;
904
mFrameCallbackTimedOut
=
true
;
905
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
updateExposure
,
Qt
::
QueuedConnection
);
906
}
907
908
return
!
mFrameCallback
;
909
}
910
911
QMargins
QWaylandWindow
::
frameMargins
()
const
912
{
913
if
(
auto
*
dec
=
decoration
())
914
return
dec
->
margins
();
915
else
if
(
mShellSurface
)
916
return
mShellSurface
->
serverSideFrameMargins
();
917
else
918
return
QPlatformWindow
::
frameMargins
();
919
}
920
921
QMargins
QWaylandWindow
::
clientSideMargins
()
const
922
{
923
if
(
auto
*
dec
=
decoration
())
924
return
dec
->
margins
();
925
return
QMargins
{};
926
}
927
928
void
QWaylandWindow
::
setCustomMargins
(
const
QMargins
&
margins
) {
929
const
QMargins
oldMargins
=
mCustomMargins
;
930
mCustomMargins
=
margins
;
931
propagateSizeHints
();
932
setGeometry
(
geometry
().
marginsRemoved
(
oldMargins
).
marginsAdded
(
margins
));
933
}
934
935
QMargins
QWaylandWindow
::
windowContentMargins
()
const
936
{
937
QMargins
shadowMargins
;
938
939
if
(
auto
*
dec
=
decoration
())
940
shadowMargins
=
dec
->
margins
(
QWaylandAbstractDecoration
::
ShadowsOnly
);
941
942
if
(!
mCustomMargins
.
isNull
())
943
shadowMargins
+=
mCustomMargins
;
944
945
return
shadowMargins
;
946
}
947
948
/*!
949
* Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
950
* topLeft is where the shadow stops and the decorations border start.
951
*/
952
QRect
QWaylandWindow
::
windowContentGeometry
()
const
953
{
954
return
QRect
(
QPoint
(),
geometry
().
size
())
955
.
marginsAdded
(
clientSideMargins
())
956
.
marginsRemoved
(
windowContentMargins
());
957
}
958
959
QMargins
QWaylandWindow
::
safeAreaMargins
()
const
960
{
961
return
m_safeAreaMargins
;
962
}
963
964
void
QWaylandWindow
::
setSafeAreaMargins
(
const
QMargins
&
margins
)
965
{
966
m_safeAreaMargins
=
margins
;
967
QWindowSystemInterface
::
handleSafeAreaMarginsChanged
(
window
());
968
}
969
970
wl_surface
*
QWaylandWindow
::
wlSurface
()
const
971
{
972
QReadLocker
locker
(&
mSurfaceLock
);
973
return
mSurface
?
mSurface
->
object
() :
nullptr
;
974
}
975
976
QWaylandShellSurface
*
QWaylandWindow
::
shellSurface
()
const
977
{
978
return
mShellSurface
;
979
}
980
981
std
::
any
QWaylandWindow
::
_surfaceRole
()
const
982
{
983
if
(
mSubSurfaceWindow
)
984
return
mSubSurfaceWindow
->
object
();
985
if
(
mShellSurface
)
986
return
mShellSurface
->
surfaceRole
();
987
return
{};
988
}
989
990
QWaylandSubSurface
*
QWaylandWindow
::
subSurfaceWindow
()
const
991
{
992
return
mSubSurfaceWindow
;
993
}
994
995
QWaylandScreen
*
QWaylandWindow
::
waylandScreen
()
const
996
{
997
auto
*
platformScreen
=
QPlatformWindow
::
screen
();
998
Q_ASSERT
(
platformScreen
);
999
if
(
platformScreen
->
isPlaceholder
())
1000
return
nullptr
;
1001
return
static_cast
<
QWaylandScreen
*>(
platformScreen
);
1002
}
1003
1004
void
QWaylandWindow
::
handleContentOrientationChange
(
Qt
::
ScreenOrientation
orientation
)
1005
{
1006
mLastReportedContentOrientation
=
orientation
;
1007
updateBufferTransform
();
1008
}
1009
1010
void
QWaylandWindow
::
updateBufferTransform
()
1011
{
1012
QReadLocker
locker
(&
mSurfaceLock
);
1013
if
(
mSurface
==
nullptr
||
mSurface
->
version
() < 2)
1014
return
;
1015
1016
wl_output_transform
transform
;
1017
Qt
::
ScreenOrientation
screenOrientation
=
Qt
::
PrimaryOrientation
;
1018
1019
if
(
mSurface
->
version
() >= 6) {
1020
const
auto
transform
=
mSurface
->
preferredBufferTransform
().
value_or
(
WL_OUTPUT_TRANSFORM_NORMAL
);
1021
if
(
auto
screen
=
waylandScreen
())
1022
screenOrientation
=
screen
->
toScreenOrientation
(
transform
,
Qt
::
PrimaryOrientation
);
1023
}
else
{
1024
if
(
auto
screen
=
window
()->
screen
())
1025
screenOrientation
=
screen
->
primaryOrientation
();
1026
}
1027
1028
const
bool
isPortrait
= (
screenOrientation
==
Qt
::
PortraitOrientation
);
1029
1030
switch
(
mLastReportedContentOrientation
) {
1031
case
Qt
::
PrimaryOrientation
:
1032
transform
=
WL_OUTPUT_TRANSFORM_NORMAL
;
1033
break
;
1034
case
Qt
::
LandscapeOrientation
:
1035
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_270
:
WL_OUTPUT_TRANSFORM_NORMAL
;
1036
break
;
1037
case
Qt
::
PortraitOrientation
:
1038
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_NORMAL
:
WL_OUTPUT_TRANSFORM_90
;
1039
break
;
1040
case
Qt
::
InvertedLandscapeOrientation
:
1041
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_90
:
WL_OUTPUT_TRANSFORM_180
;
1042
break
;
1043
case
Qt
::
InvertedPortraitOrientation
:
1044
transform
=
isPortrait
?
WL_OUTPUT_TRANSFORM_180
:
WL_OUTPUT_TRANSFORM_270
;
1045
break
;
1046
default
:
1047
Q_UNREACHABLE
();
1048
}
1049
mSurface
->
set_buffer_transform
(
transform
);
1050
}
1051
1052
void
QWaylandWindow
::
setOrientationMask
(
Qt
::
ScreenOrientations
mask
)
1053
{
1054
if
(
mShellSurface
)
1055
mShellSurface
->
setContentOrientationMask
(
mask
);
1056
}
1057
1058
void
QWaylandWindow
::
setWindowState
(
Qt
::
WindowStates
states
)
1059
{
1060
if
(
mShellSurface
)
1061
mShellSurface
->
requestWindowStates
(
states
);
1062
}
1063
1064
void
QWaylandWindow
::
setWindowFlags
(
Qt
::
WindowFlags
flags
)
1065
{
1066
const
bool
wasPopup
=
mFlags
.
testFlag
(
Qt
::
Popup
);
1067
const
bool
isPopup
=
flags
.
testFlag
(
Qt
::
Popup
);
1068
1069
mFlags
=
flags
;
1070
// changing role is not allowed on XdgShell on the same wl_surface
1071
if
(
wasPopup
!=
isPopup
) {
1072
reset
();
1073
initializeWlSurface
();
1074
if
(
window
()->
isVisible
()) {
1075
initWindow
();
1076
}
1077
}
else
{
1078
if
(
mShellSurface
)
1079
mShellSurface
->
setWindowFlags
(
flags
);
1080
}
1081
1082
updateNeedsDecoration
();
1083
1084
if
(!
m_cutouts
&& !
QPlatformWindow
::
parent
() &&
flags
.
testFlag
(
Qt
::
ExpandedClientAreaHint
) &&
mDisplay
->
cutoutsManager
()) {
1085
m_cutouts
=
std
::
make_unique
<
QtWaylandClient
::
Cutouts
>(
mDisplay
->
cutoutsManager
()->
get_cutouts
(
surface
()),
this
);
1086
}
else
if
(
m_cutouts
&& !
flags
.
testFlag
(
Qt
::
ExpandedClientAreaHint
)) {
1087
m_cutouts
.
reset
();
1088
setSafeAreaMargins
({});
1089
}
1090
1091
QReadLocker
locker
(&
mSurfaceLock
);
1092
updateInputRegion
();
1093
}
1094
1095
Qt
::
WindowFlags
QWaylandWindow
::
windowFlags
()
const
1096
{
1097
return
mFlags
;
1098
}
1099
1100
void
QWaylandWindow
::
updateNeedsDecoration
()
1101
{
1102
const
bool
hadDecoration
=
mWindowDecorationHandler
->
decoration
() !=
nullptr
;
1103
mWindowDecorationHandler
->
updateNeedsDecoration
();
1104
const
bool
hasDecoration
=
mWindowDecorationHandler
->
decoration
() !=
nullptr
;
1105
if
(
hadDecoration
!=
hasDecoration
) {
1106
requestForcedUpdate
();
1107
propagateSizeHints
();
1108
}
1109
}
1110
1111
QWaylandAbstractDecoration
*
QWaylandWindow
::
decoration
()
const
1112
{
1113
return
mWindowDecorationHandler
->
decoration
();
1114
}
1115
1116
static
QWaylandWindow *
closestShellSurfaceWindow
(QWindow *window)
1117
{
1118
while
(window) {
1119
auto
w =
static_cast
<QWaylandWindow *>(window->handle());
1120
if
(w && w->shellSurface())
1121
return
w;
1122
window = window->transientParent() ? window->transientParent() : window->parent();
1123
}
1124
return
nullptr
;
1125
}
1126
1127
QWaylandWindow
*
QWaylandWindow
::
transientParent
()
const
1128
{
1129
return
mTransientParent
;
1130
}
1131
1132
QWaylandWindow
*
QWaylandWindow
::
guessTransientParent
()
const
1133
{
1134
// Take the closest window with a shell surface, since the transient parent may be a
1135
// QWidgetWindow or some other window without a shell surface, which is then not able to
1136
// get mouse events.
1137
if
(
auto
transientParent
=
closestShellSurfaceWindow
(
window
()->
transientParent
()))
1138
return
transientParent
;
1139
1140
if
(
window
()->
type
() ==
Qt
::
Popup
) {
1141
if
(
mTopPopup
)
1142
return
mTopPopup
;
1143
}
1144
1145
if
(
window
()->
type
() ==
Qt
::
ToolTip
||
window
()->
type
() ==
Qt
::
Popup
) {
1146
if
(
auto
lastInputWindow
=
display
()->
lastInputWindow
())
1147
return
closestShellSurfaceWindow
(
lastInputWindow
->
window
());
1148
}
1149
1150
return
nullptr
;
1151
}
1152
1153
void
QWaylandWindow
::
handleMouse
(
QWaylandInputDevice
*
inputDevice
,
const
QWaylandPointerEvent
&
e
)
1154
{
1155
// There's currently no way to get info about the actual hardware device in use.
1156
// At least we get the correct seat.
1157
const
QPointingDevice
*
device
=
QPointingDevice
::
primaryPointingDevice
(
inputDevice
->
seatname
());
1158
if
(
e
.
type
==
QEvent
::
Leave
) {
1159
QWindowSystemInterface
::
handleLeaveEvent
(
window
());
1160
#
if
QT_CONFIG
(
cursor
)
1161
restoreMouseCursor
(
inputDevice
);
1162
#
endif
1163
return
;
1164
}
1165
1166
#
if
QT_CONFIG
(
cursor
)
1167
if
(
e
.
type
==
QEvent
::
Enter
) {
1168
restoreMouseCursor
(
inputDevice
);
1169
}
1170
#
endif
1171
1172
switch
(
e
.
type
) {
1173
case
QEvent
::
Enter
:
1174
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
e
.
local
,
e
.
global
);
1175
#
if
QT_CONFIG
(
cursor
)
1176
mDisplay
->
waylandCursor
()->
setPosFromEnterEvent
(
e
.
global
.
toPoint
());
1177
#
endif
1178
break
;
1179
case
QEvent
::
MouseButtonPress
:
1180
case
QEvent
::
MouseButtonRelease
:
1181
case
QEvent
::
MouseMove
:
1182
QWindowSystemInterface
::
handleMouseEvent
(
window
(),
e
.
timestamp
,
device
,
e
.
local
,
e
.
global
,
e
.
buttons
,
e
.
button
,
e
.
type
,
e
.
modifiers
);
1183
break
;
1184
case
QEvent
::
Wheel
:
1185
QWindowSystemInterface
::
handleWheelEvent
(
window
(),
e
.
timestamp
,
device
,
e
.
local
,
e
.
global
,
1186
e
.
pixelDelta
,
e
.
angleDelta
,
e
.
modifiers
,
1187
e
.
phase
,
e
.
source
,
e
.
inverted
);
1188
break
;
1189
default
:
1190
Q_UNREACHABLE
();
1191
}
1192
1193
}
1194
1195
#
ifndef
QT_NO_GESTURES
1196
void
QWaylandWindow
::
handleSwipeGesture
(
QWaylandInputDevice
*
inputDevice
,
1197
const
QWaylandPointerGestureSwipeEvent
&
e
)
1198
{
1199
switch
(
e
.
state
) {
1200
case
Qt
::
GestureStarted
:
1201
if
(
mGestureState
!=
GestureNotActive
)
1202
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected GestureStarted while already active"
;
1203
1204
1205
mGestureState
=
GestureActiveInContentArea
;
1206
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1207
inputDevice
->
mTouchPadDevice
,
1208
Qt
::
BeginNativeGesture
,
1209
e
.
local
,
e
.
global
,
e
.
fingers
);
1210
break
;
1211
case
Qt
::
GestureUpdated
:
1212
if
(
mGestureState
!=
GestureActiveInContentArea
)
1213
return
;
1214
1215
if
(!
e
.
delta
.
isNull
()) {
1216
QWindowSystemInterface
::
handleGestureEventWithValueAndDelta
(
1217
window
(),
e
.
timestamp
,
inputDevice
->
mTouchPadDevice
,
1218
Qt
::
PanNativeGesture
,
1219
0,
e
.
delta
,
e
.
local
,
e
.
global
,
e
.
fingers
);
1220
}
1221
break
;
1222
case
Qt
::
GestureFinished
:
1223
case
Qt
::
GestureCanceled
:
1224
1225
if
(
mGestureState
!=
GestureActiveInContentArea
)
1226
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected"
<< (
e
.
state
==
Qt
::
GestureFinished
?
"GestureFinished"
:
"GestureCanceled"
);
1227
1228
mGestureState
=
GestureNotActive
;
1229
1230
// There's currently no way to expose cancelled gestures to the rest of Qt, so
1231
// this part of information is lost.
1232
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1233
inputDevice
->
mTouchPadDevice
,
1234
Qt
::
EndNativeGesture
,
1235
e
.
local
,
e
.
global
,
e
.
fingers
);
1236
break
;
1237
default
:
1238
break
;
1239
}
1240
}
1241
1242
void
QWaylandWindow
::
handlePinchGesture
(
QWaylandInputDevice
*
inputDevice
,
1243
const
QWaylandPointerGesturePinchEvent
&
e
)
1244
{
1245
switch
(
e
.
state
) {
1246
case
Qt
::
GestureStarted
:
1247
if
(
mGestureState
!=
GestureNotActive
)
1248
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected GestureStarted while already active"
;
1249
1250
mGestureState
=
GestureActiveInContentArea
;
1251
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1252
inputDevice
->
mTouchPadDevice
,
1253
Qt
::
BeginNativeGesture
,
1254
e
.
local
,
e
.
global
,
e
.
fingers
);
1255
break
;
1256
case
Qt
::
GestureUpdated
:
1257
if
(
mGestureState
!=
GestureActiveInContentArea
)
1258
return
;
1259
1260
if
(!
e
.
delta
.
isNull
()) {
1261
QWindowSystemInterface
::
handleGestureEventWithValueAndDelta
(
1262
window
(),
e
.
timestamp
,
inputDevice
->
mTouchPadDevice
,
1263
Qt
::
PanNativeGesture
,
1264
0,
e
.
delta
,
e
.
local
,
e
.
global
,
e
.
fingers
);
1265
}
1266
if
(
e
.
rotation_delta
!= 0) {
1267
QWindowSystemInterface
::
handleGestureEventWithRealValue
(
window
(),
e
.
timestamp
,
1268
inputDevice
->
mTouchPadDevice
,
1269
Qt
::
RotateNativeGesture
,
1270
e
.
rotation_delta
,
1271
e
.
local
,
e
.
global
,
e
.
fingers
);
1272
}
1273
if
(
e
.
scale_delta
!= 0) {
1274
QWindowSystemInterface
::
handleGestureEventWithRealValue
(
window
(),
e
.
timestamp
,
1275
inputDevice
->
mTouchPadDevice
,
1276
Qt
::
ZoomNativeGesture
,
1277
e
.
scale_delta
,
1278
e
.
local
,
e
.
global
,
e
.
fingers
);
1279
}
1280
break
;
1281
case
Qt
::
GestureFinished
:
1282
case
Qt
::
GestureCanceled
:
1283
if
(
mGestureState
!=
GestureActiveInContentArea
)
1284
qCWarning
(
lcQpaWaylandInput
) <<
"Unexpected"
<< (
e
.
state
==
Qt
::
GestureFinished
?
"GestureFinished"
:
"GestureCanceled"
);
1285
1286
mGestureState
=
GestureNotActive
;
1287
1288
// There's currently no way to expose cancelled gestures to the rest of Qt, so
1289
// this part of information is lost.
1290
QWindowSystemInterface
::
handleGestureEvent
(
window
(),
e
.
timestamp
,
1291
inputDevice
->
mTouchPadDevice
,
1292
Qt
::
EndNativeGesture
,
1293
e
.
local
,
e
.
global
,
e
.
fingers
);
1294
break
;
1295
default
:
1296
break
;
1297
}
1298
}
1299
1300
#
endif
// #ifndef QT_NO_GESTURES
1301
1302
1303
QPointF
QWaylandWindow
::
mapInputToGlobal
(
const
QPointF
&
pointIn
)
1304
{
1305
return
mapToGlobalF
(
pointIn
);
1306
}
1307
1308
void
QWaylandWindow
::
handleScreensChanged
()
1309
{
1310
QPlatformScreen
*
newScreen
=
calculateScreenFromSurfaceEvents
();
1311
1312
if
(!
newScreen
||
newScreen
->
screen
() ==
window
()->
screen
())
1313
return
;
1314
1315
QWindowSystemInterface
::
handleWindowScreenChanged
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
newScreen
->
QPlatformScreen
::
screen
());
1316
1317
if
(
fixedToplevelPositions
&& !
QPlatformWindow
::
parent
() &&
window
()->
type
() !=
Qt
::
Popup
1318
&&
window
()->
type
() !=
Qt
::
ToolTip
&&
window
()->
type
() !=
Qt
::
Tool
1319
&&
geometry
().
topLeft
() !=
newScreen
->
geometry
().
topLeft
()) {
1320
auto
geometry
=
this
->
geometry
();
1321
geometry
.
moveTo
(
newScreen
->
geometry
().
topLeft
());
1322
setGeometry
(
geometry
);
1323
}
1324
1325
updateScale
();
1326
updateBufferTransform
();
1327
}
1328
1329
void
QWaylandWindow
::
updateScale
()
1330
{
1331
if
(
mFractionalScale
) {
1332
qreal
preferredScale
=
mFractionalScale
->
preferredScale
().
value_or
(1.0);
1333
preferredScale
=
std
::
max
<
qreal
>(1.0,
preferredScale
);
1334
Q_ASSERT
(
mViewport
);
1335
setScale
(
preferredScale
);
1336
return
;
1337
}
1338
1339
if
(
mSurface
&&
mSurface
->
version
() >= 6) {
1340
auto
preferredScale
=
mSurface
->
preferredBufferScale
().
value_or
(1);
1341
preferredScale
=
std
::
max
(1,
preferredScale
);
1342
setScale
(
preferredScale
);
1343
return
;
1344
}
1345
1346
int
scale
=
screen
()->
isPlaceholder
() ? 1 :
static_cast
<
QWaylandScreen
*>(
screen
())->
scale
();
1347
setScale
(
scale
);
1348
}
1349
1350
void
QWaylandWindow
::
setScale
(
qreal
newScale
)
1351
{
1352
if
(
mScale
.
has_value
() &&
qFuzzyCompare
(
mScale
.
value
(),
newScale
))
1353
return
;
1354
mScale
=
newScale
;
1355
1356
if
(
mSurface
) {
1357
if
(
mViewport
)
1358
updateViewport
();
1359
else
if
(
mSurface
->
version
() >= 3)
1360
mSurface
->
set_buffer_scale
(
std
::
ceil
(
newScale
));
1361
}
1362
ensureSize
();
1363
1364
QWindowSystemInterface
::
handleWindowDevicePixelRatioChanged
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
());
1365
if
(
isExposed
()) {
1366
// redraw at the new DPR
1367
window
()->
requestUpdate
();
1368
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
1369
}
1370
}
1371
1372
#
if
QT_CONFIG
(
cursor
)
1373
void
QWaylandWindow
::
restoreMouseCursor
(
QWaylandInputDevice
*
device
)
1374
{
1375
if
(
const
QCursor
*
overrideCursor
=
QGuiApplication
::
overrideCursor
()) {
1376
applyCursor
(
device
, *
overrideCursor
);
1377
}
else
if
(
mHasStoredCursor
) {
1378
applyCursor
(
device
,
mStoredCursor
);
1379
}
else
{
1380
applyCursor
(
device
,
QCursor
(
Qt
::
ArrowCursor
));
1381
}
1382
}
1383
1384
void
QWaylandWindow
::
resetStoredCursor
()
1385
{
1386
mHasStoredCursor
=
false
;
1387
}
1388
1389
// Keep track of application set cursors on each window
1390
void
QWaylandWindow
::
setStoredCursor
(
const
QCursor
&
cursor
) {
1391
mStoredCursor
=
cursor
;
1392
mHasStoredCursor
=
true
;
1393
}
1394
1395
void
QWaylandWindow
::
applyCursor
(
QWaylandInputDevice
*
device
,
const
QCursor
&
cursor
) {
1396
if
(!
device
|| !
device
->
pointer
() ||
device
->
pointer
()->
focusSurface
() !=
waylandSurface
())
1397
return
;
1398
1399
int
fallbackBufferScale
=
qCeil
(
devicePixelRatio
());
1400
device
->
setCursor
(&
cursor
, {},
fallbackBufferScale
);
1401
}
1402
#
endif
1403
1404
void
QWaylandWindow
::
requestActivateWindow
()
1405
{
1406
QWaylandWindow
*
toplevel
=
this
;
1407
while
(
auto
parent
=
static_cast
<
QWaylandWindow
*>(
toplevel
->
QPlatformWindow
::
parent
()))
1408
toplevel
=
parent
;
1409
1410
if
(
toplevel
!=
this
) {
1411
if
(
window
()->
isVisible
() &&
mDisplay
->
isWindowActivated
(
toplevel
))
1412
mDisplay
->
setActiveSubSurface
(
this
);
1413
return
;
1414
}
1415
1416
if
(
QWaylandWindow
*
sub
=
mDisplay
->
activeSubSurface
();
1417
sub
&&
window
()->
isAncestorOf
(
sub
->
window
(),
QWindow
::
ExcludeTransients
)) {
1418
mDisplay
->
setActiveSubSurface
(
nullptr
);
1419
return
;
1420
}
1421
1422
if
(
mShellSurface
)
1423
mShellSurface
->
requestActivate
();
1424
}
1425
1426
bool
QWaylandWindow
::
calculateExposure
()
const
1427
{
1428
if
(!
window
()->
isVisible
())
1429
return
false
;
1430
1431
if
(
mFrameCallbackTimedOut
)
1432
return
false
;
1433
1434
if
(
mShellSurface
)
1435
return
mShellSurface
->
isExposed
();
1436
1437
if
(
mSubSurfaceWindow
)
1438
return
mSubSurfaceWindow
->
parent
()->
isExposed
();
1439
1440
return
!(
shouldCreateShellSurface
() ||
shouldCreateSubSurface
());
1441
}
1442
1443
void
QWaylandWindow
::
updateExposure
()
1444
{
1445
bool
exposed
=
calculateExposure
();
1446
if
(
exposed
==
mExposed
)
1447
return
;
1448
1449
mExposed
=
exposed
;
1450
1451
if
(!
exposed
)
1452
sendExposeEvent
(
QRect
());
1453
else
1454
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
1455
1456
for
(
QWaylandSubSurface
*
subSurface
:
std
::
as_const
(
mChildren
)) {
1457
auto
subWindow
=
subSurface
->
window
();
1458
subWindow
->
updateExposure
();
1459
}
1460
}
1461
1462
bool
QWaylandWindow
::
isExposed
()
const
1463
{
1464
return
mExposed
;
1465
}
1466
1467
bool
QWaylandWindow
::
isActive
()
const
1468
{
1469
return
mDisplay
->
isWindowActivated
(
this
);
1470
}
1471
1472
qreal
QWaylandWindow
::
scale
()
const
1473
{
1474
return
devicePixelRatio
();
1475
}
1476
1477
qreal
QWaylandWindow
::
devicePixelRatio
()
const
1478
{
1479
return
mScale
.
value_or
(
waylandScreen
() ?
waylandScreen
()->
scale
() : 1);
1480
}
1481
1482
bool
QWaylandWindow
::
setMouseGrabEnabled
(
bool
grab
)
1483
{
1484
if
(
window
()->
type
() !=
Qt
::
Popup
) {
1485
qWarning
(
"This plugin supports grabbing the mouse only for popup windows"
);
1486
return
false
;
1487
}
1488
1489
mMouseGrab
=
grab
?
this
:
nullptr
;
1490
return
true
;
1491
}
1492
1493
QWaylandWindow
::
ToplevelWindowTilingStates
QWaylandWindow
::
toplevelWindowTilingStates
()
const
1494
{
1495
return
mLastReportedToplevelWindowTilingStates
;
1496
}
1497
1498
void
QWaylandWindow
::
handleToplevelWindowTilingStatesChanged
(
ToplevelWindowTilingStates
states
)
1499
{
1500
mLastReportedToplevelWindowTilingStates
=
states
;
1501
}
1502
1503
Qt
::
WindowStates
QWaylandWindow
::
windowStates
()
const
1504
{
1505
return
mLastReportedWindowStates
;
1506
}
1507
1508
void
QWaylandWindow
::
handleWindowStatesChanged
(
Qt
::
WindowStates
states
)
1509
{
1510
updateNeedsDecoration
();
1511
Qt
::
WindowStates
statesWithoutActive
=
states
& ~
Qt
::
WindowActive
;
1512
Qt
::
WindowStates
lastStatesWithoutActive
=
mLastReportedWindowStates
& ~
Qt
::
WindowActive
;
1513
QWindowSystemInterface
::
handleWindowStateChanged
(
window
(),
statesWithoutActive
,
1514
lastStatesWithoutActive
);
1515
mLastReportedWindowStates
=
states
;
1516
}
1517
1518
void
QWaylandWindow
::
sendProperty
(
const
QString
&
name
,
const
QVariant
&
value
)
1519
{
1520
m_properties
.
insert
(
name
,
value
);
1521
QWaylandNativeInterface
*
nativeInterface
=
static_cast
<
QWaylandNativeInterface
*>(
1522
QGuiApplication
::
platformNativeInterface
());
1523
nativeInterface
->
emitWindowPropertyChanged
(
this
,
name
);
1524
if
(
mShellSurface
)
1525
mShellSurface
->
sendProperty
(
name
,
value
);
1526
}
1527
1528
void
QWaylandWindow
::
setProperty
(
const
QString
&
name
,
const
QVariant
&
value
)
1529
{
1530
m_properties
.
insert
(
name
,
value
);
1531
QWaylandNativeInterface
*
nativeInterface
=
static_cast
<
QWaylandNativeInterface
*>(
1532
QGuiApplication
::
platformNativeInterface
());
1533
nativeInterface
->
emitWindowPropertyChanged
(
this
,
name
);
1534
}
1535
1536
QVariantMap
QWaylandWindow
::
properties
()
const
1537
{
1538
return
m_properties
;
1539
}
1540
1541
QVariant
QWaylandWindow
::
property
(
const
QString
&
name
)
1542
{
1543
return
m_properties
.
value
(
name
);
1544
}
1545
1546
QVariant
QWaylandWindow
::
property
(
const
QString
&
name
,
const
QVariant
&
defaultValue
)
1547
{
1548
return
m_properties
.
value
(
name
,
defaultValue
);
1549
}
1550
1551
#
ifdef
QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1552
void
QWaylandWindow
::
setBackingStore
(
QPlatformBackingStore
*
store
)
1553
{
1554
mBackingStore
=
dynamic_cast
<
QWaylandShmBackingStore
*>(
store
);
1555
}
1556
#
endif
1557
1558
void
QWaylandWindow
::
timerEvent
(
QTimerEvent
*
event
)
1559
{
1560
if
(
event
->
timerId
() !=
mFrameCallbackCheckIntervalTimerId
)
1561
return
;
1562
1563
{
1564
QMutexLocker
lock
(&
mFrameSyncMutex
);
1565
1566
const
bool
callbackTimerValid
=
mFrameCallbackElapsedTimer
.
isValid
();
1567
const
bool
callbackTimerExpired
=
callbackTimerValid
&&
mFrameCallbackElapsedTimer
.
hasExpired
(
mFrameCallbackTimeout
);
1568
if
(!
callbackTimerValid
||
callbackTimerExpired
) {
1569
killTimer
(
mFrameCallbackCheckIntervalTimerId
);
1570
mFrameCallbackCheckIntervalTimerId
= -1;
1571
}
1572
if
(!
callbackTimerValid
|| !
callbackTimerExpired
) {
1573
return
;
1574
}
1575
mFrameCallbackElapsedTimer
.
invalidate
();
1576
}
1577
1578
qCDebug
(
lcWaylandBackingstore
) <<
"Didn't receive frame callback in time, window should now be inexposed"
;
1579
mFrameCallbackTimedOut
=
true
;
1580
updateExposure
();
1581
}
1582
1583
void
QWaylandWindow
::
requestUpdate
()
1584
{
1585
qCDebug
(
lcWaylandBackingstore
) <<
"requestUpdate"
;
1586
Q_ASSERT
(
hasPendingUpdateRequest
());
// should be set by QPA
1587
1588
// If we have a frame callback all is good and will be taken care of there
1589
{
1590
QMutexLocker
locker
(&
mFrameSyncMutex
);
1591
if
(
mFrameCallback
)
1592
return
;
1593
}
1594
1595
// Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1596
// so use invokeMethod to delay the delivery a bit.
1597
QMetaObject
::
invokeMethod
(
this
, [
this
] {
1598
// Things might have changed in the meantime
1599
{
1600
QMutexLocker
locker
(&
mFrameSyncMutex
);
1601
if
(
mFrameCallback
)
1602
return
;
1603
}
1604
if
(
hasPendingUpdateRequest
())
1605
deliverUpdateRequest
();
1606
},
Qt
::
QueuedConnection
);
1607
}
1608
1609
// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1610
// with eglSwapBuffers) to know when it's time to commit the next one.
1611
// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1612
void
QWaylandWindow
::
handleUpdate
()
1613
{
1614
mExposeEventNeedsAttachedBuffer
=
false
;
1615
qCDebug
(
lcWaylandBackingstore
) <<
"handleUpdate"
<<
QThread
::
currentThread
();
1616
1617
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
1618
QReadLocker
lock
(&
mSurfaceLock
);
1619
if
(!
mSurface
)
1620
return
;
1621
1622
QMutexLocker
locker
(&
mFrameSyncMutex
);
1623
if
(
mFrameCallback
)
1624
return
;
1625
1626
struct
::
wl_surface
*
wrappedSurface
=
reinterpret_cast
<
struct
::
wl_surface
*>(
wl_proxy_create_wrapper
(
mSurface
->
object
()));
1627
wl_proxy_set_queue
(
reinterpret_cast
<
wl_proxy
*>(
wrappedSurface
),
mDisplay
->
frameEventQueue
());
1628
mFrameCallback
=
wl_surface_frame
(
wrappedSurface
);
1629
wl_proxy_wrapper_destroy
(
wrappedSurface
);
1630
wl_callback_add_listener
(
mFrameCallback
, &
QWaylandWindow
::
callbackListener
,
this
);
1631
1632
// Start a timer for handling the case when the compositor stops sending frame callbacks.
1633
if
(
mFrameCallbackTimeout
> 0) {
1634
QMetaObject
::
invokeMethod
(
this
, [
this
] {
1635
QMutexLocker
locker
(&
mFrameSyncMutex
);
1636
1637
if
(
mFrameCallback
) {
1638
if
(
mFrameCallbackCheckIntervalTimerId
< 0)
1639
mFrameCallbackCheckIntervalTimerId
=
startTimer
(
mFrameCallbackTimeout
);
1640
mFrameCallbackElapsedTimer
.
start
();
1641
}
1642
},
Qt
::
QueuedConnection
);
1643
}
1644
}
1645
1646
void
QWaylandWindow
::
requestForcedUpdate
()
1647
{
1648
mPendingForcedUpdate
=
true
;
1649
window
()->
requestUpdate
();
1650
}
1651
1652
void
QWaylandWindow
::
deliverUpdateRequest
()
1653
{
1654
qCDebug
(
lcWaylandBackingstore
) <<
"deliverUpdateRequest"
;
1655
if
(
decoration
())
1656
mWindowDecorationHandler
->
update
();
1657
QPlatformWindow
::
deliverUpdateRequest
();
1658
1659
// an update request doesn't always force a commit if the window isn't damaged, so also force it
1660
if
(
mPendingForcedUpdate
) {
1661
mPendingForcedUpdate
=
false
;
1662
QRect
exposeGeometry
(
QPoint
(),
geometry
().
size
());
1663
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
exposeGeometry
);
1664
}
1665
}
1666
1667
void
QWaylandWindow
::
addAttachOffset
(
const
QPoint
point
)
1668
{
1669
mOffset
+=
point
;
1670
}
1671
1672
void
QWaylandWindow
::
propagateSizeHints
()
1673
{
1674
if
(
mShellSurface
)
1675
mShellSurface
->
propagateSizeHints
();
1676
}
1677
1678
bool
QWaylandWindow
::
startSystemResize
(
Qt
::
Edges
edges
)
1679
{
1680
if
(
auto
*
seat
=
display
()->
lastInputDevice
()) {
1681
bool
rc
=
mShellSurface
&&
mShellSurface
->
resize
(
seat
,
edges
);
1682
seat
->
handleEndDrag
();
1683
return
rc
;
1684
}
1685
return
false
;
1686
}
1687
1688
bool
QtWaylandClient
::
QWaylandWindow
::
startSystemMove
()
1689
{
1690
if
(
auto
seat
=
display
()->
lastInputDevice
()) {
1691
bool
rc
=
mShellSurface
&&
mShellSurface
->
move
(
seat
);
1692
seat
->
handleEndDrag
();
1693
return
rc
;
1694
}
1695
return
false
;
1696
}
1697
1698
bool
QWaylandWindow
::
isOpaque
()
const
1699
{
1700
return
window
()->
requestedFormat
().
alphaBufferSize
() <= 0;
1701
}
1702
1703
void
QWaylandWindow
::
setOpaqueArea
(
const
QRegion
&
opaqueArea
)
1704
{
1705
if
(
opaqueArea
==
mOpaqueArea
|| !
mSurface
)
1706
return
;
1707
1708
mOpaqueArea
=
opaqueArea
;
1709
1710
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
opaqueArea
);
1711
mSurface
->
set_opaque_region
(
region
);
1712
wl_region_destroy
(
region
);
1713
}
1714
1715
void
QWaylandWindow
::
requestXdgActivationToken
(
uint
serial
)
1716
{
1717
if
(!
mShellSurface
) {
1718
qCWarning
(
lcQpaWayland
) <<
"requestXdgActivationToken is called with no surface role created, emitting synthetic signal"
;
1719
Q_EMIT
xdgActivationTokenCreated
({});
1720
return
;
1721
}
1722
mShellSurface
->
requestXdgActivationToken
(
serial
);
1723
}
1724
1725
void
QWaylandWindow
::
setXdgActivationToken
(
const
QString
&
token
)
1726
{
1727
if
(
mShellSurface
)
1728
mShellSurface
->
setXdgActivationToken
(
token
);
1729
else
1730
qCWarning
(
lcQpaWayland
) <<
"setXdgActivationToken is called with no surface role created, token"
<<
token
<<
"discarded"
;
1731
}
1732
1733
void
QWaylandWindow
::
addChildPopup
(
QWaylandWindow
*
child
)
1734
{
1735
if
(
mShellSurface
)
1736
mShellSurface
->
attachPopup
(
child
->
shellSurface
());
1737
mChildPopups
.
append
(
child
);
1738
}
1739
1740
void
QWaylandWindow
::
removeChildPopup
(
QWaylandWindow
*
child
)
1741
{
1742
if
(
mShellSurface
)
1743
mShellSurface
->
detachPopup
(
child
->
shellSurface
());
1744
mChildPopups
.
removeAll
(
child
);
1745
}
1746
1747
void
QWaylandWindow
::
closeChildPopups
() {
1748
while
(!
mChildPopups
.
isEmpty
()) {
1749
auto
popup
=
mChildPopups
.
takeLast
();
1750
popup
->
resetSurfaceRole
();
1751
}
1752
}
1753
1754
void
QWaylandWindow
::
reinit
()
1755
{
1756
if
(
window
()->
isVisible
()) {
1757
initWindow
();
1758
if
(
hasPendingUpdateRequest
())
1759
deliverUpdateRequest
();
1760
}
1761
}
1762
1763
bool
QWaylandWindow
::
windowEvent
(
QEvent
*
event
)
1764
{
1765
if
(
event
->
type
() ==
QEvent
::
ApplicationPaletteChange
1766
||
event
->
type
() ==
QEvent
::
ApplicationFontChange
) {
1767
if
(
auto
*
dec
=
decoration
();
dec
&&
window
()->
isVisible
())
1768
dec
->
update
();
1769
}
else
if
(
event
->
type
() ==
QEvent
::
WindowUnblocked
) {
1770
// QtGui sends leave event to window under cursor when modal window opens, so we have
1771
// to send enter event when modal closes and window has cursor and gets unblocked.
1772
if
(
auto
*
inputDevice
=
mDisplay
->
lastInputDevice
();
inputDevice
&&
inputDevice
->
pointerFocus
() ==
this
) {
1773
const
auto
pos
=
mDisplay
->
waylandCursor
()->
pos
();
1774
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
mapFromGlobalF
(
pos
),
pos
);
1775
}
1776
}
1777
1778
return
QPlatformWindow
::
windowEvent
(
event
);
1779
}
1780
1781
QSurfaceFormat
QWaylandWindow
::
format
()
const
1782
{
1783
return
mSurfaceFormat
;
1784
}
1785
1786
void
QWaylandWindow
::
setSessionRestoreId
(
const
QString
&
role
)
1787
{
1788
mSessionRestoreId
=
role
;
1789
}
1790
1791
QString
QWaylandWindow
::
sessionRestoreId
()
const
1792
{
1793
return
mSessionRestoreId
;
1794
}
1795
1796
void
QWaylandWindow
::
setExtendedWindowType
(
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
windowType
) {
1797
m_popupInfo
.
extendedWindowType
=
windowType
;
1798
}
1799
1800
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
QWaylandWindow
::
extendedWindowType
()
const
1801
{
1802
return
m_popupInfo
.
extendedWindowType
;
1803
}
1804
1805
void
QWaylandWindow
::
setParentControlGeometry
(
const
QRect
&
parentControlGeometry
) {
1806
m_popupInfo
.
parentControlGeometry
=
parentControlGeometry
;
1807
if
(
mExposed
) {
1808
mShellSurface
->
setWindowPosition
(
window
()->
position
());
1809
}
1810
}
1811
1812
QRect
QWaylandWindow
::
parentControlGeometry
()
const
1813
{
1814
return
m_popupInfo
.
parentControlGeometry
;
1815
}
1816
1817
void
QWaylandWindow
::
handleMousePressActivation
()
1818
{
1819
if
(!
window
() ||
window
()->
flags
() &
Qt
::
WindowDoesNotAcceptFocus
)
1820
return
;
1821
1822
const
QWaylandWindow
*
currentActiveSubSurface
=
mDisplay
->
activeSubSurface
();
1823
const
bool
isSubsurface
= !!
subSurfaceWindow
();
1824
1825
auto
toplevelIsActive
= [&](
QWaylandWindow
*
w
) ->
bool
{
1826
while
(
w
&&
w
->
subSurfaceWindow
())
1827
w
=
static_cast
<
QWaylandWindow
*>(
w
->
QPlatformWindow
::
parent
());
1828
return
mDisplay
->
isWindowActivated
(
w
);
1829
};
1830
1831
if
(
isSubsurface
&&
currentActiveSubSurface
!=
this
&&
toplevelIsActive
(
this
))
1832
mDisplay
->
setActiveSubSurface
(
this
);
1833
else
if
(!
isSubsurface
&&
currentActiveSubSurface
)
1834
mDisplay
->
setActiveSubSurface
(
nullptr
);
1835
}
1836
1837
}
1838
1839
QT_END_NAMESPACE
1840
1841
#
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:1116
qtbase
src
plugins
platforms
wayland
qwaylandwindow.cpp
Generated on
for Qt by
1.18.0