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