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