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