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