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