Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandwindow.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#
include
"qwaylandsessionmanager_p.h"
5
#
include
"qwaylandwindow_p.h"
6
7
#
include
"qwaylandbuffer_p.h"
8
#
include
"qwaylandcursor_p.h"
9
#
include
"qwaylanddisplay_p.h"
10
#
include
"qwaylandsurface_p.h"
11
#
include
"qwaylandinputdevice_p.h"
12
#
include
"qwaylandfractionalscale_p.h"
13
#
include
"qwaylandscreen_p.h"
14
#
include
"qwaylandshellsurface_p.h"
15
#
include
"qwaylandsubsurface_p.h"
16
#
include
"qwaylandabstractdecoration_p.h"
17
#
include
"qwaylandplatformservices_p.h"
18
#
include
"qwaylandnativeinterface_p.h"
19
#
include
"qwaylanddecorationfactory_p.h"
20
#
include
"qwaylandshmbackingstore_p.h"
21
#
include
"qwaylandshellintegration_p.h"
22
#
include
"qwaylandviewport_p.h"
23
#
include
"qwaylandcolormanagement_p.h"
24
25
#
include
<
QtCore
/
QFileInfo
>
26
#
include
<
QtCore
/
QPointer
>
27
#
include
<
QtCore
/
QRegularExpression
>
28
#
include
<
QtGui
/
QWindow
>
29
30
#
include
<
QGuiApplication
>
31
#
include
<
qpa
/
qwindowsysteminterface
.
h
>
32
#
include
<
QtGui
/
private
/
qguiapplication_p
.
h
>
33
#
include
<
QtGui
/
private
/
qwindow_p
.
h
>
34
35
#
include
<
QtCore
/
QDebug
>
36
#
include
<
QtCore
/
QThread
>
37
38
#
include
<
QtWaylandClient
/
private
/
qwayland
-
fractional
-
scale
-
v1
.
h
>
39
40
QT_BEGIN_NAMESPACE
41
42
using
namespace
Qt::StringLiterals;
43
44
namespace
QtWaylandClient
{
45
46
Q_LOGGING_CATEGORY
(lcWaylandBackingstore,
"qt.qpa.wayland.backingstore"
)
47
48
QWaylandWindow
*
QWaylandWindow
::
mMouseGrab
=
nullptr
;
49
QWaylandWindow
*
QWaylandWindow
::
mTopPopup
=
nullptr
;
50
51
QWaylandWindow
::
QWaylandWindow
(
QWindow
*
window
,
QWaylandDisplay
*
display
)
52
:
QPlatformWindow
(
window
)
53
,
mDisplay
(
display
)
54
,
mSurfaceLock
(
QReadWriteLock
::
Recursive
)
55
,
mShellIntegration
(
display
->
shellIntegration
())
56
,
mSurfaceFormat
(
window
->
requestedFormat
())
57
{
58
{
59
bool
ok
;
60
int
frameCallbackTimeout
=
qEnvironmentVariableIntValue
(
"QT_WAYLAND_FRAME_CALLBACK_TIMEOUT"
, &
ok
);
61
if
(
ok
)
62
mFrameCallbackTimeout
=
frameCallbackTimeout
;
63
}
64
65
mSurfaceFormat
.
setColorSpace
(
QColorSpace
{});
66
initializeWlSurface
();
67
mFlags
=
window
->
flags
();
68
69
setWindowIcon
(
window
->
icon
());
70
71
connect
(
this
, &
QWaylandWindow
::
wlSurfaceCreated
,
this
,
72
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceCreated
);
73
connect
(
this
, &
QWaylandWindow
::
wlSurfaceDestroyed
,
this
,
74
&
QNativeInterface
::
Private
::
QWaylandWindow
::
surfaceDestroyed
);
75
}
76
77
QWaylandWindow
::~
QWaylandWindow
()
78
{
79
mWindowDecoration
.
reset
();
80
reset
();
81
82
const
QWindow
*
parent
=
window
();
83
const
auto
tlw
=
QGuiApplication
::
topLevelWindows
();
84
for
(
QWindow
*
w
:
tlw
) {
85
if
(
w
->
transientParent
() ==
parent
)
86
QWindowSystemInterface
::
handleCloseEvent
(
w
);
87
}
88
89
if
(
mMouseGrab
==
this
) {
90
mMouseGrab
=
nullptr
;
91
}
92
}
93
94
void
QWaylandWindow
::
ensureSize
()
95
{
96
if
(
mBackingStore
) {
97
setBackingStore
(
mBackingStore
);
98
mBackingStore
->
recreateBackBufferIfNeeded
();
99
}
100
}
101
102
void
QWaylandWindow
::
initWindow
()
103
{
104
resetFrameCallback
();
105
106
if
(
shouldCreateSubSurface
()) {
107
Q_ASSERT
(!
mSubSurfaceWindow
);
108
109
auto
*
parent
=
static_cast
<
QWaylandWindow
*>(
QPlatformWindow
::
parent
());
110
if
(!
parent
->
mSurface
)
111
parent
->
initializeWlSurface
();
112
if
(
parent
->
wlSurface
()) {
113
if
(::
wl_subsurface
*
subsurface
=
mDisplay
->
createSubSurface
(
this
,
parent
))
114
mSubSurfaceWindow
=
new
QWaylandSubSurface
(
this
,
parent
,
subsurface
);
115
}
116
}
else
if
(
shouldCreateShellSurface
()) {
117
Q_ASSERT
(!
mShellSurface
);
118
Q_ASSERT
(
mShellIntegration
);
119
mTransientParent
=
guessTransientParent
();
120
if
(
mTransientParent
) {
121
if
(
window
()->
type
() ==
Qt
::
Popup
) {
122
if
(
mTopPopup
&&
mTopPopup
!=
mTransientParent
) {
123
qCWarning
(
lcQpaWayland
) <<
"Creating a popup with a parent,"
<<
mTransientParent
->
window
()
124
<<
"which does not match the current topmost grabbing popup,"
125
<<
mTopPopup
->
window
() <<
"With some shell surface protocols, this"
126
<<
"is not allowed. The wayland QPA plugin is currently handling"
127
<<
"it by setting the parent to the topmost grabbing popup."
128
<<
"Note, however, that this may cause positioning errors and"
129
<<
"popups closing unxpectedly. Please fix the transient parent of the popup."
;
130
mTransientParent
=
mTopPopup
;
131
}
132
mTopPopup
=
this
;
133
}
134
}
135
136
mShellSurface
=
mShellIntegration
->
createShellSurface
(
this
);
137
if
(
mShellSurface
) {
138
if
(
mTransientParent
) {
139
if
(
window
()->
type
() ==
Qt
::
ToolTip
||
window
()->
type
() ==
Qt
::
Popup
||
window
()->
type
() ==
Qt
::
Tool
)
140
mTransientParent
->
addChildPopup
(
this
);
141
}
142
143
// Set initial surface title
144
setWindowTitle
(
window
()->
title
());
145
mShellSurface
->
setIcon
(
mWindowIcon
);
146
147
// The appId is the desktop entry identifier that should follow the
148
// reverse DNS convention (see
149
// http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
150
// to xdg-shell the appId is only the name, without the .desktop suffix.
151
//
152
// If the application specifies the desktop file name use that,
153
// otherwise fall back to the executable name and prepend the
154
// reversed organization domain when available.
155
if
(!
QGuiApplication
::
desktopFileName
().
isEmpty
()) {
156
mShellSurface
->
setAppId
(
QGuiApplication
::
desktopFileName
());
157
}
else
{
158
QFileInfo
fi
=
QFileInfo
(
QCoreApplication
::
instance
()->
applicationFilePath
());
159
QStringList
domainName
=
160
QCoreApplication
::
instance
()->
organizationDomain
().
split
(
QLatin1Char
(
'.'
),
161
Qt
::
SkipEmptyParts
);
162
163
if
(
domainName
.
isEmpty
()) {
164
mShellSurface
->
setAppId
(
fi
.
baseName
());
165
}
else
{
166
QString
appId
;
167
for
(
int
i
= 0;
i
<
domainName
.
size
(); ++
i
)
168
appId
.
prepend
(
QLatin1Char
(
'.'
)).
prepend
(
domainName
.
at
(
i
));
169
appId
.
append
(
fi
.
baseName
());
170
mShellSurface
->
setAppId
(
appId
);
171
}
172
}
173
// the user may have already set some window properties, so make sure to send them out
174
for
(
auto
it
=
m_properties
.
cbegin
();
it
!=
m_properties
.
cend
(); ++
it
)
175
mShellSurface
->
sendProperty
(
it
.
key
(),
it
.
value
());
176
177
emit
surfaceRoleCreated
();
178
}
else
{
179
qWarning
(
"Could not create a shell surface object."
);
180
}
181
}
182
183
createDecoration
();
184
updateInputRegion
();
185
186
// Enable high-dpi rendering. Scale() returns the screen scale factor and will
187
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
188
// to inform the compositor that high-resolution buffers will be provided.
189
if
(
mViewport
)
190
updateViewport
();
191
else
if
(
mSurface
->
version
() >= 3)
192
mSurface
->
set_buffer_scale
(
std
::
ceil
(
scale
()));
193
194
QRect
geometry
=
windowGeometry
();
195
QRect
defaultGeometry
=
this
->
defaultGeometry
();
196
if
(
geometry
.
width
() <= 0)
197
geometry
.
setWidth
(
defaultGeometry
.
width
());
198
if
(
geometry
.
height
() <= 0)
199
geometry
.
setHeight
(
defaultGeometry
.
height
());
200
201
setGeometry_helper
(
geometry
);
202
setMask
(
window
()->
mask
());
203
if
(
mShellSurface
) {
204
mShellSurface
->
requestWindowStates
(
window
()->
windowStates
());
205
mShellSurface
->
setWindowFlags
(
mFlags
);
206
}
207
handleContentOrientationChange
(
window
()->
contentOrientation
());
208
209
if
(
mShellSurface
&&
mShellSurface
->
commitSurfaceRole
())
210
mSurface
->
commit
();
211
}
212
213
void
QWaylandWindow
::
setPendingImageDescription
()
214
{
215
mColorManagementSurface
->
setImageDescription
(
mPendingImageDescription
.
get
());
216
}
217
218
void
QWaylandWindow
::
initializeWlSurface
(
bool
colorSpace
)
219
{
220
Q_ASSERT
(!
mSurface
);
221
{
222
QWriteLocker
lock
(&
mSurfaceLock
);
223
mSurface
.
reset
(
new
QWaylandSurface
(
mDisplay
));
224
connect
(
mSurface
.
data
(), &
QWaylandSurface
::
screensChanged
,
225
this
, &
QWaylandWindow
::
handleScreensChanged
);
226
connect
(
mSurface
.
data
(), &
QWaylandSurface
::
preferredBufferScaleChanged
,
227
this
, &
QWaylandWindow
::
updateScale
);
228
connect
(
mSurface
.
data
(), &
QWaylandSurface
::
preferredBufferTransformChanged
,
229
this
, &
QWaylandWindow
::
updateBufferTransform
);
230
mSurface
->
m_window
=
this
;
231
}
232
emit
wlSurfaceCreated
();
233
234
if
(
mDisplay
->
fractionalScaleManager
() &&
qApp
->
highDpiScaleFactorRoundingPolicy
() ==
Qt
::
HighDpiScaleFactorRoundingPolicy
::
PassThrough
) {
235
mFractionalScale
.
reset
(
new
QWaylandFractionalScale
(
mDisplay
->
fractionalScaleManager
()->
get_fractional_scale
(
mSurface
->
object
())));
236
237
connect
(
mFractionalScale
.
data
(), &
QWaylandFractionalScale
::
preferredScaleChanged
,
238
this
, &
QWaylandWindow
::
updateScale
);
239
}
240
// The fractional scale manager check is needed to work around Gnome < 36 where viewports don't work
241
// Right now viewports are only necessary when a fractional scale manager is used
242
if
(
display
()->
viewporter
() &&
display
()->
fractionalScaleManager
()) {
243
mViewport
.
reset
(
new
QWaylandViewport
(
display
()->
createViewport
(
this
)));
244
}
245
246
if
(
colorSpace
) {
247
initializeColorSpace
();
248
}
249
}
250
251
void
QWaylandWindow
::
initializeColorSpace
()
252
{
253
QColorSpace
requestedColorSpace
=
window
()->
requestedFormat
().
colorSpace
();
254
if
(
requestedColorSpace
!=
QColorSpace
{} &&
mDisplay
->
colorManager
()) {
255
// TODO try a similar (same primaries + supported transfer function) color space if this fails?
256
mPendingImageDescription
=
mDisplay
->
colorManager
()->
createImageDescription
(
requestedColorSpace
);
257
if
(
mPendingImageDescription
) {
258
if
(!
mColorManagementSurface
)
259
mColorManagementSurface
=
std
::
make_unique
<
ColorManagementSurface
>(
mDisplay
->
colorManager
()->
get_surface
(
surface
()));
260
connect
(
mPendingImageDescription
.
get
(), &
ImageDescription
::
ready
,
this
, &
QWaylandWindow
::
setPendingImageDescription
,
Qt
::
SingleShotConnection
);
261
mSurfaceFormat
.
setColorSpace
(
requestedColorSpace
);
262
}
else
{
263
qCWarning
(
lcQpaWayland
) <<
"couldn't create image description for requested color space"
<<
requestedColorSpace
;
264
}
265
}
266
}
267
268
void
QWaylandWindow
::
setFormat
(
const
QSurfaceFormat
&
format
)
269
{
270
const
auto
colorSpace
=
mSurfaceFormat
.
colorSpace
();
271
mSurfaceFormat
=
format
;
272
mSurfaceFormat
.
setColorSpace
(
colorSpace
);
273
}
274
275
void
QWaylandWindow
::
setShellIntegration
(
QWaylandShellIntegration
*
shellIntegration
)
276
{
277
Q_ASSERT
(
shellIntegration
);
278
if
(
mShellSurface
) {
279
qCWarning
(
lcQpaWayland
) <<
"Cannot set shell integration while there's already a shell surface created"
;
280
return
;
281
}
282
mShellIntegration
=
shellIntegration
;
283
}
284
285
bool
QWaylandWindow
::
shouldCreateShellSurface
()
const
286
{
287
if
(!
shellIntegration
())
288
return
false
;
289
290
if
(
shouldCreateSubSurface
())
291
return
false
;
292
293
if
(
window
()->
inherits
(
"QShapedPixmapWindow"
))
294
return
false
;
295
296
if
(
qEnvironmentVariableIsSet
(
"QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"
))
297
return
!(
window
()->
flags
() &
Qt
::
BypassWindowManagerHint
);
298
299
return
true
;
300
}
301
302
bool
QWaylandWindow
::
shouldCreateSubSurface
()
const
303
{
304
return
QPlatformWindow
::
parent
() !=
nullptr
;
305
}
306
307
void
QWaylandWindow
::
beginFrame
()
308
{
309
mSurfaceLock
.
lockForRead
();
310
mInFrameRender
=
true
;
311
}
312
313
void
QWaylandWindow
::
endFrame
()
314
{
315
mSurfaceLock
.
unlock
();
316
mInFrameRender
=
false
;
317
}
318
319
void
QWaylandWindow
::
reset
()
320
{
321
resetSurfaceRole
();
322
323
if
(
mSurface
) {
324
{
325
QWriteLocker
lock
(&
mSurfaceLock
);
326
invalidateSurface
();
327
mSurface
.
reset
();
328
mViewport
.
reset
();
329
mFractionalScale
.
reset
();
330
mColorManagementSurface
.
reset
();
331
mPendingImageDescription
.
reset
();
332
}
333
emit
wlSurfaceDestroyed
();
334
}
335
336
337
mScale
=
std
::
nullopt
;
338
mOpaqueArea
=
QRegion
();
339
mMask
=
QRegion
();
340
341
mInputRegion
=
QRegion
();
342
mTransparentInputRegion
=
false
;
343
344
mDisplay
->
handleWindowDestroyed
(
this
);
345
}
346
347
void
QWaylandWindow
::
resetSurfaceRole
()
348
{
349
// Old Reset
350
closeChildPopups
();
351
352
if
(
mTopPopup
==
this
)
353
mTopPopup
=
mTransientParent
&& (
mTransientParent
->
window
()->
type
() ==
Qt
::
Popup
) ?
mTransientParent
:
nullptr
;
354
if
(
mTransientParent
)
355
mTransientParent
->
removeChildPopup
(
this
);
356
mTransientParent
=
nullptr
;
357
delete
std
::
exchange
(
mShellSurface
,
nullptr
);
358
delete
std
::
exchange
(
mSubSurfaceWindow
,
nullptr
);
359
emit
surfaceRoleDestroyed
();
360
361
resetFrameCallback
();
362
mInFrameRender
=
false
;
363
mWaitingToApplyConfigure
=
false
;
364
mExposed
=
false
;
365
}
366
367
void
QWaylandWindow
::
resetFrameCallback
()
368
{
369
{
370
QMutexLocker
lock
(&
mFrameSyncMutex
);
371
if
(
mFrameCallback
) {
372
wl_callback_destroy
(
mFrameCallback
);
373
mFrameCallback
=
nullptr
;
374
}
375
mFrameCallbackElapsedTimer
.
invalidate
();
376
mWaitingForFrameCallback
=
false
;
377
}
378
if
(
mFrameCallbackCheckIntervalTimerId
!= -1) {
379
killTimer
(
mFrameCallbackCheckIntervalTimerId
);
380
mFrameCallbackCheckIntervalTimerId
= -1;
381
}
382
mFrameCallbackTimedOut
=
false
;
383
}
384
385
QWaylandWindow
*
QWaylandWindow
::
fromWlSurface
(::
wl_surface
*
surface
)
386
{
387
if
(
auto
*
s
=
QWaylandSurface
::
fromWlSurface
(
surface
))
388
return
s
->
m_window
;
389
return
nullptr
;
390
}
391
392
WId
QWaylandWindow
::
winId
()
const
393
{
394
return
reinterpret_cast
<
WId
>(
wlSurface
());
395
}
396
397
void
QWaylandWindow
::
setParent
(
const
QPlatformWindow
*
parent
)
398
{
399
if
(
lastParent
==
parent
)
400
return
;
401
402
if
(
mSubSurfaceWindow
&&
parent
) {
// new parent, but we were a subsurface already
403
delete
mSubSurfaceWindow
;
404
QWaylandWindow
*
p
=
const_cast
<
QWaylandWindow
*>(
static_cast
<
const
QWaylandWindow
*>(
parent
));
405
mSubSurfaceWindow
=
new
QWaylandSubSurface
(
this
,
p
,
mDisplay
->
createSubSurface
(
this
,
p
));
406
}
else
if
((!
lastParent
&&
parent
) || (
lastParent
&& !
parent
)) {
407
// we're changing role, need to make a new wl_surface
408
reset
();
409
initializeWlSurface
();
410
if
(
window
()->
isVisible
()) {
411
initWindow
();
412
}
413
}
414
lastParent
=
parent
;
415
}
416
417
QString
QWaylandWindow
::
windowTitle
()
const
418
{
419
return
mWindowTitle
;
420
}
421
422
void
QWaylandWindow
::
setWindowTitle
(
const
QString
&
title
)
423
{
424
const
QString
separator
=
QString
::
fromUtf8
(
" \xe2\x80\x94 "
);
// unicode character U+2014, EM DASH
425
const
QString
formatted
=
formatWindowTitle
(
title
,
separator
);
426
427
const
int
libwaylandMaxBufferSize
= 4096;
428
// Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
429
// Also, QString is in utf-16, which means that in the worst case each character will be
430
// three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
431
const
int
maxLength
=
libwaylandMaxBufferSize
/ 3 - 100;
432
433
auto
truncated
=
QStringView
{
formatted
}.
left
(
maxLength
);
434
if
(
truncated
.
size
() <
formatted
.
size
()) {
435
qCWarning
(
lcQpaWayland
) <<
"Window titles longer than"
<<
maxLength
<<
"characters are not supported."
436
<<
"Truncating window title (from"
<<
formatted
.
size
() <<
"chars)"
;
437
}
438
439
mWindowTitle
=
truncated
.
toString
();
440
441
if
(
mShellSurface
)
442
mShellSurface
->
setTitle
(
mWindowTitle
);
443
444
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
445
mWindowDecoration
->
update
();
446
}
447
448
void
QWaylandWindow
::
setWindowIcon
(
const
QIcon
&
icon
)
449
{
450
mWindowIcon
=
icon
;
451
452
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
453
mWindowDecoration
->
update
();
454
if
(
mShellSurface
)
455
mShellSurface
->
setIcon
(
icon
);
456
}
457
458
QRect
QWaylandWindow
::
defaultGeometry
()
const
459
{
460
return
QRect
(
QPoint
(),
QSize
(500,500));
461
}
462
463
void
QWaylandWindow
::
setGeometry_helper
(
const
QRect
&
rect
)
464
{
465
QPlatformWindow
::
setGeometry
(
rect
);
466
if
(
mViewport
)
467
updateViewport
();
468
469
if
(
mSubSurfaceWindow
) {
470
QMargins
m
=
static_cast
<
QWaylandWindow
*>(
QPlatformWindow
::
parent
())->
clientSideMargins
();
471
mSubSurfaceWindow
->
set_position
(
rect
.
x
() +
m
.
left
(),
rect
.
y
() +
m
.
top
());
472
473
QWaylandWindow
*
parentWindow
=
mSubSurfaceWindow
->
parent
();
474
if
(
parentWindow
&&
parentWindow
->
isExposed
()) {
475
QRect
parentExposeGeometry
(
QPoint
(),
parentWindow
->
geometry
().
size
());
476
parentWindow
->
sendExposeEvent
(
parentExposeGeometry
);
477
}
478
}
479
}
480
481
void
QWaylandWindow
::
setGeometry
(
const
QRect
&
r
)
482
{
483
auto
rect
=
r
;
484
if
(
fixedToplevelPositions
&& !
QPlatformWindow
::
parent
() &&
window
()->
type
() !=
Qt
::
Popup
485
&&
window
()->
type
() !=
Qt
::
ToolTip
&&
window
()->
type
() !=
Qt
::
Tool
) {
486
rect
.
moveTo
(
screen
()->
geometry
().
topLeft
());
487
}
488
setGeometry_helper
(
rect
);
489
490
if
(
mShellSurface
&& !
mInResizeFromApplyConfigure
) {
491
const
QRect
frameGeometry
=
r
.
marginsAdded
(
clientSideMargins
()).
marginsRemoved
(
windowContentMargins
());
492
if
(
qt_window_private
(
window
())->
positionAutomatic
||
m_popupInfo
.
parentControlGeometry
.
isValid
())
493
mShellSurface
->
setWindowSize
(
frameGeometry
.
size
());
494
495
else
496
mShellSurface
->
setWindowGeometry
(
frameGeometry
);
497
}
498
499
if
(
mShellSurface
)
500
mShellSurface
->
setContentGeometry
(
windowContentGeometry
());
501
502
if
(
isOpaque
() &&
mMask
.
isEmpty
())
503
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
rect
.
size
()));
504
505
506
if
(
window
()->
isVisible
() &&
rect
.
isValid
()) {
507
ensureSize
();
508
if
(
mWindowDecorationEnabled
)
509
mWindowDecoration
->
update
();
510
511
QWindowSystemInterface
::
handleGeometryChange
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
geometry
());
512
mSentInitialResize
=
true
;
513
}
514
515
// 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
516
// On other platforms (X11) the expose event would be received deferred from the X server
517
// we want our behaviour to match, and only run after control has returned to the event loop
518
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
synthesizeExposeOnGeometryChange
,
Qt
::
QueuedConnection
);
519
}
520
521
void
QWaylandWindow
::
synthesizeExposeOnGeometryChange
()
522
{
523
if
(!
isExposed
())
524
return
;
525
QRect
exposeGeometry
(
QPoint
(),
geometry
().
size
());
526
if
(
exposeGeometry
==
mLastExposeGeometry
)
527
return
;
528
529
sendExposeEvent
(
exposeGeometry
);
530
}
531
532
void
QWaylandWindow
::
updateInputRegion
()
533
{
534
if
(!
mSurface
)
535
return
;
536
537
const
bool
transparentInputRegion
=
mFlags
.
testFlag
(
Qt
::
WindowTransparentForInput
);
538
539
QRegion
inputRegion
;
540
if
(!
transparentInputRegion
)
541
inputRegion
=
mMask
;
542
543
if
(
mInputRegion
==
inputRegion
&&
mTransparentInputRegion
==
transparentInputRegion
)
544
return
;
545
546
mInputRegion
=
inputRegion
;
547
mTransparentInputRegion
=
transparentInputRegion
;
548
549
if
(
mInputRegion
.
isEmpty
() && !
mTransparentInputRegion
) {
550
mSurface
->
set_input_region
(
nullptr
);
551
}
else
{
552
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
mInputRegion
);
553
mSurface
->
set_input_region
(
region
);
554
wl_region_destroy
(
region
);
555
}
556
}
557
558
void
QWaylandWindow
::
updateViewport
()
559
{
560
if
(!
surfaceSize
().
isEmpty
())
561
mViewport
->
setDestination
(
surfaceSize
());
562
}
563
564
void
QWaylandWindow
::
setGeometryFromApplyConfigure
(
const
QPoint
&
globalPosition
,
const
QSize
&
sizeWithMargins
)
565
{
566
QMargins
margins
=
clientSideMargins
();
567
568
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
569
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
570
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
571
572
QRect
geometry
(
positionWithoutMargins
,
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
573
574
mInResizeFromApplyConfigure
=
true
;
575
setGeometry
(
geometry
);
576
mInResizeFromApplyConfigure
=
false
;
577
}
578
579
void
QWaylandWindow
::
repositionFromApplyConfigure
(
const
QPoint
&
globalPosition
)
580
{
581
QMargins
margins
=
clientSideMargins
();
582
QPoint
positionWithoutMargins
=
globalPosition
+
QPoint
(
margins
.
left
(),
margins
.
top
());
583
584
QRect
geometry
(
positionWithoutMargins
,
windowGeometry
().
size
());
585
mInResizeFromApplyConfigure
=
true
;
586
setGeometry
(
geometry
);
587
mInResizeFromApplyConfigure
=
false
;
588
}
589
590
void
QWaylandWindow
::
resizeFromApplyConfigure
(
const
QSize
&
sizeWithMargins
,
const
QPoint
&
offset
)
591
{
592
QMargins
margins
=
clientSideMargins
();
593
int
widthWithoutMargins
=
qMax
(
sizeWithMargins
.
width
() - (
margins
.
left
() +
margins
.
right
()), 1);
594
int
heightWithoutMargins
=
qMax
(
sizeWithMargins
.
height
() - (
margins
.
top
() +
margins
.
bottom
()), 1);
595
QRect
geometry
(
windowGeometry
().
topLeft
(),
QSize
(
widthWithoutMargins
,
heightWithoutMargins
));
596
597
mOffset
+=
offset
;
598
mInResizeFromApplyConfigure
=
true
;
599
setGeometry
(
geometry
);
600
mInResizeFromApplyConfigure
=
false
;
601
}
602
603
void
QWaylandWindow
::
sendExposeEvent
(
const
QRect
&
rect
)
604
{
605
static
bool
sQtTestMode
=
qEnvironmentVariableIsSet
(
"QT_QTESTLIB_RUNNING"
);
606
mLastExposeGeometry
=
rect
;
607
608
if
(
sQtTestMode
) {
609
mExposeEventNeedsAttachedBuffer
=
true
;
610
}
611
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
rect
);
612
613
/**
614
* If an expose is not handled by application code, explicitly attach a buffer
615
* This primarily is a workaround for Qt unit tests using QWindow directly and
616
* wanting focus.
617
*/
618
if
(
mExposed
&&
mExposeEventNeedsAttachedBuffer
&& !
rect
.
isNull
()) {
619
auto
buffer
=
new
QWaylandShmBuffer
(
mDisplay
,
rect
.
size
(),
QImage
::
Format_ARGB32
);
620
buffer
->
image
()->
fill
(
Qt
::
transparent
);
621
buffer
->
setDeleteOnRelease
(
true
);
622
commit
(
buffer
,
QRegion
());
623
}
624
}
625
626
QPlatformScreen
*
QWaylandWindow
::
calculateScreenFromSurfaceEvents
()
const
627
{
628
QReadLocker
lock
(&
mSurfaceLock
);
629
if
(
mSurface
) {
630
if
(
auto
*
screen
=
mSurface
->
oldestEnteredScreen
())
631
return
screen
;
632
}
633
return
QPlatformWindow
::
screen
();
634
}
635
636
void
QWaylandWindow
::
setVisible
(
bool
visible
)
637
{
638
// Workaround for issue where setVisible may be called with the same value twice
639
if
(
lastVisible
==
visible
)
640
return
;
641
lastVisible
=
visible
;
642
643
if
(
visible
) {
644
setGeometry
(
windowGeometry
());
645
initWindow
();
646
updateExposure
();
647
if
(
mShellSurface
)
648
mShellSurface
->
requestActivateOnShow
();
649
}
else
{
650
// make sure isExposed is false during the next event dispatch
651
mExposed
=
false
;
652
sendExposeEvent
(
QRect
());
653
resetSurfaceRole
();
654
mSurface
->
attach
(
nullptr
, 0, 0);
655
mSurface
->
commit
();
656
}
657
}
658
659
660
void
QWaylandWindow
::
raise
()
661
{
662
if
(
mShellSurface
)
663
mShellSurface
->
raise
();
664
}
665
666
667
void
QWaylandWindow
::
lower
()
668
{
669
if
(
mShellSurface
)
670
mShellSurface
->
lower
();
671
}
672
673
void
QWaylandWindow
::
setMask
(
const
QRegion
&
mask
)
674
{
675
QReadLocker
locker
(&
mSurfaceLock
);
676
if
(!
mSurface
)
677
return
;
678
679
if
(
mMask
==
mask
)
680
return
;
681
682
mMask
=
mask
;
683
684
updateInputRegion
();
685
686
if
(
isOpaque
()) {
687
if
(
mMask
.
isEmpty
())
688
setOpaqueArea
(
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
689
else
690
setOpaqueArea
(
mMask
);
691
}
692
}
693
694
void
QWaylandWindow
::
setAlertState
(
bool
enabled
)
695
{
696
if
(
mShellSurface
)
697
mShellSurface
->
setAlertState
(
enabled
);
698
}
699
700
bool
QWaylandWindow
::
isAlertState
()
const
701
{
702
if
(
mShellSurface
)
703
return
mShellSurface
->
isAlertState
();
704
705
return
false
;
706
}
707
708
void
QWaylandWindow
::
applyConfigureWhenPossible
()
709
{
710
if
(!
mWaitingToApplyConfigure
) {
711
mWaitingToApplyConfigure
=
true
;
712
QMetaObject
::
invokeMethod
(
this
, &
QWaylandWindow
::
applyConfigure
,
Qt
::
QueuedConnection
);
713
}
714
}
715
716
void
QWaylandWindow
::
applyConfigure
()
717
{
718
if
(!
mWaitingToApplyConfigure
)
719
return
;
720
721
Q_ASSERT_X
(
QThread
::
isMainThread
(),
722
"QWaylandWindow::applyConfigure"
,
"not called from main thread"
);
723
724
// If we're mid paint, use an exposeEvent to flush the current frame.
725
// When this completes we know that no other frames will be rendering.
726
// This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
727
if
(
mInFrameRender
)
728
QWindowSystemInterface
::
handleExposeEvent
<
QWindowSystemInterface
::
SynchronousDelivery
>(
window
(),
QRect
(
QPoint
(0, 0),
geometry
().
size
()));
729
if
(
mShellSurface
)
730
mShellSurface
->
applyConfigure
();
731
732
mWaitingToApplyConfigure
=
false
;
733
if
(
mExposed
)
734
sendExposeEvent
(
QRect
(
QPoint
(),
geometry
().
size
()));
735
else
736
// we still need to commit the configured ack for a hidden surface
737
commit
();
738
}
739
740
void
QWaylandWindow
::
attach
(
QWaylandBuffer
*
buffer
,
int
x
,
int
y
)
741
{
742
QReadLocker
locker
(&
mSurfaceLock
);
743
if
(
mSurface
==
nullptr
)
744
return
;
745
746
if
(
buffer
) {
747
Q_ASSERT
(!
buffer
->
committed
());
748
handleUpdate
();
749
buffer
->
setBusy
(
true
);
750
if
(
mSurface
->
version
() >=
WL_SURFACE_OFFSET_SINCE_VERSION
) {
751
mSurface
->
offset
(
x
,
y
);
752
mSurface
->
attach
(
buffer
->
buffer
(), 0, 0);
753
}
else
{
754
mSurface
->
attach
(
buffer
->
buffer
(),
x
,
y
);
755
}
756
}
else
{
757
mSurface
->
attach
(
nullptr
, 0, 0);
758
}
759
}
760
761
void
QWaylandWindow
::
attachOffset
(
QWaylandBuffer
*
buffer
)
762
{
763
attach
(
buffer
,
mOffset
.
x
(),
mOffset
.
y
());
764
mOffset
=
QPoint
();
765
}
766
767
void
QWaylandWindow
::
damage
(
const
QRect
&
rect
)
768
{
769
QReadLocker
locker
(&
mSurfaceLock
);
770
if
(
mSurface
==
nullptr
)
771
return
;
772
773
const
qreal
s
=
scale
();
774
if
(
mSurface
->
version
() >= 4) {
775
const
QRect
bufferRect
=
776
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
777
.
toAlignedRect
();
778
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
779
bufferRect
.
height
());
780
}
else
{
781
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
782
}
783
}
784
785
void
QWaylandWindow
::
safeCommit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
786
{
787
if
(
isExposed
()) {
788
commit
(
buffer
,
damage
);
789
}
else
{
790
buffer
->
setBusy
(
false
);
791
}
792
}
793
794
bool
QWaylandWindow
::
allowsIndependentThreadedRendering
()
const
795
{
796
return
!
mWaitingToApplyConfigure
;
797
}
798
799
void
QWaylandWindow
::
commit
(
QWaylandBuffer
*
buffer
,
const
QRegion
&
damage
)
800
{
801
Q_ASSERT
(
isExposed
());
802
if
(
buffer
->
committed
()) {
803
mSurface
->
commit
();
804
qCDebug
(
lcWaylandBackingstore
) <<
"Buffer already committed, not attaching."
;
805
return
;
806
}
807
808
QReadLocker
locker
(&
mSurfaceLock
);
809
if
(!
mSurface
)
810
return
;
811
812
attachOffset
(
buffer
);
813
if
(
mSurface
->
version
() >= 4) {
814
const
qreal
s
=
scale
();
815
for
(
const
QRect
&
rect
:
damage
) {
816
const
QRect
bufferRect
=
817
QRectF
(
s
*
rect
.
x
(),
s
*
rect
.
y
(),
s
*
rect
.
width
(),
s
*
rect
.
height
())
818
.
toAlignedRect
();
819
mSurface
->
damage_buffer
(
bufferRect
.
x
(),
bufferRect
.
y
(),
bufferRect
.
width
(),
820
bufferRect
.
height
());
821
}
822
}
else
{
823
for
(
const
QRect
&
rect
:
damage
)
824
mSurface
->
damage
(
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
825
}
826
Q_ASSERT
(!
buffer
->
committed
());
827
buffer
->
setCommitted
();
828
mSurface
->
commit
();
829
}
830
831
void
QWaylandWindow
::
commit
()
832
{
833
QReadLocker
locker
(&
mSurfaceLock
);
834
if
(
mSurface
!=
nullptr
)
835
mSurface
->
commit
();
836
}
837
838
const
wl_callback_listener
QWaylandWindow
::
callbackListener
= {
839
[](
void
*
data
,
wl_callback
*
callback
,
uint32_t
time
) {
840
Q_UNUSED
(
time
);
841
auto
*
window
=
static_cast
<
QWaylandWindow
*>(
data
);
842
window
->
handleFrameCallback
(
callback
);
843
}
844
};
845
846
void
QWaylandWindow
::
handleFrameCallback
(
wl_callback
*
callback
)
847
{
848
QMutexLocker
locker
(&
mFrameSyncMutex
);
849
if
(!
mFrameCallback
) {
850
// This means the callback is already unset by QWaylandWindow::reset.
851
// The wl_callback object will be destroyed there too.
852
return
;
853
}
854
Q_ASSERT
(
callback
==
mFrameCallback
);
855
wl_callback_destroy
(
callback
);
856
mFrameCallback
=
nullptr
;
857
858
mWaitingForFrameCallback
=
false
;
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
(
mWaitingForFrameCallback
&&
mFrameSyncWait
.
wait
(&
mFrameSyncMutex
,
deadline
)) { }
888
889
if
(
mWaitingForFrameCallback
) {
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
!
mWaitingForFrameCallback
;
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
(
mWaitingForFrameCallback
)
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
(
mWaitingForFrameCallback
)
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
(
mWaitingForFrameCallback
)
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
mWaitingForFrameCallback
=
true
;
1805
1806
// Start a timer for handling the case when the compositor stops sending frame callbacks.
1807
if
(
mFrameCallbackTimeout
> 0) {
1808
QMetaObject
::
invokeMethod
(
this
, [
this
] {
1809
QMutexLocker
locker
(&
mFrameSyncMutex
);
1810
1811
if
(
mWaitingForFrameCallback
) {
1812
if
(
mFrameCallbackCheckIntervalTimerId
< 0)
1813
mFrameCallbackCheckIntervalTimerId
=
startTimer
(
mFrameCallbackTimeout
);
1814
mFrameCallbackElapsedTimer
.
start
();
1815
}
1816
},
Qt
::
QueuedConnection
);
1817
}
1818
}
1819
1820
void
QWaylandWindow
::
deliverUpdateRequest
()
1821
{
1822
qCDebug
(
lcWaylandBackingstore
) <<
"deliverUpdateRequest"
;
1823
QPlatformWindow
::
deliverUpdateRequest
();
1824
}
1825
1826
void
QWaylandWindow
::
addAttachOffset
(
const
QPoint
point
)
1827
{
1828
mOffset
+=
point
;
1829
}
1830
1831
void
QWaylandWindow
::
propagateSizeHints
()
1832
{
1833
if
(
mShellSurface
)
1834
mShellSurface
->
propagateSizeHints
();
1835
}
1836
1837
bool
QWaylandWindow
::
startSystemResize
(
Qt
::
Edges
edges
)
1838
{
1839
if
(
auto
*
seat
=
display
()->
lastInputDevice
()) {
1840
bool
rc
=
mShellSurface
&&
mShellSurface
->
resize
(
seat
,
edges
);
1841
seat
->
handleEndDrag
();
1842
return
rc
;
1843
}
1844
return
false
;
1845
}
1846
1847
bool
QtWaylandClient
::
QWaylandWindow
::
startSystemMove
()
1848
{
1849
if
(
auto
seat
=
display
()->
lastInputDevice
()) {
1850
bool
rc
=
mShellSurface
&&
mShellSurface
->
move
(
seat
);
1851
seat
->
handleEndDrag
();
1852
return
rc
;
1853
}
1854
return
false
;
1855
}
1856
1857
bool
QWaylandWindow
::
isOpaque
()
const
1858
{
1859
return
window
()->
requestedFormat
().
alphaBufferSize
() <= 0;
1860
}
1861
1862
void
QWaylandWindow
::
setOpaqueArea
(
const
QRegion
&
opaqueArea
)
1863
{
1864
const
QRegion
translatedOpaqueArea
=
opaqueArea
.
translated
(
clientSideMargins
().
left
(),
clientSideMargins
().
top
());
1865
1866
if
(
translatedOpaqueArea
==
mOpaqueArea
|| !
mSurface
)
1867
return
;
1868
1869
mOpaqueArea
=
translatedOpaqueArea
;
1870
1871
struct
::
wl_region
*
region
=
mDisplay
->
createRegion
(
translatedOpaqueArea
);
1872
mSurface
->
set_opaque_region
(
region
);
1873
wl_region_destroy
(
region
);
1874
}
1875
1876
void
QWaylandWindow
::
requestXdgActivationToken
(
uint
serial
)
1877
{
1878
if
(!
mShellSurface
) {
1879
qCWarning
(
lcQpaWayland
) <<
"requestXdgActivationToken is called with no surface role created, emitting synthetic signal"
;
1880
Q_EMIT
xdgActivationTokenCreated
({});
1881
return
;
1882
}
1883
mShellSurface
->
requestXdgActivationToken
(
serial
);
1884
}
1885
1886
void
QWaylandWindow
::
setXdgActivationToken
(
const
QString
&
token
)
1887
{
1888
if
(
mShellSurface
)
1889
mShellSurface
->
setXdgActivationToken
(
token
);
1890
else
1891
qCWarning
(
lcQpaWayland
) <<
"setXdgActivationToken is called with no surface role created, token"
<<
token
<<
"discarded"
;
1892
}
1893
1894
void
QWaylandWindow
::
addChildPopup
(
QWaylandWindow
*
child
)
1895
{
1896
if
(
mShellSurface
)
1897
mShellSurface
->
attachPopup
(
child
->
shellSurface
());
1898
mChildPopups
.
append
(
child
);
1899
}
1900
1901
void
QWaylandWindow
::
removeChildPopup
(
QWaylandWindow
*
child
)
1902
{
1903
if
(
mShellSurface
)
1904
mShellSurface
->
detachPopup
(
child
->
shellSurface
());
1905
mChildPopups
.
removeAll
(
child
);
1906
}
1907
1908
void
QWaylandWindow
::
closeChildPopups
() {
1909
while
(!
mChildPopups
.
isEmpty
()) {
1910
auto
popup
=
mChildPopups
.
takeLast
();
1911
popup
->
resetSurfaceRole
();
1912
}
1913
}
1914
1915
void
QWaylandWindow
::
reinit
()
1916
{
1917
if
(
window
()->
isVisible
()) {
1918
initWindow
();
1919
if
(
hasPendingUpdateRequest
())
1920
deliverUpdateRequest
();
1921
}
1922
}
1923
1924
bool
QWaylandWindow
::
windowEvent
(
QEvent
*
event
)
1925
{
1926
if
(
event
->
type
() ==
QEvent
::
ApplicationPaletteChange
1927
||
event
->
type
() ==
QEvent
::
ApplicationFontChange
) {
1928
if
(
mWindowDecorationEnabled
&&
window
()->
isVisible
())
1929
mWindowDecoration
->
update
();
1930
}
else
if
(
event
->
type
() ==
QEvent
::
WindowUnblocked
) {
1931
// QtGui sends leave event to window under cursor when modal window opens, so we have
1932
// to send enter event when modal closes and window has cursor and gets unblocked.
1933
if
(
auto
*
inputDevice
=
mDisplay
->
lastInputDevice
();
inputDevice
&&
inputDevice
->
pointerFocus
() ==
this
) {
1934
const
auto
pos
=
mDisplay
->
waylandCursor
()->
pos
();
1935
QWindowSystemInterface
::
handleEnterEvent
(
window
(),
mapFromGlobalF
(
pos
),
pos
);
1936
}
1937
}
1938
1939
return
QPlatformWindow
::
windowEvent
(
event
);
1940
}
1941
1942
QSurfaceFormat
QWaylandWindow
::
format
()
const
1943
{
1944
return
mSurfaceFormat
;
1945
}
1946
1947
void
QWaylandWindow
::
setSessionRestoreId
(
const
QString
&
role
)
1948
{
1949
mSessionRestoreId
=
role
;
1950
}
1951
1952
QString
QWaylandWindow
::
sessionRestoreId
()
const
1953
{
1954
return
mSessionRestoreId
;
1955
}
1956
1957
void
QWaylandWindow
::
setExtendedWindowType
(
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
windowType
) {
1958
m_popupInfo
.
extendedWindowType
=
windowType
;
1959
}
1960
1961
QNativeInterface
::
Private
::
QWaylandWindow
::
WindowType
QWaylandWindow
::
extendedWindowType
()
const
1962
{
1963
return
m_popupInfo
.
extendedWindowType
;
1964
}
1965
1966
void
QWaylandWindow
::
setParentControlGeometry
(
const
QRect
&
parentControlGeometry
) {
1967
m_popupInfo
.
parentControlGeometry
=
parentControlGeometry
;
1968
if
(
mExposed
) {
1969
mShellSurface
->
setWindowPosition
(
window
()->
position
());
1970
}
1971
}
1972
1973
QRect
QWaylandWindow
::
parentControlGeometry
()
const
1974
{
1975
return
m_popupInfo
.
parentControlGeometry
;
1976
}
1977
1978
}
1979
1980
QT_END_NAMESPACE
1981
1982
#
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.14.0