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