summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--.qmake.conf2
-rw-r--r--config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro1
-rw-r--r--config.tests/dmabuf_client_buffer/main.cpp71
-rw-r--r--config.tests/wayland/main.cpp10
-rw-r--r--examples/wayland/minimal-cpp/compositor.h4
-rw-r--r--examples/wayland/minimal-cpp/window.cpp2
-rw-r--r--examples/wayland/pure-qml/qml/main.qml2
-rw-r--r--examples/wayland/qwindow-compositor/compositor.cpp4
-rw-r--r--examples/wayland/qwindow-compositor/compositor.h2
-rw-r--r--examples/wayland/qwindow-compositor/window.cpp2
-rw-r--r--features/wayland-scanner-client-wayland-protocol-include.prf53
-rw-r--r--src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml245
-rw-r--r--src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml348
-rw-r--r--src/3rdparty/protocol/qt_attribution.json82
-rw-r--r--src/3rdparty/protocol/scaler.xml208
-rw-r--r--src/3rdparty/protocol/viewporter.xml186
-rw-r--r--src/3rdparty/protocol/wayland.xml1615
-rw-r--r--src/client/client.pro14
-rw-r--r--src/client/configure.json41
-rw-r--r--src/client/hardwareintegration/qwaylandserverbufferintegration_p.h2
-rw-r--r--src/client/qwaylandabstractdecoration.cpp36
-rw-r--r--src/client/qwaylandabstractdecoration_p.h6
-rw-r--r--src/client/qwaylandbuffer_p.h3
-rw-r--r--src/client/qwaylandcursor.cpp67
-rw-r--r--src/client/qwaylandcursor_p.h6
-rw-r--r--src/client/qwaylanddisplay.cpp109
-rw-r--r--src/client/qwaylanddisplay_p.h39
-rw-r--r--src/client/qwaylandextendedsurface_p.h1
-rw-r--r--src/client/qwaylandinputcontext.cpp15
-rw-r--r--src/client/qwaylandinputcontext_p.h3
-rw-r--r--src/client/qwaylandinputdevice.cpp644
-rw-r--r--src/client/qwaylandinputdevice_p.h118
-rw-r--r--src/client/qwaylandintegration.cpp136
-rw-r--r--src/client/qwaylandintegration_p.h4
-rw-r--r--src/client/qwaylandscreen.cpp68
-rw-r--r--src/client/qwaylandscreen_p.h9
-rw-r--r--src/client/qwaylandshellsurface_p.h10
-rw-r--r--src/client/qwaylandshmbackingstore.cpp3
-rw-r--r--src/client/qwaylandsubsurface_p.h2
-rw-r--r--src/client/qwaylandwindow.cpp80
-rw-r--r--src/client/qwaylandwindow_p.h10
-rw-r--r--src/client/qwaylandwindowmanagerintegration_p.h1
-rw-r--r--src/compositor/compositor.pro4
-rw-r--r--src/compositor/compositor_api/qwaylandclient.cpp2
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.cpp32
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor_p.h13
-rw-r--r--src/compositor/compositor_api/qwaylanddestroylistener_p.h2
-rw-r--r--src/compositor/compositor_api/qwaylandkeyboard.cpp143
-rw-r--r--src/compositor/compositor_api/qwaylandkeyboard_p.h15
-rw-r--r--src/compositor/compositor_api/qwaylandoutput.cpp5
-rw-r--r--src/compositor/compositor_api/qwaylandpointer.cpp6
-rw-r--r--src/compositor/compositor_api/qwaylandquickcompositor.cpp12
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp73
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.h4
-rw-r--r--src/compositor/compositor_api/qwaylandseat.cpp86
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.cpp159
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.h21
-rw-r--r--src/compositor/compositor_api/qwaylandsurface_p.h10
-rw-r--r--src/compositor/configure.json11
-rw-r--r--src/compositor/doc/src/qtwaylandcompositor-overview.qdoc110
-rw-r--r--src/compositor/extensions/extensions.pri8
-rw-r--r--src/compositor/extensions/pregenerated/3rdparty/.gitignore3
-rw-r--r--src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h3
-rw-r--r--src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h2
-rw-r--r--src/compositor/extensions/qwaylandiviapplication.cpp17
-rw-r--r--src/compositor/extensions/qwaylandivisurface.cpp9
-rw-r--r--src/compositor/extensions/qwaylandshellsurface.cpp2
-rw-r--r--src/compositor/extensions/qwaylandtextinput.cpp11
-rw-r--r--src/compositor/extensions/qwaylandviewporter.cpp243
-rw-r--r--src/compositor/extensions/qwaylandviewporter.h63
-rw-r--r--src/compositor/extensions/qwaylandviewporter_p.h93
-rw-r--r--src/compositor/extensions/qwaylandwlscaler.cpp274
-rw-r--r--src/compositor/extensions/qwaylandwlscaler.h67
-rw-r--r--src/compositor/extensions/qwaylandwlscaler_p.h93
-rw-r--r--src/compositor/extensions/qwaylandwlshell.cpp13
-rw-r--r--src/compositor/extensions/qwaylandwlshell_p.h2
-rw-r--r--src/compositor/extensions/qwaylandwlshellintegration.cpp12
-rw-r--r--src/compositor/extensions/qwaylandwlshellintegration_p.h4
-rw-r--r--src/compositor/extensions/qwaylandxdgdecorationv1.cpp10
-rw-r--r--src/compositor/extensions/qwaylandxdgshell.cpp46
-rw-r--r--src/compositor/extensions/qwaylandxdgshellintegration.cpp22
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv5.cpp27
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv5integration.cpp24
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv5integration_p.h4
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv6.cpp42
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv6integration.cpp22
-rw-r--r--src/compositor/extensions/qwaylandxdgshellv6integration_p.h4
-rw-r--r--src/compositor/global/global.pri1
-rw-r--r--src/compositor/global/qwaylandcompositorextension.cpp2
-rw-r--r--src/compositor/global/qwaylandutils_p.h73
-rw-r--r--src/compositor/hardware_integration/qwlclientbufferintegration_p.h2
-rw-r--r--src/compositor/wayland_wrapper/qwlclientbuffer.cpp2
-rw-r--r--src/compositor/wayland_wrapper/qwlclientbuffer_p.h4
-rw-r--r--src/compositor/wayland_wrapper/qwldatadevicemanager_p.h2
-rw-r--r--src/compositor/wayland_wrapper/qwldatasource.cpp3
-rw-r--r--src/compositor/wayland_wrapper/qwlregion.cpp6
-rw-r--r--src/compositor/wayland_wrapper/wayland_wrapper.pri10
-rw-r--r--src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp2
-rw-r--r--src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h7
-rw-r--r--src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp1
-rw-r--r--src/hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h2
-rw-r--r--src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h2
-rw-r--r--src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h2
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp10
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h2
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h2
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp203
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.h6
-rw-r--r--src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp2
-rw-r--r--src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h11
-rw-r--r--src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp2
-rw-r--r--src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h5
-rw-r--r--src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp1
-rw-r--r--src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri2
-rw-r--r--src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h3
-rw-r--r--src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h2
-rw-r--r--src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp2
-rw-r--r--src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h2
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri16
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp332
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h162
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp501
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h138
-rw-r--r--src/hardwareintegration/compositor/xcomposite-egl/xcompositeeglintegration.h2
-rw-r--r--src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h5
-rw-r--r--src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h2
-rw-r--r--src/imports/compositor/compositor.pro3
-rw-r--r--src/imports/compositor/plugins.qmltypes43
-rw-r--r--src/imports/compositor/qwaylandquickcompositorplugin.cpp10
-rw-r--r--src/plugins/decorations/bradient/main.cpp57
-rw-r--r--src/plugins/hardwareintegration/compositor/compositor.pro2
-rw-r--r--src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json3
-rw-r--r--src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro12
-rw-r--r--src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp63
-rw-r--r--src/plugins/hardwareintegration/hardwareintegration.pro2
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json3
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro23
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/main.cpp69
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp71
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h67
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp (renamed from src/shared/qwaylandxkb_p.h)40
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h67
-rw-r--r--src/plugins/shellintegration/shellintegration.pro14
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h1
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp15
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h5
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/.gitignore3
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp1
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp24
-rw-r--r--src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h8
-rw-r--r--src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp65
-rw-r--r--src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h13
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp65
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h13
-rw-r--r--src/qtwaylandscanner/qtwaylandscanner.cpp24
-rw-r--r--src/shared/qwaylandxkb.cpp395
-rw-r--r--src/src.pro14
-rw-r--r--sync.profile8
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/client/client.pro13
-rw-r--r--tests/auto/client/client/client.pro2
-rw-r--r--tests/auto/client/client/tst_client.cpp116
-rw-r--r--tests/auto/client/datadevicev1/datadevicev1.pro4
-rw-r--r--tests/auto/client/datadevicev1/tst_datadevicev1.cpp213
-rw-r--r--tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro4
-rw-r--r--tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp111
-rw-r--r--tests/auto/client/inputcontext/inputcontext.pro6
-rw-r--r--tests/auto/client/inputcontext/tst_inputcontext.cpp184
-rw-r--r--tests/auto/client/iviapplication/iviapplication.pro2
-rw-r--r--tests/auto/client/output/output.pro5
-rw-r--r--tests/auto/client/output/tst_output.cpp228
-rw-r--r--tests/auto/client/seatv4/seatv4.pro9
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp560
-rw-r--r--tests/auto/client/shared/corecompositor.cpp140
-rw-r--r--tests/auto/client/shared/corecompositor.h209
-rw-r--r--tests/auto/client/shared/coreprotocol.cpp422
-rw-r--r--tests/auto/client/shared/coreprotocol.h373
-rw-r--r--tests/auto/client/shared/datadevice.cpp108
-rw-r--r--tests/auto/client/shared/datadevice.h109
-rw-r--r--tests/auto/client/shared/mockcompositor.cpp518
-rw-r--r--tests/auto/client/shared/mockcompositor.h294
-rw-r--r--tests/auto/client/shared/shared.pri45
-rw-r--r--tests/auto/client/shared/textinput.cpp45
-rw-r--r--tests/auto/client/shared/textinput.h51
-rw-r--r--tests/auto/client/shared/xdgshell.cpp242
-rw-r--r--tests/auto/client/shared/xdgshell.h154
-rw-r--r--tests/auto/client/shared_old/mockcompositor.cpp489
-rw-r--r--tests/auto/client/shared_old/mockcompositor.h285
-rw-r--r--tests/auto/client/shared_old/mockfullscreenshellv1.cpp43
-rw-r--r--tests/auto/client/shared_old/mockfullscreenshellv1.h58
-rw-r--r--tests/auto/client/shared_old/mockinput.cpp (renamed from tests/auto/client/shared/mockinput.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockinput.h (renamed from tests/auto/client/shared/mockinput.h)0
-rw-r--r--tests/auto/client/shared_old/mockiviapplication.cpp (renamed from tests/auto/client/shared/mockiviapplication.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockiviapplication.h (renamed from tests/auto/client/shared/mockiviapplication.h)0
-rw-r--r--tests/auto/client/shared_old/mockoutput.cpp (renamed from tests/auto/client/shared/mockoutput.cpp)42
-rw-r--r--tests/auto/client/shared_old/mockoutput.h (renamed from tests/auto/client/shared/mockoutput.h)1
-rw-r--r--tests/auto/client/shared_old/mocksurface.cpp (renamed from tests/auto/client/shared/mocksurface.cpp)28
-rw-r--r--tests/auto/client/shared_old/mocksurface.h (renamed from tests/auto/client/shared/mocksurface.h)0
-rw-r--r--tests/auto/client/shared_old/mockwlshell.cpp (renamed from tests/auto/client/shared/mockwlshell.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockwlshell.h (renamed from tests/auto/client/shared/mockwlshell.h)0
-rw-r--r--tests/auto/client/shared_old/mockxdgshellv6.cpp (renamed from tests/auto/client/shared/mockxdgshellv6.cpp)0
-rw-r--r--tests/auto/client/shared_old/mockxdgshellv6.h (renamed from tests/auto/client/shared/mockxdgshellv6.h)0
-rw-r--r--tests/auto/client/shared_old/shared_old.pri34
-rw-r--r--tests/auto/client/surface/surface.pro5
-rw-r--r--tests/auto/client/surface/tst_surface.cpp158
-rw-r--r--tests/auto/client/xdgoutput/tst_xdgoutput.cpp153
-rw-r--r--tests/auto/client/xdgoutput/xdgoutput.pro8
-rw-r--r--tests/auto/client/xdgshell/tst_xdgshell.cpp483
-rw-r--r--tests/auto/client/xdgshell/xdgshell.pro5
-rw-r--r--tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp2
-rw-r--r--tests/auto/client/xdgshellv6/xdgshellv6.pro2
-rw-r--r--tests/auto/compositor/compositor/BLACKLIST2
-rw-r--r--tests/auto/compositor/compositor/compositor.pro4
-rw-r--r--tests/auto/compositor/compositor/mockclient.cpp14
-rw-r--r--tests/auto/compositor/compositor/mockclient.h9
-rw-r--r--tests/auto/compositor/compositor/mockkeyboard.h2
-rw-r--r--tests/auto/compositor/compositor/mockpointer.h2
-rw-r--r--tests/auto/compositor/compositor/mockseat.h2
-rw-r--r--tests/auto/compositor/compositor/testcompositor.cpp6
-rw-r--r--tests/auto/compositor/compositor/tst_compositor.cpp645
-rw-r--r--tests/manual/wlscaler/main.cpp59
-rw-r--r--tests/manual/wlscaler/main.qml100
-rw-r--r--tests/manual/wlscaler/qml.qrc5
-rw-r--r--tests/manual/wlscaler/wlscaler.pro7
229 files changed, 12541 insertions, 3143 deletions
diff --git a/.gitignore b/.gitignore
index bb3819ca3..b337d343b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,8 @@ Makefile*
*.moc
*_wrapper.sh
.qmake.stash
-qwayland-server-*.cpp
-qwayland-server-*.h
+qwayland-*.cpp
+qwayland-*.h
*-protocol.c
*-client-protocol.h
*-server-protocol.h
@@ -45,29 +45,18 @@ examples/wayland/minimal-qml/minimal-qml
examples/wayland/multi-output/multi-output
examples/wayland/pure-qml/pure-qml
examples/wayland/server-buffer/client/client
-examples/wayland/server-buffer/client/qwayland-*.cpp
-examples/wayland/server-buffer/client/qwayland-*.h
examples/wayland/server-buffer/compositor/compositor
+examples/wayland/server-side-decoration/server-side-decoration
examples/wayland/subsurface/subsurface
examples/wayland/spanning-screens/spanning-screens
-src/client/qwayland-*.cpp
src/client/QtWaylandClient.version
src/client/QtWaylandClient.version.in
src/compositor/QtWaylandCompositor.version
src/compositor/QtWaylandCompositor.version.in
-src/plugins/platforms/*/qwayland-*.cpp
-src/plugins/platforms/*/qwayland-*.h
src/plugins/platforms/wayland_common/libwayland_common.a
src/plugins/platforms/wayland_common/libwayland_common.prl
-src/plugins/hardwareintegration/*/*/qwayland*.cpp
-src/plugins/hardwareintegration/*/*/qwayland*.h
-src/plugins/shellintegration/ivi-shell/qwayland-ivi-application.h
-src/plugins/shellintegration/ivi-shell/qwayland-ivi-application.cpp
-src/plugins/shellintegration/ivi-shell/qwayland-ivi-controller.h
-src/plugins/shellintegration/ivi-shell/qwayland-ivi-controller.cpp
src/imports/compositor/compositor.qrc
-tests/auto/client/client/tst_client
+tests/auto/client/*/tst_*
+!tests/auto/client/*/tst_*.cpp
tests/auto/compositor/compositor/tst_compositor
-tests/auto/compositor/compositor/qwayland-*.cpp
-tests/auto/compositor/compositor/qwayland-*.h
*~
diff --git a/.qmake.conf b/.qmake.conf
index 1bf9543ac..f8cda0e7d 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,3 @@
load(qt_build_config)
-MODULE_VERSION = 5.12.3
+MODULE_VERSION = 5.13.0
diff --git a/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro b/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro
new file mode 100644
index 000000000..28dcadcbf
--- /dev/null
+++ b/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro
@@ -0,0 +1 @@
+SOURCES += main.cpp
diff --git a/config.tests/dmabuf_client_buffer/main.cpp b/config.tests/dmabuf_client_buffer/main.cpp
new file mode 100644
index 000000000..71447dc8e
--- /dev/null
+++ b/config.tests/dmabuf_client_buffer/main.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Compositor.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+//If libdrm is available, the following files should exist
+#include "drm_mode.h"
+#include "drm_fourcc.h"
+
+int main()
+{
+// test if DMA BUF is supported
+#ifndef EGL_LINUX_DMA_BUF_EXT
+#error DMA BUF Extension not available
+#endif
+
+// test if DMA BUF import modifier extension is supported
+#ifndef EGL_EXT_image_dma_buf_import_modifiers
+#error DMA BUF Import modifier extension not available
+#endif
+
+ return 0;
+}
diff --git a/config.tests/wayland/main.cpp b/config.tests/wayland/main.cpp
index 9e0002db5..8cab379bf 100644
--- a/config.tests/wayland/main.cpp
+++ b/config.tests/wayland/main.cpp
@@ -53,15 +53,15 @@
int main()
{
#if WAYLAND_VERSION_MAJOR < 1
-# error Wayland 1.6.0 or higher required
+# error Wayland 1.8.0 or higher required
#endif
#if WAYLAND_VERSION_MAJOR == 1
-# if WAYLAND_VERSION_MINOR < 6
-# error Wayland 1.6.0 or higher required
+# if WAYLAND_VERSION_MINOR < 8
+# error Wayland 1.8.0 or higher required
# endif
-# if WAYLAND_VERSION_MINOR == 6
+# if WAYLAND_VERSION_MINOR == 8
# if WAYLAND_VERSION_MICRO < 0
-# error Wayland 1.6.0 or higher required
+# error Wayland 1.8.0 or higher required
# endif
# endif
#endif
diff --git a/examples/wayland/minimal-cpp/compositor.h b/examples/wayland/minimal-cpp/compositor.h
index f06421320..3c0c80e0e 100644
--- a/examples/wayland/minimal-cpp/compositor.h
+++ b/examples/wayland/minimal-cpp/compositor.h
@@ -71,11 +71,11 @@ public:
QOpenGLTexture *getTexture();
int iviId() const { return m_iviId; }
- QRect globalGeometry() const { return QRect(globalPosition(), surface()->size()); }
+ QRect globalGeometry() const { return QRect(globalPosition(), surface()->destinationSize()); }
void setGlobalPosition(const QPoint &globalPos) { m_pos = globalPos; m_positionSet = true; }
QPoint globalPosition() const { return m_pos; }
QPoint mapToLocal(const QPoint &globalPos) const;
- QSize size() const { return surface() ? surface()->size() : QSize(); }
+ QSize size() const { return surface() ? surface()->destinationSize() : QSize(); }
void initPosition(const QSize &screenSize, const QSize &surfaceSize);
diff --git a/examples/wayland/minimal-cpp/window.cpp b/examples/wayland/minimal-cpp/window.cpp
index 4dd7c1499..f32fb515c 100644
--- a/examples/wayland/minimal-cpp/window.cpp
+++ b/examples/wayland/minimal-cpp/window.cpp
@@ -96,7 +96,7 @@ void Window::paintGL()
GLuint textureId = texture->textureId();
QWaylandSurface *surface = view->surface();
if (surface && surface->hasContent()) {
- QSize s = surface->size();
+ QSize s = surface->destinationSize();
view->initPosition(size(), s);
QPointF pos = view->globalPosition();
QRectF surfaceGeometry(pos, s);
diff --git a/examples/wayland/pure-qml/qml/main.qml b/examples/wayland/pure-qml/qml/main.qml
index 69be7cf10..483de7514 100644
--- a/examples/wayland/pure-qml/qml/main.qml
+++ b/examples/wayland/pure-qml/qml/main.qml
@@ -72,6 +72,6 @@ WaylandCompositor {
onWlShellSurfaceCreated: screen.handleShellSurface(shellSurface)
}
- // Extension for Virtual keyboard support
+ // Extension for Input Method (QT_IM_MODULE) support at compositor-side
TextInputManager {}
}
diff --git a/examples/wayland/qwindow-compositor/compositor.cpp b/examples/wayland/qwindow-compositor/compositor.cpp
index f25e67d87..199de22e2 100644
--- a/examples/wayland/qwindow-compositor/compositor.cpp
+++ b/examples/wayland/qwindow-compositor/compositor.cpp
@@ -79,7 +79,7 @@ QOpenGLTexture *View::getTexture()
if (newContent) {
m_texture = buf.toOpenGLTexture();
if (surface()) {
- m_size = surface()->size();
+ m_size = surface()->destinationSize();
m_origin = buf.origin() == QWaylandSurface::OriginTopLeft
? QOpenGLTextureBlitter::OriginTopLeft
: QOpenGLTextureBlitter::OriginBottomLeft;
@@ -96,7 +96,7 @@ QOpenGLTextureBlitter::Origin View::textureOrigin() const
QSize View::size() const
{
- return surface() ? surface()->size() : m_size;
+ return surface() ? surface()->destinationSize() : m_size;
}
bool View::isCursor() const
diff --git a/examples/wayland/qwindow-compositor/compositor.h b/examples/wayland/qwindow-compositor/compositor.h
index 2a395a1ab..8f18dc53d 100644
--- a/examples/wayland/qwindow-compositor/compositor.h
+++ b/examples/wayland/qwindow-compositor/compositor.h
@@ -82,7 +82,7 @@ public:
void setParentView(View *parent) { m_parentView = parent; }
View *parentView() const { return m_parentView; }
QPointF parentPosition() const { return m_parentView ? (m_parentView->position() + m_parentView->parentPosition()) : QPointF(); }
- QSize windowSize() { return m_xdgSurface ? m_xdgSurface->windowGeometry().size() : surface() ? surface()->size() : m_size; }
+ QSize windowSize() { return m_xdgSurface ? m_xdgSurface->windowGeometry().size() : surface() ? surface()->destinationSize() : m_size; }
QPoint offset() const { return m_offset; }
qreal animationFactor() const {return m_animationFactor; }
diff --git a/examples/wayland/qwindow-compositor/window.cpp b/examples/wayland/qwindow-compositor/window.cpp
index c8e5604bd..9a8ffc2de 100644
--- a/examples/wayland/qwindow-compositor/window.cpp
+++ b/examples/wayland/qwindow-compositor/window.cpp
@@ -181,7 +181,7 @@ void Window::startResize(int edge, bool anchored)
m_grabState = ResizeGrab;
m_resizeEdge = edge;
m_resizeAnchored = anchored;
- m_resizeAnchorPosition = getAnchorPosition(m_mouseView->position(), edge, m_mouseView->surface()->size());
+ m_resizeAnchorPosition = getAnchorPosition(m_mouseView->position(), edge, m_mouseView->surface()->destinationSize());
}
void Window::startDrag(View *dragIcon)
diff --git a/features/wayland-scanner-client-wayland-protocol-include.prf b/features/wayland-scanner-client-wayland-protocol-include.prf
new file mode 100644
index 000000000..9c5d2d1ed
--- /dev/null
+++ b/features/wayland-scanner-client-wayland-protocol-include.prf
@@ -0,0 +1,53 @@
+# Special version of WAYLANDCLIENTSOURCES to be used with protocols that
+# have requests that create objects with interfaces defined in the core
+# wayland protocol.
+#
+# E.g. the xcomposite protocol has a request which creates a wl_buffer. With
+# the regular wayland-scanner.prf compilation would fail because
+# wl_buffer_interface is not defined.
+#
+# This version solves the problem by prepending
+# #include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
+# to the wayland-scanner generated files.
+
+isEmpty(QMAKE_WAYLAND_SCANNER):error("QMAKE_WAYLAND_SCANNER not defined for this mkspec")
+
+!isEmpty(MODULE_INCNAME) {
+ WAYLAND_INCLUDE_DIR = $$MODULE_INCNAME/private
+}
+
+wayland_client_header.name = wayland ${QMAKE_FILE_BASE}
+wayland_client_header.input = WAYLANDCLIENTSOURCES
+wayland_client_header.variable_out = HEADERS
+wayland_client_header.output = wayland-${QMAKE_FILE_BASE}-client-protocol$${first(QMAKE_EXT_H)}
+# XXX: Prepend the necessary include in the generated header
+wayland_client_header.commands = echo \"$${LITERAL_HASH}include <QtWaylandClient/private/wayland-wayland-client-protocol.h>\" > ${QMAKE_FILE_OUT} && $$QMAKE_WAYLAND_SCANNER --include-core-only client-header < ${QMAKE_FILE_IN} >> ${QMAKE_FILE_OUT}
+QMAKE_EXTRA_COMPILERS += wayland_client_header
+
+wayland_code.name = wayland ${QMAKE_FILE_BASE}
+wayland_code.input = WAYLANDCLIENTSOURCES
+wayland_code.variable_out = SOURCES
+wayland_code.output = wayland-${QMAKE_FILE_BASE}-protocol.c
+wayland_code.commands = $$QMAKE_WAYLAND_SCANNER --include-core-only code < ${QMAKE_FILE_IN} > ${QMAKE_FILE_OUT}
+silent:wayland_code.commands = @echo Wayland code header ${QMAKE_FILE_IN} && $$wayland_code.commands
+QMAKE_EXTRA_COMPILERS += wayland_code
+
+qtPrepareTool(QMAKE_QTWAYLANDSCANNER, qtwaylandscanner)
+
+qtwayland_client_header.name = qtwayland ${QMAKE_FILE_BASE}
+qtwayland_client_header.input = WAYLANDCLIENTSOURCES
+qtwayland_client_header.variable_out = HEADERS
+qtwayland_client_header.depends += $$QMAKE_QTWAYLANDSCANNER_EXE wayland-${QMAKE_FILE_BASE}-client-protocol$${first(QMAKE_EXT_H)}
+qtwayland_client_header.output = qwayland-${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)}
+qtwayland_client_header.commands = $$QMAKE_QTWAYLANDSCANNER client-header ${QMAKE_FILE_IN} $$WAYLAND_INCLUDE_DIR > ${QMAKE_FILE_OUT}
+silent:qtwayland_client_header.commands = @echo QtWayland client header ${QMAKE_FILE_IN} && $$qtwayland_client_header.commands
+QMAKE_EXTRA_COMPILERS += qtwayland_client_header
+
+qtwayland_client_code.name = qtwayland ${QMAKE_FILE_BASE}
+qtwayland_client_code.input = WAYLANDCLIENTSOURCES
+qtwayland_client_code.variable_out = SOURCES
+qtwayland_client_code.depends += $$QMAKE_QTWAYLANDSCANNER_EXE qwayland-${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)}
+qtwayland_client_code.output = qwayland-${QMAKE_FILE_BASE}.cpp
+qtwayland_client_code.commands = $$QMAKE_QTWAYLANDSCANNER client-code ${QMAKE_FILE_IN} $$WAYLAND_INCLUDE_DIR > ${QMAKE_FILE_OUT}
+silent:qtwayland_client_code.commands = @echo QtWayland client code ${QMAKE_FILE_IN} && $$qtwayland_client_code.commands
+QMAKE_EXTRA_COMPILERS += qtwayland_client_code
diff --git a/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml b/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml
new file mode 100644
index 000000000..1bca7b7c7
--- /dev/null
+++ b/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="fullscreen_shell_unstable_v1">
+
+ <copyright>
+ Copyright © 2016 Yong Bakos
+ Copyright © 2015 Jason Ekstrand
+ Copyright © 2015 Jonas Ådahl
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="zwp_fullscreen_shell_v1" version="1">
+ <description summary="displays a single surface per output">
+ Displays a single surface per output.
+
+ This interface provides a mechanism for a single client to display
+ simple full-screen surfaces. While there technically may be multiple
+ clients bound to this interface, only one of those clients should be
+ shown at a time.
+
+ To present a surface, the client uses either the present_surface or
+ present_surface_for_mode requests. Presenting a surface takes effect
+ on the next wl_surface.commit. See the individual requests for
+ details about scaling and mode switches.
+
+ The client can have at most one surface per output at any time.
+ Requesting a surface to be presented on an output that already has a
+ surface replaces the previously presented surface. Presenting a null
+ surface removes its content and effectively disables the output.
+ Exactly what happens when an output is "disabled" is
+ compositor-specific. The same surface may be presented on multiple
+ outputs simultaneously.
+
+ Once a surface is presented on an output, it stays on that output
+ until either the client removes it or the compositor destroys the
+ output. This way, the client can update the output's contents by
+ simply attaching a new buffer.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <request name="release" type="destructor">
+ <description summary="release the wl_fullscreen_shell interface">
+ Release the binding from the wl_fullscreen_shell interface.
+
+ This destroys the server-side object and frees this binding. If
+ the client binds to wl_fullscreen_shell multiple times, it may wish
+ to free some of those bindings.
+ </description>
+ </request>
+
+ <enum name="capability">
+ <description summary="capabilities advertised by the compositor">
+ Various capabilities that can be advertised by the compositor. They
+ are advertised one-at-a-time when the wl_fullscreen_shell interface is
+ bound. See the wl_fullscreen_shell.capability event for more details.
+
+ ARBITRARY_MODES:
+ This is a hint to the client that indicates that the compositor is
+ capable of setting practically any mode on its outputs. If this
+ capability is provided, wl_fullscreen_shell.present_surface_for_mode
+ will almost never fail and clients should feel free to set whatever
+ mode they like. If the compositor does not advertise this, it may
+ still support some modes that are not advertised through wl_global.mode
+ but it is less likely.
+
+ CURSOR_PLANE:
+ This is a hint to the client that indicates that the compositor can
+ handle a cursor surface from the client without actually compositing.
+ This may be because of a hardware cursor plane or some other mechanism.
+ If the compositor does not advertise this capability then setting
+ wl_pointer.cursor may degrade performance or be ignored entirely. If
+ CURSOR_PLANE is not advertised, it is recommended that the client draw
+ its own cursor and set wl_pointer.cursor(NULL).
+ </description>
+ <entry name="arbitrary_modes" value="1" summary="compositor is capable of almost any output mode"/>
+ <entry name="cursor_plane" value="2" summary="compositor has a separate cursor plane"/>
+ </enum>
+
+ <event name="capability">
+ <description summary="advertises a capability of the compositor">
+ Advertises a single capability of the compositor.
+
+ When the wl_fullscreen_shell interface is bound, this event is emitted
+ once for each capability advertised. Valid capabilities are given by
+ the wl_fullscreen_shell.capability enum. If clients want to take
+ advantage of any of these capabilities, they should use a
+ wl_display.sync request immediately after binding to ensure that they
+ receive all the capability events.
+ </description>
+ <arg name="capability" type="uint"/>
+ </event>
+
+ <enum name="present_method">
+ <description summary="different method to set the surface fullscreen">
+ Hints to indicate to the compositor how to deal with a conflict
+ between the dimensions of the surface and the dimensions of the
+ output. The compositor is free to ignore this parameter.
+ </description>
+ <entry name="default" value="0" summary="no preference, apply default policy"/>
+ <entry name="center" value="1" summary="center the surface on the output"/>
+ <entry name="zoom" value="2" summary="scale the surface, preserving aspect ratio, to the largest size that will fit on the output" />
+ <entry name="zoom_crop" value="3" summary="scale the surface, preserving aspect ratio, to fully fill the output cropping if needed" />
+ <entry name="stretch" value="4" summary="scale the surface to the size of the output ignoring aspect ratio" />
+ </enum>
+
+ <request name="present_surface">
+ <description summary="present surface for display">
+ Present a surface on the given output.
+
+ If the output is null, the compositor will present the surface on
+ whatever display (or displays) it thinks best. In particular, this
+ may replace any or all surfaces currently presented so it should
+ not be used in combination with placing surfaces on specific
+ outputs.
+
+ The method parameter is a hint to the compositor for how the surface
+ is to be presented. In particular, it tells the compositor how to
+ handle a size mismatch between the presented surface and the
+ output. The compositor is free to ignore this parameter.
+
+ The "zoom", "zoom_crop", and "stretch" methods imply a scaling
+ operation on the surface. This will override any kind of output
+ scaling, so the buffer_scale property of the surface is effectively
+ ignored.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
+ <arg name="method" type="uint"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ </request>
+
+ <request name="present_surface_for_mode">
+ <description summary="present surface for display at a particular mode">
+ Presents a surface on the given output for a particular mode.
+
+ If the current size of the output differs from that of the surface,
+ the compositor will attempt to change the size of the output to
+ match the surface. The result of the mode-switch operation will be
+ returned via the provided wl_fullscreen_shell_mode_feedback object.
+
+ If the current output mode matches the one requested or if the
+ compositor successfully switches the mode to match the surface,
+ then the mode_successful event will be sent and the output will
+ contain the contents of the given surface. If the compositor
+ cannot match the output size to the surface size, the mode_failed
+ will be sent and the output will contain the contents of the
+ previously presented surface (if any). If another surface is
+ presented on the given output before either of these has a chance
+ to happen, the present_cancelled event will be sent.
+
+ Due to race conditions and other issues unknown to the client, no
+ mode-switch operation is guaranteed to succeed. However, if the
+ mode is one advertised by wl_output.mode or if the compositor
+ advertises the ARBITRARY_MODES capability, then the client should
+ expect that the mode-switch operation will usually succeed.
+
+ If the size of the presented surface changes, the resulting output
+ is undefined. The compositor may attempt to change the output mode
+ to compensate. However, there is no guarantee that a suitable mode
+ will be found and the client has no way to be notified of success
+ or failure.
+
+ The framerate parameter specifies the desired framerate for the
+ output in mHz. The compositor is free to ignore this parameter. A
+ value of 0 indicates that the client has no preference.
+
+ If the value of wl_output.scale differs from wl_surface.buffer_scale,
+ then the compositor may choose a mode that matches either the buffer
+ size or the surface size. In either case, the surface will fill the
+ output.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="framerate" type="int"/>
+ <arg name="feedback" type="new_id" interface="zwp_fullscreen_shell_mode_feedback_v1"/>
+ </request>
+
+ <enum name="error">
+ <description summary="wl_fullscreen_shell error values">
+ These errors can be emitted in response to wl_fullscreen_shell requests.
+ </description>
+ <entry name="invalid_method" value="0" summary="present_method is not known"/>
+ </enum>
+ </interface>
+
+ <interface name="zwp_fullscreen_shell_mode_feedback_v1" version="1">
+ <event name="mode_successful">
+ <description summary="mode switch succeeded">
+ This event indicates that the attempted mode switch operation was
+ successful. A surface of the size requested in the mode switch
+ will fill the output without scaling.
+
+ Upon receiving this event, the client should destroy the
+ wl_fullscreen_shell_mode_feedback object.
+ </description>
+ </event>
+
+ <event name="mode_failed">
+ <description summary="mode switch failed">
+ This event indicates that the attempted mode switch operation
+ failed. This may be because the requested output mode is not
+ possible or it may mean that the compositor does not want to allow it.
+
+ Upon receiving this event, the client should destroy the
+ wl_fullscreen_shell_mode_feedback object.
+ </description>
+ </event>
+
+ <event name="present_cancelled">
+ <description summary="mode switch cancelled">
+ This event indicates that the attempted mode switch operation was
+ cancelled. Most likely this is because the client requested a
+ second mode switch before the first one completed.
+
+ Upon receiving this event, the client should destroy the
+ wl_fullscreen_shell_mode_feedback object.
+ </description>
+ </event>
+ </interface>
+
+</protocol>
diff --git a/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml b/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml
new file mode 100644
index 000000000..154afe23e
--- /dev/null
+++ b/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="linux_dmabuf_unstable_v1">
+
+ <copyright>
+ Copyright © 2014, 2015 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="zwp_linux_dmabuf_v1" version="3">
+ <description summary="factory for creating dmabuf-based wl_buffers">
+ Following the interfaces from:
+ https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
+ and the Linux DRM sub-system's AddFb2 ioctl.
+
+ This interface offers ways to create generic dmabuf-based
+ wl_buffers. Immediately after a client binds to this interface,
+ the set of supported formats and format modifiers is sent with
+ 'format' and 'modifier' events.
+
+ The following are required from clients:
+
+ - Clients must ensure that either all data in the dma-buf is
+ coherent for all subsequent read access or that coherency is
+ correctly handled by the underlying kernel-side dma-buf
+ implementation.
+
+ - Don't make any more attachments after sending the buffer to the
+ compositor. Making more attachments later increases the risk of
+ the compositor not being able to use (re-import) an existing
+ dmabuf-based wl_buffer.
+
+ The underlying graphics stack must ensure the following:
+
+ - The dmabuf file descriptors relayed to the server will stay valid
+ for the whole lifetime of the wl_buffer. This means the server may
+ at any time use those fds to import the dmabuf into any kernel
+ sub-system that might accept it.
+
+ To create a wl_buffer from one or more dmabufs, a client creates a
+ zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
+ request. All planes required by the intended format are added with
+ the 'add' request. Finally, a 'create' or 'create_immed' request is
+ issued, which has the following outcome depending on the import success.
+
+ The 'create' request,
+ - on success, triggers a 'created' event which provides the final
+ wl_buffer to the client.
+ - on failure, triggers a 'failed' event to convey that the server
+ cannot use the dmabufs received from the client.
+
+ For the 'create_immed' request,
+ - on success, the server immediately imports the added dmabufs to
+ create a wl_buffer. No event is sent from the server in this case.
+ - on failure, the server can choose to either:
+ - terminate the client by raising a fatal error.
+ - mark the wl_buffer as failed, and send a 'failed' event to the
+ client. If the client uses a failed wl_buffer as an argument to any
+ request, the behaviour is compositor implementation-defined.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind the factory">
+ Objects created through this interface, especially wl_buffers, will
+ remain valid.
+ </description>
+ </request>
+
+ <request name="create_params">
+ <description summary="create a temporary object for buffer parameters">
+ This temporary object is used to collect multiple dmabuf handles into
+ a single batch to create a wl_buffer. It can only be used once and
+ should be destroyed after a 'created' or 'failed' event has been
+ received.
+ </description>
+ <arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
+ summary="the new temporary"/>
+ </request>
+
+ <event name="format">
+ <description summary="supported buffer format">
+ This event advertises one buffer format that the server supports.
+ All the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees
+ that the client has received all supported formats.
+
+ For the definition of the format codes, see the
+ zwp_linux_buffer_params_v1::create request.
+
+ Warning: the 'format' event is likely to be deprecated and replaced
+ with the 'modifier' event introduced in zwp_linux_dmabuf_v1
+ version 3, described below. Please refrain from using the information
+ received from this event.
+ </description>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ </event>
+
+ <event name="modifier" since="3">
+ <description summary="supported buffer format modifier">
+ This event advertises the formats that the server supports, along with
+ the modifiers supported for each format. All the supported modifiers
+ for all the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees that
+ the client has received all supported format-modifier pairs.
+
+ For the definition of the format and modifier codes, see the
+ zwp_linux_buffer_params_v1::create request.
+ </description>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="modifier_hi" type="uint"
+ summary="high 32 bits of layout modifier"/>
+ <arg name="modifier_lo" type="uint"
+ summary="low 32 bits of layout modifier"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_linux_buffer_params_v1" version="3">
+ <description summary="parameters for creating a dmabuf-based wl_buffer">
+ This temporary object is a collection of dmabufs and other
+ parameters that together form a single logical buffer. The temporary
+ object may eventually create one wl_buffer unless cancelled by
+ destroying it before requesting 'create'.
+
+ Single-planar formats only require one dmabuf, however
+ multi-planar formats may require more than one dmabuf. For all
+ formats, an 'add' request must be called once per plane (even if the
+ underlying dmabuf fd is identical).
+
+ You must use consecutive plane indices ('plane_idx' argument for 'add')
+ from zero to the number of planes used by the drm_fourcc format code.
+ All planes required by the format must be given exactly once, but can
+ be given in any order. Each plane index can be set only once.
+ </description>
+
+ <enum name="error">
+ <entry name="already_used" value="0"
+ summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
+ <entry name="plane_idx" value="1"
+ summary="plane index out of bounds"/>
+ <entry name="plane_set" value="2"
+ summary="the plane index was already set"/>
+ <entry name="incomplete" value="3"
+ summary="missing or too many planes to create a buffer"/>
+ <entry name="invalid_format" value="4"
+ summary="format not supported"/>
+ <entry name="invalid_dimensions" value="5"
+ summary="invalid width or height"/>
+ <entry name="out_of_bounds" value="6"
+ summary="offset + stride * height goes out of dmabuf bounds"/>
+ <entry name="invalid_wl_buffer" value="7"
+ summary="invalid wl_buffer resulted from importing dmabufs via
+ the create_immed request on given buffer_params"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="delete this object, used or not">
+ Cleans up the temporary data sent to the server for dmabuf-based
+ wl_buffer creation.
+ </description>
+ </request>
+
+ <request name="add">
+ <description summary="add a dmabuf to the temporary set">
+ This request adds one dmabuf to the set in this
+ zwp_linux_buffer_params_v1.
+
+ The 64-bit unsigned value combined from modifier_hi and modifier_lo
+ is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
+ fb modifier, which is defined in drm_mode.h of Linux UAPI.
+ This is an opaque token. Drivers use this token to express tiling,
+ compression, etc. driver-specific modifications to the base format
+ defined by the DRM fourcc code.
+
+ This request raises the PLANE_IDX error if plane_idx is too large.
+ The error PLANE_SET is raised if attempting to set a plane that
+ was already set.
+ </description>
+ <arg name="fd" type="fd" summary="dmabuf fd"/>
+ <arg name="plane_idx" type="uint" summary="plane index"/>
+ <arg name="offset" type="uint" summary="offset in bytes"/>
+ <arg name="stride" type="uint" summary="stride in bytes"/>
+ <arg name="modifier_hi" type="uint"
+ summary="high 32 bits of layout modifier"/>
+ <arg name="modifier_lo" type="uint"
+ summary="low 32 bits of layout modifier"/>
+ </request>
+
+ <enum name="flags">
+ <entry name="y_invert" value="1" summary="contents are y-inverted"/>
+ <entry name="interlaced" value="2" summary="content is interlaced"/>
+ <entry name="bottom_first" value="4" summary="bottom field first"/>
+ </enum>
+
+ <request name="create">
+ <description summary="create a wl_buffer from the given dmabufs">
+ This asks for creation of a wl_buffer from the added dmabuf
+ buffers. The wl_buffer is not created immediately but returned via
+ the 'created' event if the dmabuf sharing succeeds. The sharing
+ may fail at runtime for reasons a client cannot predict, in
+ which case the 'failed' event is triggered.
+
+ The 'format' argument is a DRM_FORMAT code, as defined by the
+ libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
+ authoritative source on how the format codes should work.
+
+ The 'flags' is a bitfield of the flags defined in enum "flags".
+ 'y_invert' means the that the image needs to be y-flipped.
+
+ Flag 'interlaced' means that the frame in the buffer is not
+ progressive as usual, but interlaced. An interlaced buffer as
+ supported here must always contain both top and bottom fields.
+ The top field always begins on the first pixel row. The temporal
+ ordering between the two fields is top field first, unless
+ 'bottom_first' is specified. It is undefined whether 'bottom_first'
+ is ignored if 'interlaced' is not set.
+
+ This protocol does not convey any information about field rate,
+ duration, or timing, other than the relative ordering between the
+ two fields in one buffer. A compositor may have to estimate the
+ intended field rate from the incoming buffer rate. It is undefined
+ whether the time of receiving wl_surface.commit with a new buffer
+ attached, applying the wl_surface state, wl_surface.frame callback
+ trigger, presentation, or any other point in the compositor cycle
+ is used to measure the frame or field times. There is no support
+ for detecting missed or late frames/fields/buffers either, and
+ there is no support whatsoever for cooperating with interlaced
+ compositor output.
+
+ The composited image quality resulting from the use of interlaced
+ buffers is explicitly undefined. A compositor may use elaborate
+ hardware features or software to deinterlace and create progressive
+ output frames from a sequence of interlaced input buffers, or it
+ may produce substandard image quality. However, compositors that
+ cannot guarantee reasonable image quality in all cases are recommended
+ to just reject all interlaced buffers.
+
+ Any argument errors, including non-positive width or height,
+ mismatch between the number of planes and the format, bad
+ format, bad offset or stride, may be indicated by fatal protocol
+ errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
+ OUT_OF_BOUNDS.
+
+ Dmabuf import errors in the server that are not obvious client
+ bugs are returned via the 'failed' event as non-fatal. This
+ allows attempting dmabuf sharing and falling back in the client
+ if it fails.
+
+ This request can be sent only once in the object's lifetime, after
+ which the only legal request is destroy. This object should be
+ destroyed after issuing a 'create' request. Attempting to use this
+ object after issuing 'create' raises ALREADY_USED protocol error.
+
+ It is not mandatory to issue 'create'. If a client wants to
+ cancel the buffer creation, it can just destroy this object.
+ </description>
+ <arg name="width" type="int" summary="base plane width in pixels"/>
+ <arg name="height" type="int" summary="base plane height in pixels"/>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="flags" type="uint" summary="see enum flags"/>
+ </request>
+
+ <event name="created">
+ <description summary="buffer creation succeeded">
+ This event indicates that the attempted buffer creation was
+ successful. It provides the new wl_buffer referencing the dmabuf(s).
+
+ Upon receiving this event, the client should destroy the
+ zlinux_dmabuf_params object.
+ </description>
+ <arg name="buffer" type="new_id" interface="wl_buffer"
+ summary="the newly created wl_buffer"/>
+ </event>
+
+ <event name="failed">
+ <description summary="buffer creation failed">
+ This event indicates that the attempted buffer creation has
+ failed. It usually means that one of the dmabuf constraints
+ has not been fulfilled.
+
+ Upon receiving this event, the client should destroy the
+ zlinux_buffer_params object.
+ </description>
+ </event>
+
+ <request name="create_immed" since="2">
+ <description summary="immediately create a wl_buffer from the given
+ dmabufs">
+ This asks for immediate creation of a wl_buffer by importing the
+ added dmabufs.
+
+ In case of import success, no event is sent from the server, and the
+ wl_buffer is ready to be used by the client.
+
+ Upon import failure, either of the following may happen, as seen fit
+ by the implementation:
+ - the client is terminated with one of the following fatal protocol
+ errors:
+ - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
+ in case of argument errors such as mismatch between the number
+ of planes and the format, bad format, non-positive width or
+ height, or bad offset or stride.
+ - INVALID_WL_BUFFER, in case the cause for failure is unknown or
+ plaform specific.
+ - the server creates an invalid wl_buffer, marks it as failed and
+ sends a 'failed' event to the client. The result of using this
+ invalid wl_buffer as an argument in any request by the client is
+ defined by the compositor implementation.
+
+ This takes the same arguments as a 'create' request, and obeys the
+ same restrictions.
+ </description>
+ <arg name="buffer_id" type="new_id" interface="wl_buffer"
+ summary="id for the newly created wl_buffer"/>
+ <arg name="width" type="int" summary="base plane width in pixels"/>
+ <arg name="height" type="int" summary="base plane height in pixels"/>
+ <arg name="format" type="uint" summary="DRM_FORMAT code"/>
+ <arg name="flags" type="uint" summary="see enum flags"/>
+ </request>
+
+ </interface>
+
+</protocol>
diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json
index 051bd3f95..7e068f755 100644
--- a/src/3rdparty/protocol/qt_attribution.json
+++ b/src/3rdparty/protocol/qt_attribution.json
@@ -1,5 +1,24 @@
[
{
+ "Id": "wayland-fullscreen-protocol",
+ "Name": "Wayland Fullscreen Shell Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland platform plugin.",
+ "Files": "fullscreen-shell-unstable-v1.xml",
+
+ "Description": "A Wayland shell for displaying a single surface per output",
+ "Homepage": "https://wayland.freedesktop.org",
+ "Version": "unstable v1",
+ "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2016 Yong Bakos
+Copyright © 2015 Jason Ekstrand
+Copyright © 2015 Jonas Ådahl"
+ },
+
+ {
"Id": "wayland-protocol",
"Name": "Wayland Protocol",
"QDocModule": "qtwaylandcompositor",
@@ -8,14 +27,14 @@
"Description": "Wayland is a protocol for a compositor to talk to its clients.",
"Homepage": "https://wayland.freedesktop.org",
- "Version": "1.6.1",
- "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland/tag/?id=1.6.1",
- "LicenseId": "HPND",
- "License": "HPND License",
- "LicenseFile": "HPND_LICENSE.txt",
+ "Version": "1.16.0",
+ "DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland/raw/1.16.0/protocol/wayland.xml",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2008-2011 Kristian Høgsberg
- Copyright © 2010-2011 Intel Corporation
- Copyright © 2012-2013 Collabora, Ltd."
+Copyright © 2010-2011 Intel Corporation
+Copyright © 2012-2013 Collabora, Ltd."
},
{
@@ -37,6 +56,40 @@ Copyright (c) 2013 BMW Car IT GmbH"
},
{
+ "Id": "wayland-scaler-protocol",
+ "Name": "Wayland Scaler Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland Compositor API",
+ "Files": "scaler.xml",
+
+ "Description": "The Wayland scaler extension allows a client to scale or crop a surface without modifying the buffer",
+ "Homepage": "https://wayland.freedesktop.org",
+ "Version": "2",
+ "DownloadLocation": "https://cgit.freedesktop.org/wayland/weston/plain/protocol/scaler.xml?h=1.11.1",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2013-2014 Collabora, Ltd."
+ },
+
+ {
+ "Id": "wayland-viewporter-protocol",
+ "Name": "Wayland Viewporter Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland Compositor API",
+ "Files": "viewporter.xml",
+
+ "Description": "The Wayland viewporter extension allows a client to scale or crop a surface without modifying the buffer",
+ "Homepage": "https://wayland.freedesktop.org",
+ "Version": "1",
+ "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/stable/viewporter/viewporter.xml",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2013-2016 Collabora, Ltd."
+ },
+
+ {
"Id": "wayland-xdg-decoration-protocol",
"Name": "Wayland xdg-decoration Protocol",
"QDocModule": "qtwaylandcompositor",
@@ -106,6 +159,21 @@ Copyright © 2015, 2016 Jan Arne Petersen"
},
{
+ "Id": "wayland-linux-dmabuf-unstable-v1",
+ "Name": "Wayland Linux Dmabuf Unstable V1 Protocol",
+ "QDocModule": "qtwaylandcompositor",
+ "QtUsage": "Used in the Qt Wayland Compositor",
+ "Files": "linux-dmabuf-unstable-v1.xml",
+
+ "Description": "The linux dmabuf protocol is a way to create dmabuf-based wl_buffers",
+ "Homepage": "https://wayland.freedesktop.org",
+ "LicenseId": "MIT",
+ "License": "MIT License",
+ "LicenseFile": "MIT_LICENSE.txt",
+ "Copyright": "Copyright © 2014, 2015 Collabora, Ltd."
+ },
+
+ {
"Id": "wayland-eglstream-controller",
"Name": "Wayland EGLStream Controller Protocol",
"QDocModule": "qtwaylandcompositor",
diff --git a/src/3rdparty/protocol/scaler.xml b/src/3rdparty/protocol/scaler.xml
new file mode 100644
index 000000000..0e482a635
--- /dev/null
+++ b/src/3rdparty/protocol/scaler.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="scaler">
+
+ <copyright>
+ Copyright © 2013-2014 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="wl_scaler" version="2">
+ <description summary="surface cropping and scaling">
+ The global interface exposing surface cropping and scaling
+ capabilities is used to instantiate an interface extension for a
+ wl_surface object. This extended interface will then allow
+ cropping and scaling the surface contents, effectively
+ disconnecting the direct relationship between the buffer and the
+ surface size.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind from the cropping and scaling interface">
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other objects,
+ wl_viewport objects included.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="viewport_exists" value="0"
+ summary="the surface already has a viewport object associated"/>
+ </enum>
+
+ <request name="get_viewport">
+ <description summary="extend surface interface for crop and scale">
+ Instantiate an interface extension for the given wl_surface to
+ crop and scale its content. If the given wl_surface already has
+ a wl_viewport object associated, the viewport_exists
+ protocol error is raised.
+ </description>
+
+ <arg name="id" type="new_id" interface="wl_viewport"
+ summary="the new viewport interface id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface"/>
+ </request>
+ </interface>
+
+ <interface name="wl_viewport" version="2">
+ <description summary="crop and scale interface to a wl_surface">
+ An additional interface to a wl_surface object, which allows the
+ client to specify the cropping and scaling of the surface
+ contents.
+
+ This interface allows to define the source rectangle (src_x,
+ src_y, src_width, src_height) from where to take the wl_buffer
+ contents, and scale that to destination size (dst_width,
+ dst_height). This state is double-buffered, and is applied on the
+ next wl_surface.commit.
+
+ The two parts of crop and scale state are independent: the source
+ rectangle, and the destination size. Initially both are unset, that
+ is, no scaling is applied. The whole of the current wl_buffer is
+ used as the source, and the surface size is as defined in
+ wl_surface.attach.
+
+ If the destination size is set, it causes the surface size to become
+ dst_width, dst_height. The source (rectangle) is scaled to exactly
+ this size. This overrides whatever the attached wl_buffer size is,
+ unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
+ has no content and therefore no size. Otherwise, the size is always
+ at least 1x1 in surface coordinates.
+
+ If the source rectangle is set, it defines what area of the
+ wl_buffer is taken as the source. If the source rectangle is set and
+ the destination size is not set, the surface size becomes the source
+ rectangle size rounded up to the nearest integer. If the source size
+ is already exactly integers, this results in cropping without scaling.
+
+ The coordinate transformations from buffer pixel coordinates up to
+ the surface-local coordinates happen in the following order:
+ 1. buffer_transform (wl_surface.set_buffer_transform)
+ 2. buffer_scale (wl_surface.set_buffer_scale)
+ 3. crop and scale (wl_viewport.set*)
+ This means, that the source rectangle coordinates of crop and scale
+ are given in the coordinates after the buffer transform and scale,
+ i.e. in the coordinates that would be the surface-local coordinates
+ if the crop and scale was not applied.
+
+ If the source rectangle is partially or completely outside of the
+ wl_buffer, then the surface contents are undefined (not void), and
+ the surface size is still dst_width, dst_height.
+
+ The x, y arguments of wl_surface.attach are applied as normal to
+ the surface. They indicate how many pixels to remove from the
+ surface size from the left and the top. In other words, they are
+ still in the surface-local coordinate system, just like dst_width
+ and dst_height are.
+
+ If the wl_surface associated with the wl_viewport is destroyed,
+ the wl_viewport object becomes inert.
+
+ If the wl_viewport object is destroyed, the crop and scale
+ state is removed from the wl_surface. The change will be applied
+ on the next wl_surface.commit.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove scaling and cropping from the surface">
+ The associated wl_surface's crop and scale state is removed.
+ The change is applied on the next wl_surface.commit.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="bad_value" value="0"
+ summary="negative or zero values in width or height"/>
+ </enum>
+
+ <request name="set">
+ <description summary="set the crop and scale state">
+ Set both source rectangle and destination size of the associated
+ wl_surface. See wl_viewport for the description, and relation to
+ the wl_buffer size.
+
+ The bad_value protocol error is raised if src_width or
+ src_height is negative, or if dst_width or dst_height is not
+ positive.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+
+ Arguments dst_x and dst_y do not exist here, use the x and y
+ arguments to wl_surface.attach. The x, y, dst_width, and dst_height
+ define the surface-local coordinate system irrespective of the
+ attached wl_buffer size.
+ </description>
+
+ <arg name="src_x" type="fixed" summary="source rectangle x"/>
+ <arg name="src_y" type="fixed" summary="source rectangle y"/>
+ <arg name="src_width" type="fixed" summary="source rectangle width"/>
+ <arg name="src_height" type="fixed" summary="source rectangle height"/>
+ <arg name="dst_width" type="int" summary="surface width"/>
+ <arg name="dst_height" type="int" summary="surface height"/>
+ </request>
+
+ <request name="set_source" since="2">
+ <description summary="set the source rectangle for cropping">
+ Set the source rectangle of the associated wl_surface. See
+ wl_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If width is -1.0 and height is -1.0, the source rectangle is unset
+ instead. Any other pair of values for width and height that
+ contains zero or negative values raises the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+ </description>
+
+ <arg name="x" type="fixed" summary="source rectangle x"/>
+ <arg name="y" type="fixed" summary="source rectangle y"/>
+ <arg name="width" type="fixed" summary="source rectangle width"/>
+ <arg name="height" type="fixed" summary="source rectangle height"/>
+ </request>
+
+ <request name="set_destination" since="2">
+ <description summary="set the surface size for scaling">
+ Set the destination size of the associated wl_surface. See
+ wl_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If width is -1 and height is -1, the destination size is unset
+ instead. Any other pair of values for width and height that
+ contains zero or negative values raises the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+
+ Arguments x and y do not exist here, use the x and y arguments to
+ wl_surface.attach. The x, y, width, and height define the
+ surface-local coordinate system irrespective of the attached
+ wl_buffer size.
+ </description>
+
+ <arg name="width" type="int" summary="surface width"/>
+ <arg name="height" type="int" summary="surface height"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/src/3rdparty/protocol/viewporter.xml b/src/3rdparty/protocol/viewporter.xml
new file mode 100644
index 000000000..c732d8c35
--- /dev/null
+++ b/src/3rdparty/protocol/viewporter.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="viewporter">
+
+ <copyright>
+ Copyright © 2013-2016 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="wp_viewporter" version="1">
+ <description summary="surface cropping and scaling">
+ The global interface exposing surface cropping and scaling
+ capabilities is used to instantiate an interface extension for a
+ wl_surface object. This extended interface will then allow
+ cropping and scaling the surface contents, effectively
+ disconnecting the direct relationship between the buffer and the
+ surface size.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind from the cropping and scaling interface">
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other objects,
+ wp_viewport objects included.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="viewport_exists" value="0"
+ summary="the surface already has a viewport object associated"/>
+ </enum>
+
+ <request name="get_viewport">
+ <description summary="extend surface interface for crop and scale">
+ Instantiate an interface extension for the given wl_surface to
+ crop and scale its content. If the given wl_surface already has
+ a wp_viewport object associated, the viewport_exists
+ protocol error is raised.
+ </description>
+ <arg name="id" type="new_id" interface="wp_viewport"
+ summary="the new viewport interface id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface"/>
+ </request>
+ </interface>
+
+ <interface name="wp_viewport" version="1">
+ <description summary="crop and scale interface to a wl_surface">
+ An additional interface to a wl_surface object, which allows the
+ client to specify the cropping and scaling of the surface
+ contents.
+
+ This interface works with two concepts: the source rectangle (src_x,
+ src_y, src_width, src_height), and the destination size (dst_width,
+ dst_height). The contents of the source rectangle are scaled to the
+ destination size, and content outside the source rectangle is ignored.
+ This state is double-buffered, and is applied on the next
+ wl_surface.commit.
+
+ The two parts of crop and scale state are independent: the source
+ rectangle, and the destination size. Initially both are unset, that
+ is, no scaling is applied. The whole of the current wl_buffer is
+ used as the source, and the surface size is as defined in
+ wl_surface.attach.
+
+ If the destination size is set, it causes the surface size to become
+ dst_width, dst_height. The source (rectangle) is scaled to exactly
+ this size. This overrides whatever the attached wl_buffer size is,
+ unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
+ has no content and therefore no size. Otherwise, the size is always
+ at least 1x1 in surface local coordinates.
+
+ If the source rectangle is set, it defines what area of the wl_buffer is
+ taken as the source. If the source rectangle is set and the destination
+ size is not set, then src_width and src_height must be integers, and the
+ surface size becomes the source rectangle size. This results in cropping
+ without scaling. If src_width or src_height are not integers and
+ destination size is not set, the bad_size protocol error is raised when
+ the surface state is applied.
+
+ The coordinate transformations from buffer pixel coordinates up to
+ the surface-local coordinates happen in the following order:
+ 1. buffer_transform (wl_surface.set_buffer_transform)
+ 2. buffer_scale (wl_surface.set_buffer_scale)
+ 3. crop and scale (wp_viewport.set*)
+ This means, that the source rectangle coordinates of crop and scale
+ are given in the coordinates after the buffer transform and scale,
+ i.e. in the coordinates that would be the surface-local coordinates
+ if the crop and scale was not applied.
+
+ If src_x or src_y are negative, the bad_value protocol error is raised.
+ Otherwise, if the source rectangle is partially or completely outside of
+ the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
+ when the surface state is applied. A NULL wl_buffer does not raise the
+ out_of_buffer error.
+
+ The x, y arguments of wl_surface.attach are applied as normal to
+ the surface. They indicate how many pixels to remove from the
+ surface size from the left and the top. In other words, they are
+ still in the surface-local coordinate system, just like dst_width
+ and dst_height are.
+
+ If the wl_surface associated with the wp_viewport is destroyed,
+ all wp_viewport requests except 'destroy' raise the protocol error
+ no_surface.
+
+ If the wp_viewport object is destroyed, the crop and scale
+ state is removed from the wl_surface. The change will be applied
+ on the next wl_surface.commit.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove scaling and cropping from the surface">
+ The associated wl_surface's crop and scale state is removed.
+ The change is applied on the next wl_surface.commit.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="bad_value" value="0"
+ summary="negative or zero values in width or height"/>
+ <entry name="bad_size" value="1"
+ summary="destination size is not integer"/>
+ <entry name="out_of_buffer" value="2"
+ summary="source rectangle extends outside of the content area"/>
+ <entry name="no_surface" value="3"
+ summary="the wl_surface was destroyed"/>
+ </enum>
+
+ <request name="set_source">
+ <description summary="set the source rectangle for cropping">
+ Set the source rectangle of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If all of x, y, width and height are -1.0, the source rectangle is
+ unset instead. Any other set of values where width or height are zero
+ or negative, or x or y are negative, raise the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+ </description>
+ <arg name="x" type="fixed" summary="source rectangle x"/>
+ <arg name="y" type="fixed" summary="source rectangle y"/>
+ <arg name="width" type="fixed" summary="source rectangle width"/>
+ <arg name="height" type="fixed" summary="source rectangle height"/>
+ </request>
+
+ <request name="set_destination">
+ <description summary="set the surface size for scaling">
+ Set the destination size of the associated wl_surface. See
+ wp_viewport for the description, and relation to the wl_buffer
+ size.
+
+ If width is -1 and height is -1, the destination size is unset
+ instead. Any other pair of values for width and height that
+ contains zero or negative values raises the bad_value protocol
+ error.
+
+ The crop and scale state is double-buffered state, and will be
+ applied on the next wl_surface.commit.
+ </description>
+ <arg name="width" type="int" summary="surface width"/>
+ <arg name="height" type="int" summary="surface height"/>
+ </request>
+ </interface>
+
+</protocol>
diff --git a/src/3rdparty/protocol/wayland.xml b/src/3rdparty/protocol/wayland.xml
index bb457bcf7..141038b7a 100644
--- a/src/3rdparty/protocol/wayland.xml
+++ b/src/3rdparty/protocol/wayland.xml
@@ -6,26 +6,26 @@
Copyright © 2010-2011 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
- Permission to use, copy, modify, distribute, and sell this
- software and its documentation for any purpose is hereby granted
- without fee, provided that the above copyright notice appear in
- all copies and that both that copyright notice and this permission
- notice appear in supporting documentation, and that the name of
- the copyright holders not be used in advertising or publicity
- pertaining to distribution of the software without specific,
- written prior permission. The copyright holders make no
- representations about the suitability of this software for any
- purpose. It is provided "as is" without express or implied
- warranty.
-
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- THIS SOFTWARE.
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
</copyright>
<interface name="wl_display" version="1">
@@ -48,7 +48,8 @@
The callback_data passed in the callback is the event serial.
</description>
- <arg name="callback" type="new_id" interface="wl_callback"/>
+ <arg name="callback" type="new_id" interface="wl_callback"
+ summary="callback object for the sync request"/>
</request>
<request name="get_registry">
@@ -56,8 +57,15 @@
This request creates a registry object that allows the client
to list and bind the global objects available from the
compositor.
+
+ It should be noted that the server side resources consumed in
+ response to a get_registry request can only be released when the
+ client disconnects, not when the client side proxy is destroyed.
+ Therefore, clients should invoke get_registry as infrequently as
+ possible to avoid wasting memory.
</description>
- <arg name="registry" type="new_id" interface="wl_registry"/>
+ <arg name="registry" type="new_id" interface="wl_registry"
+ summary="global registry object"/>
</request>
<event name="error">
@@ -67,12 +75,12 @@
where the error occurred, most often in response to a request
to that object. The code identifies the error and is defined
by the object interface. As such, each interface defines its
- own set of error codes. The message is an brief description
+ own set of error codes. The message is a brief description
of the error, for (debugging) convenience.
</description>
- <arg name="object_id" type="object"/>
- <arg name="code" type="uint"/>
- <arg name="message" type="string"/>
+ <arg name="object_id" type="object" summary="object where the error occurred"/>
+ <arg name="code" type="uint" summary="error code"/>
+ <arg name="message" type="string" summary="error description"/>
</event>
<enum name="error">
@@ -93,17 +101,17 @@
This event is used internally by the object ID management
logic. When a client deletes an object, the server will send
this event to acknowledge that it has seen the delete request.
- When the client receive this event, it will know that it can
+ When the client receives this event, it will know that it can
safely reuse the object ID.
</description>
- <arg name="id" type="uint" />
+ <arg name="id" type="uint" summary="deleted object ID"/>
</event>
</interface>
<interface name="wl_registry" version="1">
<description summary="global registry object">
- The global registry object. The server has a number of global
- objects that are available to all clients. These objects
+ The singleton global registry object. The server has a number of
+ global objects that are available to all clients. These objects
typically represent an actual object in the server (for example,
an input device) or they are singleton objects that provide
extension functionality.
@@ -127,39 +135,39 @@
<request name="bind">
<description summary="bind an object to the display">
Binds a new, client-created object to the server using the
- specified name as the identifier.
+ specified name as the identifier.
</description>
- <arg name="name" type="uint" summary="unique name for the object"/>
- <arg name="id" type="new_id"/>
+ <arg name="name" type="uint" summary="unique numeric name of the object"/>
+ <arg name="id" type="new_id" summary="bounded object"/>
</request>
<event name="global">
<description summary="announce global object">
Notify the client of global objects.
- The event notifies the client that a global object with
- the given name is now available, and it implements the
- given version of the given interface.
+ The event notifies the client that a global object with
+ the given name is now available, and it implements the
+ given version of the given interface.
</description>
- <arg name="name" type="uint"/>
- <arg name="interface" type="string"/>
- <arg name="version" type="uint"/>
+ <arg name="name" type="uint" summary="numeric name of the global object"/>
+ <arg name="interface" type="string" summary="interface implemented by the object"/>
+ <arg name="version" type="uint" summary="interface version"/>
</event>
<event name="global_remove">
<description summary="announce removal of global object">
Notify the client of removed global objects.
- This event notifies the client that the global identified
- by name is no longer available. If the client bound to
- the global using the bind request, the client should now
- destroy that object.
+ This event notifies the client that the global identified
+ by name is no longer available. If the client bound to
+ the global using the bind request, the client should now
+ destroy that object.
The object remains valid and requests to the object will be
ignored until the client destroys it, to avoid races between
the global going away and a client sending a request to it.
</description>
- <arg name="name" type="uint"/>
+ <arg name="name" type="uint" summary="numeric name of the global object"/>
</event>
</interface>
@@ -168,15 +176,16 @@
Clients can handle the 'done' event to get notified when
the related request is done.
</description>
+
<event name="done">
<description summary="done event">
- Notify the client when the related request is done.
+ Notify the client when the related request is done.
</description>
- <arg name="callback_data" type="uint" summary="request-specific data for the wl_callback"/>
+ <arg name="callback_data" type="uint" summary="request-specific data for the callback"/>
</event>
</interface>
- <interface name="wl_compositor" version="3">
+ <interface name="wl_compositor" version="4">
<description summary="the compositor singleton">
A compositor. This object is a singleton global. The
compositor is in charge of combining the contents of multiple
@@ -187,14 +196,14 @@
<description summary="create new surface">
Ask the compositor to create a new surface.
</description>
- <arg name="id" type="new_id" interface="wl_surface"/>
+ <arg name="id" type="new_id" interface="wl_surface" summary="the new surface"/>
</request>
<request name="create_region">
<description summary="create new region">
Ask the compositor to create a new region.
</description>
- <arg name="id" type="new_id" interface="wl_region"/>
+ <arg name="id" type="new_id" interface="wl_region" summary="the new region"/>
</request>
</interface>
@@ -214,8 +223,8 @@
Create a wl_buffer object from the pool.
The buffer is created offset bytes into the pool and has
- width and height as specified. The stride arguments specifies
- the number of bytes from beginning of one row to the beginning
+ width and height as specified. The stride argument specifies
+ the number of bytes from the beginning of one row to the beginning
of the next. The format is the pixel format of the buffer and
must be one of those advertised through the wl_shm.format event.
@@ -223,13 +232,12 @@
so it is valid to destroy the pool immediately after creating
a buffer from it.
</description>
-
- <arg name="id" type="new_id" interface="wl_buffer"/>
- <arg name="offset" type="int"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
- <arg name="stride" type="int"/>
- <arg name="format" type="uint"/>
+ <arg name="id" type="new_id" interface="wl_buffer" summary="buffer to create"/>
+ <arg name="offset" type="int" summary="buffer byte offset within the pool"/>
+ <arg name="width" type="int" summary="buffer width, in pixels"/>
+ <arg name="height" type="int" summary="buffer height, in pixels"/>
+ <arg name="stride" type="int" summary="number of bytes from the beginning of one row to the beginning of the next row"/>
+ <arg name="format" type="uint" enum="wl_shm.format" summary="buffer pixel format"/>
</request>
<request name="destroy" type="destructor">
@@ -249,14 +257,13 @@
created, but using the new size. This request can only be
used to make the pool bigger.
</description>
-
- <arg name="size" type="int"/>
+ <arg name="size" type="int" summary="new size of the pool, in bytes"/>
</request>
</interface>
<interface name="wl_shm" version="1">
<description summary="shared memory support">
- A global singleton object that provides support for shared
+ A singleton global object that provides support for shared
memory.
Clients can create wl_shm_pool objects using the create_pool
@@ -278,73 +285,74 @@
<enum name="format">
<description summary="pixel formats">
- This describes the memory layout of an individual pixel.
-
- All renderers should support argb8888 and xrgb8888 but any other
- formats are optional and may not be supported by the particular
- renderer in use.
- </description>
- <entry name="argb8888" value="0" summary="32-bit ARGB format"/>
- <entry name="xrgb8888" value="1" summary="32-bit RGB format"/>
- <!-- The drm format codes match the #defines in drm_fourcc.h.
- The formats actually supported by the compositor will be
- reported by the format event. -->
- <entry name="c8" value="0x20203843"/>
- <entry name="rgb332" value="0x38424752"/>
- <entry name="bgr233" value="0x38524742"/>
- <entry name="xrgb4444" value="0x32315258"/>
- <entry name="xbgr4444" value="0x32314258"/>
- <entry name="rgbx4444" value="0x32315852"/>
- <entry name="bgrx4444" value="0x32315842"/>
- <entry name="argb4444" value="0x32315241"/>
- <entry name="abgr4444" value="0x32314241"/>
- <entry name="rgba4444" value="0x32314152"/>
- <entry name="bgra4444" value="0x32314142"/>
- <entry name="xrgb1555" value="0x35315258"/>
- <entry name="xbgr1555" value="0x35314258"/>
- <entry name="rgbx5551" value="0x35315852"/>
- <entry name="bgrx5551" value="0x35315842"/>
- <entry name="argb1555" value="0x35315241"/>
- <entry name="abgr1555" value="0x35314241"/>
- <entry name="rgba5551" value="0x35314152"/>
- <entry name="bgra5551" value="0x35314142"/>
- <entry name="rgb565" value="0x36314752"/>
- <entry name="bgr565" value="0x36314742"/>
- <entry name="rgb888" value="0x34324752"/>
- <entry name="bgr888" value="0x34324742"/>
- <entry name="xbgr8888" value="0x34324258"/>
- <entry name="rgbx8888" value="0x34325852"/>
- <entry name="bgrx8888" value="0x34325842"/>
- <entry name="abgr8888" value="0x34324241"/>
- <entry name="rgba8888" value="0x34324152"/>
- <entry name="bgra8888" value="0x34324142"/>
- <entry name="xrgb2101010" value="0x30335258"/>
- <entry name="xbgr2101010" value="0x30334258"/>
- <entry name="rgbx1010102" value="0x30335852"/>
- <entry name="bgrx1010102" value="0x30335842"/>
- <entry name="argb2101010" value="0x30335241"/>
- <entry name="abgr2101010" value="0x30334241"/>
- <entry name="rgba1010102" value="0x30334152"/>
- <entry name="bgra1010102" value="0x30334142"/>
- <entry name="yuyv" value="0x56595559"/>
- <entry name="yvyu" value="0x55595659"/>
- <entry name="uyvy" value="0x59565955"/>
- <entry name="vyuy" value="0x59555956"/>
- <entry name="ayuv" value="0x56555941"/>
- <entry name="nv12" value="0x3231564e"/>
- <entry name="nv21" value="0x3132564e"/>
- <entry name="nv16" value="0x3631564e"/>
- <entry name="nv61" value="0x3136564e"/>
- <entry name="yuv410" value="0x39565559"/>
- <entry name="yvu410" value="0x39555659"/>
- <entry name="yuv411" value="0x31315559"/>
- <entry name="yvu411" value="0x31315659"/>
- <entry name="yuv420" value="0x32315559"/>
- <entry name="yvu420" value="0x32315659"/>
- <entry name="yuv422" value="0x36315559"/>
- <entry name="yvu422" value="0x36315659"/>
- <entry name="yuv444" value="0x34325559"/>
- <entry name="yvu444" value="0x34325659"/>
+ This describes the memory layout of an individual pixel.
+
+ All renderers should support argb8888 and xrgb8888 but any other
+ formats are optional and may not be supported by the particular
+ renderer in use.
+
+ The drm format codes match the macros defined in drm_fourcc.h.
+ The formats actually supported by the compositor will be
+ reported by the format event.
+ </description>
+ <entry name="argb8888" value="0" summary="32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian"/>
+ <entry name="xrgb8888" value="1" summary="32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian"/>
+ <entry name="c8" value="0x20203843" summary="8-bit color index format, [7:0] C"/>
+ <entry name="rgb332" value="0x38424752" summary="8-bit RGB format, [7:0] R:G:B 3:3:2"/>
+ <entry name="bgr233" value="0x38524742" summary="8-bit BGR format, [7:0] B:G:R 2:3:3"/>
+ <entry name="xrgb4444" value="0x32315258" summary="16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian"/>
+ <entry name="xbgr4444" value="0x32314258" summary="16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian"/>
+ <entry name="rgbx4444" value="0x32315852" summary="16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian"/>
+ <entry name="bgrx4444" value="0x32315842" summary="16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian"/>
+ <entry name="argb4444" value="0x32315241" summary="16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian"/>
+ <entry name="abgr4444" value="0x32314241" summary="16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian"/>
+ <entry name="rgba4444" value="0x32314152" summary="16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian"/>
+ <entry name="bgra4444" value="0x32314142" summary="16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian"/>
+ <entry name="xrgb1555" value="0x35315258" summary="16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian"/>
+ <entry name="xbgr1555" value="0x35314258" summary="16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian"/>
+ <entry name="rgbx5551" value="0x35315852" summary="16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian"/>
+ <entry name="bgrx5551" value="0x35315842" summary="16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian"/>
+ <entry name="argb1555" value="0x35315241" summary="16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian"/>
+ <entry name="abgr1555" value="0x35314241" summary="16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian"/>
+ <entry name="rgba5551" value="0x35314152" summary="16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian"/>
+ <entry name="bgra5551" value="0x35314142" summary="16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian"/>
+ <entry name="rgb565" value="0x36314752" summary="16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian"/>
+ <entry name="bgr565" value="0x36314742" summary="16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian"/>
+ <entry name="rgb888" value="0x34324752" summary="24-bit RGB format, [23:0] R:G:B little endian"/>
+ <entry name="bgr888" value="0x34324742" summary="24-bit BGR format, [23:0] B:G:R little endian"/>
+ <entry name="xbgr8888" value="0x34324258" summary="32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian"/>
+ <entry name="rgbx8888" value="0x34325852" summary="32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian"/>
+ <entry name="bgrx8888" value="0x34325842" summary="32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian"/>
+ <entry name="abgr8888" value="0x34324241" summary="32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian"/>
+ <entry name="rgba8888" value="0x34324152" summary="32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian"/>
+ <entry name="bgra8888" value="0x34324142" summary="32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian"/>
+ <entry name="xrgb2101010" value="0x30335258" summary="32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian"/>
+ <entry name="xbgr2101010" value="0x30334258" summary="32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian"/>
+ <entry name="rgbx1010102" value="0x30335852" summary="32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian"/>
+ <entry name="bgrx1010102" value="0x30335842" summary="32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian"/>
+ <entry name="argb2101010" value="0x30335241" summary="32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian"/>
+ <entry name="abgr2101010" value="0x30334241" summary="32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian"/>
+ <entry name="rgba1010102" value="0x30334152" summary="32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian"/>
+ <entry name="bgra1010102" value="0x30334142" summary="32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian"/>
+ <entry name="yuyv" value="0x56595559" summary="packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian"/>
+ <entry name="yvyu" value="0x55595659" summary="packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian"/>
+ <entry name="uyvy" value="0x59565955" summary="packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian"/>
+ <entry name="vyuy" value="0x59555956" summary="packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian"/>
+ <entry name="ayuv" value="0x56555941" summary="packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian"/>
+ <entry name="nv12" value="0x3231564e" summary="2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane"/>
+ <entry name="nv21" value="0x3132564e" summary="2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane"/>
+ <entry name="nv16" value="0x3631564e" summary="2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane"/>
+ <entry name="nv61" value="0x3136564e" summary="2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane"/>
+ <entry name="yuv410" value="0x39565559" summary="3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes"/>
+ <entry name="yvu410" value="0x39555659" summary="3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes"/>
+ <entry name="yuv411" value="0x31315559" summary="3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes"/>
+ <entry name="yvu411" value="0x31315659" summary="3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes"/>
+ <entry name="yuv420" value="0x32315559" summary="3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes"/>
+ <entry name="yvu420" value="0x32315659" summary="3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes"/>
+ <entry name="yuv422" value="0x36315559" summary="3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes"/>
+ <entry name="yvu422" value="0x36315659" summary="3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes"/>
+ <entry name="yuv444" value="0x34325559" summary="3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes"/>
+ <entry name="yvu444" value="0x34325659" summary="3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes"/>
</enum>
<request name="create_pool">
@@ -353,12 +361,11 @@
The pool can be used to create shared memory based buffer
objects. The server will mmap size bytes of the passed file
- descriptor, to use as backing memory for the pool.
+ descriptor, to use as backing memory for the pool.
</description>
-
- <arg name="id" type="new_id" interface="wl_shm_pool"/>
- <arg name="fd" type="fd"/>
- <arg name="size" type="int"/>
+ <arg name="id" type="new_id" interface="wl_shm_pool" summary="pool to create"/>
+ <arg name="fd" type="fd" summary="file descriptor for the pool"/>
+ <arg name="size" type="int" summary="pool size, in bytes"/>
</request>
<event name="format">
@@ -367,7 +374,7 @@
can be used for buffers. Known formats include
argb8888 and xrgb8888.
</description>
- <arg name="format" type="uint"/>
+ <arg name="format" type="uint" enum="format" summary="buffer pixel format"/>
</event>
</interface>
@@ -392,13 +399,13 @@
<event name="release">
<description summary="compositor releases buffer">
Sent when this wl_buffer is no longer used by the compositor.
- The client is now free to re-use or destroy this buffer and its
+ The client is now free to reuse or destroy this buffer and its
backing storage.
If a client receives a release event before the frame callback
requested in the same wl_surface.commit that attaches this
wl_buffer to a surface, then the client is immediately free to
- re-use the buffer and its backing storage, and does not need a
+ reuse the buffer and its backing storage, and does not need a
second buffer for the next surface content update. Typically
this is possible, when the compositor maintains a copy of the
wl_surface contents, e.g. as a GL texture. This is an important
@@ -407,8 +414,7 @@
</event>
</interface>
-
- <interface name="wl_data_offer" version="1">
+ <interface name="wl_data_offer" version="3">
<description summary="offer to transfer data">
A wl_data_offer represents a piece of data offered for transfer
by another client (the source client). It is used by the
@@ -418,16 +424,36 @@
data directly from the source client.
</description>
+ <enum name="error">
+ <entry name="invalid_finish" value="0"
+ summary="finish request was called untimely"/>
+ <entry name="invalid_action_mask" value="1"
+ summary="action mask contains invalid values"/>
+ <entry name="invalid_action" value="2"
+ summary="action argument has an invalid value"/>
+ <entry name="invalid_offer" value="3"
+ summary="offer doesn't accept this request"/>
+ </enum>
+
<request name="accept">
<description summary="accept one of the offered mime types">
Indicate that the client can accept the given mime type, or
NULL for not accepted.
- Used for feedback during drag-and-drop.
- </description>
+ For objects of version 2 or older, this request is used by the
+ client to give feedback whether the client can receive the given
+ mime type, or NULL if none is accepted; the feedback does not
+ determine whether the drag-and-drop operation succeeds or not.
- <arg name="serial" type="uint"/>
- <arg name="mime_type" type="string" allow-null="true"/>
+ For objects of version 3 or newer, this request determines the
+ final result of the drag-and-drop operation. If the end result
+ is that no mime types were accepted, the drag-and-drop operation
+ will be cancelled and the corresponding drag source will receive
+ wl_data_source.cancelled. Clients may still use this event in
+ conjunction with wl_data_source.action for feedback.
+ </description>
+ <arg name="serial" type="uint" summary="serial number of the accept request"/>
+ <arg name="mime_type" type="string" allow-null="true" summary="mime type accepted by the client"/>
</request>
<request name="receive">
@@ -440,11 +466,16 @@
file descriptor.
The receiving client reads from the read end of the pipe until
- EOF and the closes its end, at which point the transfer is
+ EOF and then closes its end, at which point the transfer is
complete.
+
+ This request may happen multiple times for different mime types,
+ both before and after wl_data_device.drop. Drag-and-drop destination
+ clients may preemptively fetch data or examine it more closely to
+ determine acceptance.
</description>
- <arg name="mime_type" type="string"/>
- <arg name="fd" type="fd"/>
+ <arg name="mime_type" type="string" summary="mime type desired by receiver"/>
+ <arg name="fd" type="fd" summary="file descriptor for data transfer"/>
</request>
<request name="destroy" type="destructor">
@@ -458,12 +489,117 @@
Sent immediately after creating the wl_data_offer object. One
event per offered mime type.
</description>
+ <arg name="mime_type" type="string" summary="offered mime type"/>
+ </event>
+
+ <!-- Version 3 additions -->
+
+ <request name="finish" since="3">
+ <description summary="the offer will no longer be used">
+ Notifies the compositor that the drag destination successfully
+ finished the drag-and-drop operation.
+
+ Upon receiving this request, the compositor will emit
+ wl_data_source.dnd_finished on the drag source client.
+
+ It is a client error to perform other requests than
+ wl_data_offer.destroy after this one. It is also an error to perform
+ this request after a NULL mime type has been set in
+ wl_data_offer.accept or no action was received through
+ wl_data_offer.action.
+ </description>
+ </request>
+
+ <request name="set_actions" since="3">
+ <description summary="set the available/preferred drag-and-drop actions">
+ Sets the actions that the destination side client supports for
+ this operation. This request may trigger the emission of
+ wl_data_source.action and wl_data_offer.action events if the compositor
+ needs to change the selected action.
+
+ This request can be called multiple times throughout the
+ drag-and-drop operation, typically in response to wl_data_device.enter
+ or wl_data_device.motion events.
+
+ This request determines the final result of the drag-and-drop
+ operation. If the end result is that no action is accepted,
+ the drag source will receive wl_drag_source.cancelled.
+
+ The dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, and the preferred_action
+ argument must only contain one of those values set, otherwise it
+ will result in a protocol error.
+
+ While managing an "ask" action, the destination drag-and-drop client
+ may perform further wl_data_offer.receive requests, and is expected
+ to perform one last wl_data_offer.set_actions request with a preferred
+ action other than "ask" (and optionally wl_data_offer.accept) before
+ requesting wl_data_offer.finish, in order to convey the action selected
+ by the user. If the preferred action is not in the
+ wl_data_offer.source_actions mask, an error will be raised.
+
+ If the "ask" action is dismissed (e.g. user cancellation), the client
+ is expected to perform wl_data_offer.destroy right away.
+
+ This request can only be made on drag-and-drop offers, a protocol error
+ will be raised otherwise.
+ </description>
+ <arg name="dnd_actions" type="uint" summary="actions supported by the destination client"/>
+ <arg name="preferred_action" type="uint" summary="action preferred by the destination client"/>
+ </request>
+
+ <event name="source_actions" since="3">
+ <description summary="notify the source-side available actions">
+ This event indicates the actions offered by the data source. It
+ will be sent right after wl_data_device.enter, or anytime the source
+ side changes its offered actions through wl_data_source.set_actions.
+ </description>
+ <arg name="source_actions" type="uint" summary="actions offered by the data source"/>
+ </event>
- <arg name="mime_type" type="string"/>
+ <event name="action" since="3">
+ <description summary="notify the selected action">
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation in response to destination side action changes through
+ wl_data_offer.set_actions.
+
+ This event will no longer be emitted after wl_data_device.drop
+ happened on the drag-and-drop destination, the client must
+ honor the last action received, or the last preferred one set
+ through wl_data_offer.set_actions when handling an "ask" action.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. Prior to
+ receiving wl_data_device.drop, the chosen action may change (e.g.
+ due to keyboard modifiers being pressed). At the time of receiving
+ wl_data_device.drop the drag-and-drop destination must honor the
+ last action received.
+
+ Action changes may still happen after wl_data_device.drop,
+ especially on "ask" actions, where the drag-and-drop destination
+ may choose another action afterwards. Action changes happening
+ at this stage are always the result of inter-client negotiation, the
+ compositor shall no longer be able to induce a different action.
+
+ Upon "ask" actions, it is expected that the drag-and-drop destination
+ may potentially choose a different action and/or mime type,
+ based on wl_data_offer.source_actions and finally chosen by the
+ user (e.g. popping up a menu with the available options). The
+ final wl_data_offer.set_actions and wl_data_offer.accept requests
+ must happen before the call to wl_data_offer.finish.
+ </description>
+ <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
</event>
</interface>
- <interface name="wl_data_source" version="1">
+ <interface name="wl_data_source" version="3">
<description summary="offer to transfer data">
The wl_data_source object is the source side of a wl_data_offer.
It is created by the source client in a data transfer and
@@ -471,13 +607,20 @@
to requests to transfer the data.
</description>
+ <enum name="error">
+ <entry name="invalid_action_mask" value="0"
+ summary="action mask contains invalid values"/>
+ <entry name="invalid_source" value="1"
+ summary="source doesn't accept this request"/>
+ </enum>
+
<request name="offer">
<description summary="add an offered mime type">
This request adds a mime type to the set of mime types
advertised to targets. Can be called several times to offer
multiple types.
</description>
- <arg name="mime_type" type="string"/>
+ <arg name="mime_type" type="string" summary="mime type offered by the data source"/>
</request>
<request name="destroy" type="destructor">
@@ -493,8 +636,7 @@
Used for feedback during drag-and-drop.
</description>
-
- <arg name="mime_type" type="string" allow-null="true"/>
+ <arg name="mime_type" type="string" allow-null="true" summary="mime type accepted by the target"/>
</event>
<event name="send">
@@ -503,21 +645,114 @@
specified mime type over the passed file descriptor, then
close it.
</description>
-
- <arg name="mime_type" type="string"/>
- <arg name="fd" type="fd"/>
+ <arg name="mime_type" type="string" summary="mime type for the data"/>
+ <arg name="fd" type="fd" summary="file descriptor for the data"/>
</event>
<event name="cancelled">
<description summary="selection was cancelled">
- This data source has been replaced by another data source.
+ This data source is no longer valid. There are several reasons why
+ this could happen:
+
+ - The data source has been replaced by another data source.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not accept any of the mime types offered through
+ wl_data_source.target.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not select any of the actions present in the mask offered through
+ wl_data_source.action.
+ - The drag-and-drop operation was performed but didn't happen over a
+ surface.
+ - The compositor cancelled the drag-and-drop operation (e.g. compositor
+ dependent timeouts to avoid stale drag-and-drop transfers).
+
The client should clean up and destroy this data source.
+
+ For objects of version 2 or older, wl_data_source.cancelled will
+ only be emitted if the data source was replaced by another data
+ source.
</description>
</event>
+ <!-- Version 3 additions -->
+
+ <request name="set_actions" since="3">
+ <description summary="set the available drag-and-drop actions">
+ Sets the actions that the source side client supports for this
+ operation. This request may trigger wl_data_source.action and
+ wl_data_offer.action events if the compositor needs to change the
+ selected action.
+
+ The dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, otherwise it will result
+ in a protocol error.
+
+ This request must be made once only, and can only be made on sources
+ used in drag-and-drop, so it must be performed before
+ wl_data_device.start_drag. Attempting to use the source other than
+ for drag-and-drop will raise a protocol error.
+ </description>
+ <arg name="dnd_actions" type="uint" summary="actions supported by the data source"/>
+ </request>
+
+ <event name="dnd_drop_performed" since="3">
+ <description summary="the drag-and-drop operation physically finished">
+ The user performed the drop action. This event does not indicate
+ acceptance, wl_data_source.cancelled may still be emitted afterwards
+ if the drop destination does not accept any mime type.
+
+ However, this event might however not be received if the compositor
+ cancelled the drag-and-drop operation before this event could happen.
+
+ Note that the data_source may still be used in the future and should
+ not be destroyed here.
+ </description>
+ </event>
+
+ <event name="dnd_finished" since="3">
+ <description summary="the drag-and-drop operation concluded">
+ The drop destination finished interoperating with this data
+ source, so the client is now free to destroy this data source and
+ free all associated data.
+
+ If the action used to perform the operation was "move", the
+ source can now delete the transferred data.
+ </description>
+ </event>
+
+ <event name="action" since="3">
+ <description summary="notify the selected action">
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation, mainly in response to destination side changes through
+ wl_data_offer.set_actions, and as the data device enters/leaves
+ surfaces.
+
+ It is only possible to receive this event after
+ wl_data_source.dnd_drop_performed if the drag-and-drop operation
+ ended in an "ask" action, in which case the final wl_data_source.action
+ event will happen immediately before wl_data_source.dnd_finished.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. The chosen
+ action may change alongside negotiation (e.g. an "ask" action can turn
+ into a "move" operation), so the effects of the final action must
+ always be applied in wl_data_offer.dnd_finished.
+
+ Clients can trigger cursor surface changes from this point, so
+ they reflect the current action.
+ </description>
+ <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
+ </event>
</interface>
- <interface name="wl_data_device" version="1">
+ <interface name="wl_data_device" version="3">
<description summary="data transfer device">
There is one wl_data_device per seat which can be obtained
from the global wl_data_device_manager singleton.
@@ -525,6 +760,11 @@
A wl_data_device provides access to inter-client data transfer
mechanisms such as copy-and-paste and drag-and-drop.
</description>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ </enum>
+
<request name="start_drag">
<description summary="start drag-and-drop operation">
This request asks the compositor to start a drag-and-drop
@@ -545,7 +785,9 @@
the top-left corner of the icon surface is placed at the cursor
hotspot, but subsequent wl_surface.attach request can move the
relative position. Attach requests must be confirmed with
- wl_surface.commit as usual.
+ wl_surface.commit as usual. The icon surface is given the role of
+ a drag-and-drop icon. If the icon surface already has another role,
+ it raises a protocol error.
The current and pending input regions of the icon wl_surface are
cleared, and wl_surface.set_input_region is ignored until the
@@ -553,10 +795,10 @@
as an icon ends, the current and pending input regions become
undefined, and the wl_surface is unmapped.
</description>
- <arg name="source" type="object" interface="wl_data_source" allow-null="true"/>
- <arg name="origin" type="object" interface="wl_surface"/>
- <arg name="icon" type="object" interface="wl_surface" allow-null="true"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the origin"/>
+ <arg name="source" type="object" interface="wl_data_source" allow-null="true" summary="data source for the eventual transfer"/>
+ <arg name="origin" type="object" interface="wl_surface" summary="surface where the drag originates"/>
+ <arg name="icon" type="object" interface="wl_surface" allow-null="true" summary="drag-and-drop icon surface"/>
+ <arg name="serial" type="uint" summary="serial number of the implicit grab on the origin"/>
</request>
<request name="set_selection">
@@ -566,8 +808,8 @@
To unset the selection, set the source to NULL.
</description>
- <arg name="source" type="object" interface="wl_data_source" allow-null="true"/>
- <arg name="serial" type="uint" summary="serial of the event that triggered this request"/>
+ <arg name="source" type="object" interface="wl_data_source" allow-null="true" summary="data source for the selection"/>
+ <arg name="serial" type="uint" summary="serial number of the event that triggered this request"/>
</request>
<event name="data_offer">
@@ -580,23 +822,22 @@
object will send out data_offer.offer events to describe the
mime types it offers.
</description>
-
- <arg name="id" type="new_id" interface="wl_data_offer"/>
+ <arg name="id" type="new_id" interface="wl_data_offer" summary="the new data_offer object"/>
</event>
<event name="enter">
<description summary="initiate drag-and-drop session">
This event is sent when an active drag-and-drop pointer enters
a surface owned by the client. The position of the pointer at
- enter time is provided by the x and y arguments, in surface
- local coordinates.
+ enter time is provided by the x and y arguments, in surface-local
+ coordinates.
</description>
-
- <arg name="serial" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- <arg name="x" type="fixed"/>
- <arg name="y" type="fixed"/>
- <arg name="id" type="object" interface="wl_data_offer" allow-null="true"/>
+ <arg name="serial" type="uint" summary="serial number of the enter event"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="client surface entered"/>
+ <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="y" type="fixed" summary="surface-local y coordinate"/>
+ <arg name="id" type="object" interface="wl_data_offer" allow-null="true"
+ summary="source data_offer object"/>
</event>
<event name="leave">
@@ -611,18 +852,29 @@
<description summary="drag-and-drop session motion">
This event is sent when the drag-and-drop pointer moves within
the currently focused surface. The new position of the pointer
- is provided by the x and y arguments, in surface local
+ is provided by the x and y arguments, in surface-local
coordinates.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="x" type="fixed"/>
- <arg name="y" type="fixed"/>
+ <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="y" type="fixed" summary="surface-local y coordinate"/>
</event>
<event name="drop">
- <description summary="end drag-and-drag session successfully">
+ <description summary="end drag-and-drop session successfully">
The event is sent when a drag-and-drop operation is ended
because the implicit grab is removed.
+
+ The drag-and-drop destination is expected to honor the last action
+ received through wl_data_offer.action, if the resulting action is
+ "copy" or "move", the destination can still perform
+ wl_data_offer.receive requests, and is expected to end all
+ transfers with a wl_data_offer.finish request.
+
+ If the resulting action is "ask", the action will not be considered
+ final. The drag-and-drop destination is expected to perform one last
+ wl_data_offer.set_actions request, or wl_data_offer.destroy in order
+ to cancel the operation.
</description>
</event>
@@ -636,35 +888,85 @@
immediately before receiving keyboard focus and when a new
selection is set while the client has keyboard focus. The
data_offer is valid until a new data_offer or NULL is received
- or until the client loses keyboard focus.
+ or until the client loses keyboard focus. The client must
+ destroy the previous selection data_offer, if any, upon receiving
+ this event.
</description>
- <arg name="id" type="object" interface="wl_data_offer" allow-null="true"/>
+ <arg name="id" type="object" interface="wl_data_offer" allow-null="true"
+ summary="selection data_offer object"/>
</event>
+
+ <!-- Version 2 additions -->
+
+ <request name="release" type="destructor" since="2">
+ <description summary="destroy data device">
+ This request destroys the data device.
+ </description>
+ </request>
</interface>
- <interface name="wl_data_device_manager" version="1">
+ <interface name="wl_data_device_manager" version="3">
<description summary="data transfer interface">
The wl_data_device_manager is a singleton global object that
provides access to inter-client data transfer mechanisms such as
copy-and-paste and drag-and-drop. These mechanisms are tied to
a wl_seat and this interface lets a client get a wl_data_device
corresponding to a wl_seat.
+
+ Depending on the version bound, the objects created from the bound
+ wl_data_device_manager object will have different requirements for
+ functioning properly. See wl_data_source.set_actions,
+ wl_data_offer.accept and wl_data_offer.finish for details.
</description>
<request name="create_data_source">
<description summary="create a new data source">
- Create a new data source.
+ Create a new data source.
</description>
- <arg name="id" type="new_id" interface="wl_data_source"/>
+ <arg name="id" type="new_id" interface="wl_data_source" summary="data source to create"/>
</request>
<request name="get_data_device">
<description summary="create a new data device">
- Create a new data device for a given seat.
+ Create a new data device for a given seat.
</description>
- <arg name="id" type="new_id" interface="wl_data_device"/>
- <arg name="seat" type="object" interface="wl_seat"/>
+ <arg name="id" type="new_id" interface="wl_data_device" summary="data device to create"/>
+ <arg name="seat" type="object" interface="wl_seat" summary="seat associated with the data device"/>
</request>
+
+ <!-- Version 3 additions -->
+
+ <enum name="dnd_action" bitfield="true" since="3">
+ <description summary="drag and drop actions">
+ This is a bitmask of the available/preferred actions in a
+ drag-and-drop operation.
+
+ In the compositor, the selected action is a result of matching the
+ actions offered by the source and destination sides. "action" events
+ with a "none" action will be sent to both source and destination if
+ there is no match. All further checks will effectively happen on
+ (source actions ∩ destination actions).
+
+ In addition, compositors may also pick different actions in
+ reaction to key modifiers being pressed. One common design that
+ is used in major toolkits (and the behavior recommended for
+ compositors) is:
+
+ - If no modifiers are pressed, the first match (in bit order)
+ will be used.
+ - Pressing Shift selects "move", if enabled in the mask.
+ - Pressing Control selects "copy", if enabled in the mask.
+
+ Behavior beyond that is considered implementation-dependent.
+ Compositors may for example bind other modifiers (like Alt/Meta)
+ or drags initiated with other buttons than BTN_LEFT to specific
+ actions (e.g. "ask").
+ </description>
+ <entry name="none" value="0" summary="no action"/>
+ <entry name="copy" value="1" summary="copy action"/>
+ <entry name="move" value="2" summary="move action"/>
+ <entry name="ask" value="4" summary="ask action"/>
+ </enum>
</interface>
<interface name="wl_shell" version="1">
@@ -674,21 +976,29 @@
It allows clients to associate a wl_shell_surface with
a basic surface.
+
+ Note! This protocol is deprecated and not intended for production use.
+ For desktop-style user interfaces, use xdg_shell.
</description>
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ </enum>
+
<request name="get_shell_surface">
<description summary="create a shell surface from a surface">
- Create a shell surface for an existing surface.
+ Create a shell surface for an existing surface. This gives
+ the wl_surface the role of a shell surface. If the wl_surface
+ already has another role, it raises a protocol error.
Only one shell surface can be associated with a given surface.
</description>
- <arg name="id" type="new_id" interface="wl_shell_surface"/>
- <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="id" type="new_id" interface="wl_shell_surface" summary="shell surface to create"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface to be given the shell surface role"/>
</request>
</interface>
<interface name="wl_shell_surface" version="1">
-
<description summary="desktop-style metadata interface">
An interface that may be implemented by a wl_surface, for
implementations that provide a desktop-style user interface.
@@ -698,7 +1008,7 @@
metadata like title and class, etc.
On the server side the object is automatically destroyed when
- the related wl_surface is destroyed. On client side,
+ the related wl_surface is destroyed. On the client side,
wl_shell_surface_destroy() must be called before destroying
the wl_surface object.
</description>
@@ -708,7 +1018,7 @@
A client must respond to a ping event with a pong request or
the client may be deemed unresponsive.
</description>
- <arg name="serial" type="uint" summary="serial of the ping event"/>
+ <arg name="serial" type="uint" summary="serial number of the ping event"/>
</request>
<request name="move">
@@ -719,26 +1029,26 @@
The server may ignore move requests depending on the state of
the surface (e.g. fullscreen or maximized).
</description>
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
+ <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+ <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
</request>
- <enum name="resize">
+ <enum name="resize" bitfield="true">
<description summary="edge values for resizing">
These values are used to indicate which edge of a surface
is being dragged in a resize operation. The server may
use this information to adapt its behavior, e.g. choose
an appropriate cursor image.
</description>
- <entry name="none" value="0"/>
- <entry name="top" value="1"/>
- <entry name="bottom" value="2"/>
- <entry name="left" value="4"/>
- <entry name="top_left" value="5"/>
- <entry name="bottom_left" value="6"/>
- <entry name="right" value="8"/>
- <entry name="top_right" value="9"/>
- <entry name="bottom_right" value="10"/>
+ <entry name="none" value="0" summary="no edge"/>
+ <entry name="top" value="1" summary="top edge"/>
+ <entry name="bottom" value="2" summary="bottom edge"/>
+ <entry name="left" value="4" summary="left edge"/>
+ <entry name="top_left" value="5" summary="top and left edges"/>
+ <entry name="bottom_left" value="6" summary="bottom and left edges"/>
+ <entry name="right" value="8" summary="right edge"/>
+ <entry name="top_right" value="9" summary="top and right edges"/>
+ <entry name="bottom_right" value="10" summary="bottom and right edges"/>
</enum>
<request name="resize">
@@ -749,9 +1059,9 @@
The server may ignore resize requests depending on the state of
the surface (e.g. fullscreen or maximized).
</description>
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
+ <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+ <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
+ <arg name="edges" type="uint" enum="resize" summary="which edge or corner is being dragged"/>
</request>
<request name="set_toplevel">
@@ -762,7 +1072,7 @@
</description>
</request>
- <enum name="transient">
+ <enum name="transient" bitfield="true">
<description summary="details of transient behaviour">
These flags specify details of the expected behaviour
of transient surfaces. Used in the set_transient request.
@@ -774,17 +1084,16 @@
<description summary="make the surface a transient surface">
Map the surface relative to an existing surface.
- The x and y arguments specify the locations of the upper left
+ The x and y arguments specify the location of the upper left
corner of the surface relative to the upper left corner of the
- parent surface, in surface local coordinates.
+ parent surface, in surface-local coordinates.
The flags argument controls details of the transient behaviour.
</description>
-
- <arg name="parent" type="object" interface="wl_surface"/>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="flags" type="uint"/>
+ <arg name="parent" type="object" interface="wl_surface" summary="parent surface"/>
+ <arg name="x" type="int" summary="surface-local x coordinate"/>
+ <arg name="y" type="int" summary="surface-local y coordinate"/>
+ <arg name="flags" type="uint" enum="transient" summary="transient surface behavior"/>
</request>
<enum name="fullscreen_method">
@@ -815,7 +1124,7 @@
The framerate parameter is used only when the method is set
to "driver", to indicate the preferred framerate. A value of 0
- indicates that the app does not care about framerate. The
+ indicates that the client does not care about framerate. The
framerate is specified in mHz, that is framerate of 60000 is 60Hz.
A method of "scale" or "driver" implies a scaling operation of
@@ -835,9 +1144,10 @@
with the dimensions for the output on which the surface will
be made fullscreen.
</description>
- <arg name="method" type="uint"/>
- <arg name="framerate" type="uint"/>
- <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="method" type="uint" enum="fullscreen_method" summary="method for resolving size conflict"/>
+ <arg name="framerate" type="uint" summary="framerate in mHz"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"
+ summary="output on which the surface is to be fullscreen"/>
</request>
<request name="set_popup">
@@ -853,22 +1163,21 @@
be unmapped).
The popup grab continues until the window is destroyed or a
- mouse button is pressed in any other clients window. A click
- in any of the clients surfaces is reported as normal, however,
- clicks in other clients surfaces will be discarded and trigger
+ mouse button is pressed in any other client's window. A click
+ in any of the client's surfaces is reported as normal, however,
+ clicks in other clients' surfaces will be discarded and trigger
the callback.
- The x and y arguments specify the locations of the upper left
+ The x and y arguments specify the location of the upper left
corner of the surface relative to the upper left corner of the
- parent surface, in surface local coordinates.
- </description>
-
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- <arg name="parent" type="object" interface="wl_surface"/>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="flags" type="uint"/>
+ parent surface, in surface-local coordinates.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+ <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
+ <arg name="parent" type="object" interface="wl_surface" summary="parent surface"/>
+ <arg name="x" type="int" summary="surface-local x coordinate"/>
+ <arg name="y" type="int" summary="surface-local y coordinate"/>
+ <arg name="flags" type="uint" enum="transient" summary="transient surface behavior"/>
</request>
<request name="set_maximized">
@@ -886,13 +1195,14 @@
on the next buffer attach to this surface.
A maximized surface typically fills the entire output it is
- bound to, except for desktop element such as panels. This is
+ bound to, except for desktop elements such as panels. This is
the main difference between a maximized shell surface and a
fullscreen shell surface.
The details depend on the compositor implementation.
</description>
- <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"
+ summary="output on which the surface is to be maximized"/>
</request>
<request name="set_title">
@@ -905,7 +1215,7 @@
The string must be encoded in UTF-8.
</description>
- <arg name="title" type="string"/>
+ <arg name="title" type="string" summary="surface title"/>
</request>
<request name="set_class">
@@ -917,7 +1227,7 @@
file name (or the full path if it is a non-standard location) of
the application's .desktop file as the class.
</description>
- <arg name="class_" type="string"/>
+ <arg name="class_" type="string" summary="surface class"/>
</request>
<event name="ping">
@@ -925,7 +1235,7 @@
Ping a client to check if it is receiving events and sending
requests. A client is expected to reply with a pong request.
</description>
- <arg name="serial" type="uint"/>
+ <arg name="serial" type="uint" summary="serial number of the ping"/>
</event>
<event name="configure">
@@ -946,12 +1256,11 @@
event it received.
The width and height arguments specify the size of the window
- in surface local coordinates.
+ in surface-local coordinates.
</description>
-
- <arg name="edges" type="uint"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
+ <arg name="edges" type="uint" enum="resize" summary="how the surface was resized"/>
+ <arg name="width" type="int" summary="new width of the surface"/>
+ <arg name="height" type="int" summary="new height of the surface"/>
</event>
<event name="popup_done">
@@ -963,23 +1272,52 @@
</event>
</interface>
- <interface name="wl_surface" version="3">
+ <interface name="wl_surface" version="4">
<description summary="an onscreen surface">
A surface is a rectangular area that is displayed on the screen.
It has a location, size and pixel contents.
The size of a surface (and relative positions on it) is described
- in surface local coordinates, which may differ from the buffer
- local coordinates of the pixel content, in case a buffer_transform
+ in surface-local coordinates, which may differ from the buffer
+ coordinates of the pixel content, in case a buffer_transform
or a buffer_scale is used.
- Surfaces are also used for some special purposes, e.g. as
- cursor images for pointers, drag icons, etc.
+ A surface without a "role" is fairly useless: a compositor does
+ not know where, when or how to present it. The role is the
+ purpose of a wl_surface. Examples of roles are a cursor for a
+ pointer (as set by wl_pointer.set_cursor), a drag icon
+ (wl_data_device.start_drag), a sub-surface
+ (wl_subcompositor.get_subsurface), and a window as defined by a
+ shell protocol (e.g. wl_shell.get_shell_surface).
+
+ A surface can have only one role at a time. Initially a
+ wl_surface does not have a role. Once a wl_surface is given a
+ role, it is set permanently for the whole lifetime of the
+ wl_surface object. Giving the current role again is allowed,
+ unless explicitly forbidden by the relevant interface
+ specification.
+
+ Surface roles are given by requests in other interfaces such as
+ wl_pointer.set_cursor. The request should explicitly mention
+ that this request gives a role to a wl_surface. Often, this
+ request also creates a new protocol object that represents the
+ role and adds additional functionality to wl_surface. When a
+ client wants to destroy a wl_surface, they must destroy this 'role
+ object' before the wl_surface.
+
+ Destroying the role object does not remove the role from the
+ wl_surface, but it may stop the wl_surface from "playing the role".
+ For instance, if a wl_subsurface object is destroyed, the wl_surface
+ it was created for will be unmapped and forget its position and
+ z-order. It is allowed to create a wl_subsurface for the same
+ wl_surface again, but it is not allowed to use the wl_surface as
+ a cursor (cursor is a different role than sub-surface, and role
+ switching is not allowed).
</description>
<enum name="error">
<description summary="wl_surface error values">
- These errors can be emitted in response to wl_surface requests.
+ These errors can be emitted in response to wl_surface requests.
</description>
<entry name="invalid_scale" value="0" summary="buffer scale value is invalid"/>
<entry name="invalid_transform" value="1" summary="buffer transform value is invalid"/>
@@ -1002,7 +1340,7 @@
The x and y arguments specify the location of the new pending
buffer's upper left corner, relative to the current buffer's upper
- left corner, in surface local coordinates. In other words, the
+ left corner, in surface-local coordinates. In other words, the
x and y, combined with the new surface size define in which
directions the surface's size changes.
@@ -1020,7 +1358,7 @@
any time after the wl_surface.commit request. When the compositor
will not access the pixels anymore, it will send the
wl_buffer.release event. Only after receiving wl_buffer.release,
- the client may re-use the wl_buffer. A wl_buffer that has been
+ the client may reuse the wl_buffer. A wl_buffer that has been
attached and then replaced by another attach instead of committed
will not receive a release event, and is not used by the
compositor.
@@ -1033,24 +1371,23 @@
If wl_surface.attach is sent with a NULL wl_buffer, the
following wl_surface.commit will remove the surface content.
</description>
-
- <arg name="buffer" type="object" interface="wl_buffer" allow-null="true"/>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
+ <arg name="buffer" type="object" interface="wl_buffer" allow-null="true"
+ summary="buffer of surface contents"/>
+ <arg name="x" type="int" summary="surface-local x coordinate"/>
+ <arg name="y" type="int" summary="surface-local y coordinate"/>
</request>
<request name="damage">
<description summary="mark part of the surface damaged">
This request is used to describe the regions where the pending
buffer is different from the current surface contents, and where
- the surface therefore needs to be repainted. The pending buffer
- must be set by wl_surface.attach before sending damage. The
- compositor ignores the parts of the damage that fall outside of
- the surface.
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
Damage is double-buffered state, see wl_surface.commit.
- The damage rectangle is specified in surface local coordinates.
+ The damage rectangle is specified in surface-local coordinates,
+ where x and y specify the upper left corner of the damage rectangle.
The initial value for pending damage is empty: no damage.
wl_surface.damage adds pending damage: the new pending damage
@@ -1059,17 +1396,20 @@
wl_surface.commit assigns pending damage as the current damage,
and clears pending damage. The server will clear the current
damage as it repaints the surface.
- </description>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
+ Alternatively, damage can be posted with wl_surface.damage_buffer
+ which uses buffer coordinates instead of surface coordinates,
+ and is probably the preferred and intuitive way of doing this.
+ </description>
+ <arg name="x" type="int" summary="surface-local x coordinate"/>
+ <arg name="y" type="int" summary="surface-local y coordinate"/>
+ <arg name="width" type="int" summary="width of damage rectangle"/>
+ <arg name="height" type="int" summary="height of damage rectangle"/>
</request>
<request name="frame">
<description summary="request a frame throttling hint">
- Request a notification when it is a good time start drawing a new
+ Request a notification when it is a good time to start drawing a new
frame, by creating a frame callback. This is useful for throttling
redrawing operations, and driving animations.
@@ -1088,10 +1428,10 @@
will not send excessive updates, while still allowing
the highest possible update rate for clients that wait for the reply
before drawing again. The server should give some time for the client
- to draw and commit after sending the frame callback events to let them
+ to draw and commit after sending the frame callback events to let it
hit the next output refresh.
- A server should avoid signalling the frame callbacks if the
+ A server should avoid signaling the frame callbacks if the
surface is not visible in any way, e.g. the surface is off-screen,
or completely obscured by other opaque surfaces.
@@ -1102,8 +1442,7 @@
The callback_data passed in the callback is the current time, in
milliseconds, with an undefined base.
</description>
-
- <arg name="callback" type="new_id" interface="wl_callback"/>
+ <arg name="callback" type="new_id" interface="wl_callback" summary="callback object for the frame request"/>
</request>
<request name="set_opaque_region">
@@ -1112,12 +1451,12 @@
opaque content.
The opaque region is an optimization hint for the compositor
- that lets it optimize out redrawing of content behind opaque
+ that lets it optimize the redrawing of content behind opaque
regions. Setting an opaque region is not required for correct
behaviour, but marking transparent content as opaque will result
in repaint artifacts.
- The opaque region is specified in surface local coordinates.
+ The opaque region is specified in surface-local coordinates.
The compositor ignores the parts of the opaque region that fall
outside of the surface.
@@ -1128,13 +1467,13 @@
wl_surface.commit copies the pending region to the current region.
Otherwise, the pending and current regions are never changed.
- The initial value for opaque region is empty. Setting the pending
+ The initial value for an opaque region is empty. Setting the pending
opaque region has copy semantics, and the wl_region object can be
destroyed immediately. A NULL wl_region causes the pending opaque
region to be set to empty.
</description>
-
- <arg name="region" type="object" interface="wl_region" allow-null="true"/>
+ <arg name="region" type="object" interface="wl_region" allow-null="true"
+ summary="opaque region of the surface"/>
</request>
<request name="set_input_region">
@@ -1146,7 +1485,7 @@
surface in the server surface stack. The compositor ignores the
parts of the input region that fall outside of the surface.
- The input region is specified in surface local coordinates.
+ The input region is specified in surface-local coordinates.
Input region is double-buffered state, see wl_surface.commit.
@@ -1156,26 +1495,26 @@
except cursor and icon surfaces are special cases, see
wl_pointer.set_cursor and wl_data_device.start_drag.
- The initial value for input region is infinite. That means the
+ The initial value for an input region is infinite. That means the
whole surface will accept input. Setting the pending input region
has copy semantics, and the wl_region object can be destroyed
immediately. A NULL wl_region causes the input region to be set
to infinite.
</description>
-
- <arg name="region" type="object" interface="wl_region" allow-null="true"/>
+ <arg name="region" type="object" interface="wl_region" allow-null="true"
+ summary="input region of the surface"/>
</request>
<request name="commit">
<description summary="commit pending surface state">
Surface state (input, opaque, and damage regions, attached buffers,
- etc.) is double-buffered. Protocol requests modify the pending
- state, as opposed to current state in use by the compositor. Commit
+ etc.) is double-buffered. Protocol requests modify the pending state,
+ as opposed to the current state in use by the compositor. A commit
request atomically applies all pending state, replacing the current
state. After commit, the new pending state is as documented for each
related request.
- On commit, a pending wl_buffer is applied first, all other state
+ On commit, a pending wl_buffer is applied first, and all other state
second. This means that all coordinates in double-buffered state are
relative to the new wl_buffer coming into use, except for
wl_surface.attach itself. If there is no pending wl_buffer, the
@@ -1196,7 +1535,7 @@
Note that a surface may be overlapping with zero or more outputs.
</description>
- <arg name="output" type="object" interface="wl_output"/>
+ <arg name="output" type="object" interface="wl_output" summary="output entered by the surface"/>
</event>
<event name="leave">
@@ -1205,7 +1544,7 @@
results in it no longer having any part of it within the scanout region
of an output.
</description>
- <arg name="output" type="object" interface="wl_output"/>
+ <arg name="output" type="object" interface="wl_output" summary="output left by the surface"/>
</event>
<!-- Version 2 additions -->
@@ -1227,7 +1566,7 @@
values are never changed.
The purpose of this request is to allow clients to render content
- according to the output transform, thus permiting the compositor to
+ according to the output transform, thus permitting the compositor to
use certain optimizations even if the display is rotated. Using
hardware overlays and scanning out a client buffer for fullscreen
surfaces are examples of such optimizations. Those optimizations are
@@ -1242,7 +1581,8 @@
wl_output.transform enum the invalid_transform protocol error
is raised.
</description>
- <arg name="transform" type="int"/>
+ <arg name="transform" type="int" enum="wl_output.transform"
+ summary="transform for interpreting buffer contents"/>
</request>
<!-- Version 3 additions -->
@@ -1261,9 +1601,9 @@
Otherwise, the pending and current values are never changed.
The purpose of this request is to allow clients to supply higher
- resolution buffer data for use on high resolution outputs. Its
- intended that you pick the same buffer scale as the scale of the
- output that the surface is displayed on.This means the compositor
+ resolution buffer data for use on high resolution outputs. It is
+ intended that you pick the same buffer scale as the scale of the
+ output that the surface is displayed on. This means the compositor
can avoid scaling when rendering the surface on that output.
Note that if the scale is larger than 1, then you have to attach
@@ -1273,11 +1613,54 @@
If scale is not positive the invalid_scale protocol error is
raised.
</description>
- <arg name="scale" type="int"/>
+ <arg name="scale" type="int"
+ summary="positive scale for interpreting buffer contents"/>
+ </request>
+
+ <!-- Version 4 additions -->
+ <request name="damage_buffer" since="4">
+ <description summary="mark part of the surface damaged using buffer coordinates">
+ This request is used to describe the regions where the pending
+ buffer is different from the current surface contents, and where
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
+
+ Damage is double-buffered state, see wl_surface.commit.
+
+ The damage rectangle is specified in buffer coordinates,
+ where x and y specify the upper left corner of the damage rectangle.
+
+ The initial value for pending damage is empty: no damage.
+ wl_surface.damage_buffer adds pending damage: the new pending
+ damage is the union of old pending damage and the given rectangle.
+
+ wl_surface.commit assigns pending damage as the current damage,
+ and clears pending damage. The server will clear the current
+ damage as it repaints the surface.
+
+ This request differs from wl_surface.damage in only one way - it
+ takes damage in buffer coordinates instead of surface-local
+ coordinates. While this generally is more intuitive than surface
+ coordinates, it is especially desirable when using wp_viewport
+ or when a drawing library (like EGL) is unaware of buffer scale
+ and buffer transform.
+
+ Note: Because buffer transformation changes and damage requests may
+ be interleaved in the protocol stream, it is impossible to determine
+ the actual mapping between surface and buffer damage until
+ wl_surface.commit time. Therefore, compositors wishing to take both
+ kinds of damage into account will have to accumulate damage from the
+ two requests separately and only transform from one to the other
+ after receiving the wl_surface.commit.
+ </description>
+ <arg name="x" type="int" summary="buffer-local x coordinate"/>
+ <arg name="y" type="int" summary="buffer-local y coordinate"/>
+ <arg name="width" type="int" summary="width of damage rectangle"/>
+ <arg name="height" type="int" summary="height of damage rectangle"/>
</request>
</interface>
- <interface name="wl_seat" version="4">
+ <interface name="wl_seat" version="6">
<description summary="group of input devices">
A seat is a group of keyboards, pointer and touch devices. This
object is published as a global during start up, or when such a
@@ -1285,59 +1668,86 @@
maintains a keyboard focus and a pointer focus.
</description>
- <enum name="capability">
+ <enum name="capability" bitfield="true">
<description summary="seat capability bitmask">
- This is a bitmask of capabilities this seat has; if a member is
- set, then it is present on the seat.
+ This is a bitmask of capabilities this seat has; if a member is
+ set, then it is present on the seat.
</description>
- <entry name="pointer" value="1" summary="The seat has pointer devices"/>
- <entry name="keyboard" value="2" summary="The seat has one or more keyboards"/>
- <entry name="touch" value="4" summary="The seat has touch devices"/>
+ <entry name="pointer" value="1" summary="the seat has pointer devices"/>
+ <entry name="keyboard" value="2" summary="the seat has one or more keyboards"/>
+ <entry name="touch" value="4" summary="the seat has touch devices"/>
</enum>
<event name="capabilities">
<description summary="seat capabilities changed">
- This is emitted whenever a seat gains or loses the pointer,
+ This is emitted whenever a seat gains or loses the pointer,
keyboard or touch capabilities. The argument is a capability
enum containing the complete set of capabilities this seat has.
+
+ When the pointer capability is added, a client may create a
+ wl_pointer object using the wl_seat.get_pointer request. This object
+ will receive pointer events until the capability is removed in the
+ future.
+
+ When the pointer capability is removed, a client should destroy the
+ wl_pointer objects associated with the seat where the capability was
+ removed, using the wl_pointer.release request. No further pointer
+ events will be received on these objects.
+
+ In some compositors, if a seat regains the pointer capability and a
+ client has a previously obtained wl_pointer object of version 4 or
+ less, that object may start sending pointer events again. This
+ behavior is considered a misinterpretation of the intended behavior
+ and must not be relied upon by the client. wl_pointer objects of
+ version 5 or later must not send events if created before the most
+ recent event notifying the client of an added pointer capability.
+
+ The above behavior also applies to wl_keyboard and wl_touch with the
+ keyboard and touch capabilities, respectively.
</description>
- <arg name="capabilities" type="uint"/>
+ <arg name="capabilities" type="uint" enum="capability" summary="capabilities of the seat"/>
</event>
<request name="get_pointer">
<description summary="return pointer object">
- The ID provided will be initialized to the wl_pointer interface
+ The ID provided will be initialized to the wl_pointer interface
for this seat.
- This request only takes effect if the seat has the pointer
- capability.
+ This request only takes effect if the seat has the pointer
+ capability, or has had the pointer capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the pointer capability.
</description>
- <arg name="id" type="new_id" interface="wl_pointer"/>
+ <arg name="id" type="new_id" interface="wl_pointer" summary="seat pointer"/>
</request>
<request name="get_keyboard">
<description summary="return keyboard object">
- The ID provided will be initialized to the wl_keyboard interface
+ The ID provided will be initialized to the wl_keyboard interface
for this seat.
- This request only takes effect if the seat has the keyboard
- capability.
+ This request only takes effect if the seat has the keyboard
+ capability, or has had the keyboard capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the keyboard capability.
</description>
- <arg name="id" type="new_id" interface="wl_keyboard"/>
+ <arg name="id" type="new_id" interface="wl_keyboard" summary="seat keyboard"/>
</request>
<request name="get_touch">
<description summary="return touch object">
- The ID provided will be initialized to the wl_touch interface
+ The ID provided will be initialized to the wl_touch interface
for this seat.
- This request only takes effect if the seat has the touch
- capability.
+ This request only takes effect if the seat has the touch
+ capability, or has had the touch capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the touch capability.
</description>
- <arg name="id" type="new_id" interface="wl_touch"/>
+ <arg name="id" type="new_id" interface="wl_touch" summary="seat touch interface"/>
</request>
- <!-- Version 2 of additions -->
+ <!-- Version 2 additions -->
<event name="name" since="2">
<description summary="unique identifier for this seat">
@@ -1345,12 +1755,21 @@
identify which physical devices the seat represents. Based on
the seat configuration used by the compositor.
</description>
- <arg name="name" type="string"/>
+ <arg name="name" type="string" summary="seat identifier"/>
</event>
+ <!-- Version 5 additions -->
+
+ <request name="release" type="destructor" since="5">
+ <description summary="release the seat object">
+ Using this request a client can tell the server that it is not going to
+ use the seat object anymore.
+ </description>
+ </request>
+
</interface>
- <interface name="wl_pointer" version="3">
+ <interface name="wl_pointer" version="6">
<description summary="pointer input device">
The wl_pointer interface represents one or more input devices,
such as mice, which control the pointer location and pointer_focus
@@ -1362,10 +1781,18 @@
and scrolling.
</description>
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ </enum>
+
<request name="set_cursor">
<description summary="set the pointer surface">
Set the pointer surface, i.e., the surface that contains the
- pointer image (cursor). This request only takes effect if the pointer
+ pointer image (cursor). This request gives the surface the role
+ of a cursor. If the surface already has another role, it raises
+ a protocol error.
+
+ The cursor actually changes only if the pointer
focus for this device is one of the requesting client's surfaces
or the surface parameter is the current pointer surface. If
there was a previous surface set with this request it is
@@ -1374,8 +1801,8 @@
The parameters hotspot_x and hotspot_y define the position of
the pointer surface relative to the pointer location. Its
top-left corner is always at (x, y) - (hotspot_x, hotspot_y),
- where (x, y) are the coordinates of the pointer location, in surface
- local coordinates.
+ where (x, y) are the coordinates of the pointer location, in
+ surface-local coordinates.
On surface.attach requests to the pointer surface, hotspot_x
and hotspot_y are decremented by the x and y parameters
@@ -1392,11 +1819,11 @@
cursor ends, the current and pending input regions become
undefined, and the wl_surface is unmapped.
</description>
-
- <arg name="serial" type="uint" summary="serial of the enter event"/>
- <arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
- <arg name="hotspot_x" type="int" summary="x coordinate in surface-relative coordinates"/>
- <arg name="hotspot_y" type="int" summary="y coordinate in surface-relative coordinates"/>
+ <arg name="serial" type="uint" summary="serial number of the enter event"/>
+ <arg name="surface" type="object" interface="wl_surface" allow-null="true"
+ summary="pointer surface"/>
+ <arg name="hotspot_x" type="int" summary="surface-local x coordinate"/>
+ <arg name="hotspot_y" type="int" summary="surface-local y coordinate"/>
</request>
<event name="enter">
@@ -1404,15 +1831,14 @@
Notification that this seat's pointer is focused on a certain
surface.
- When an seat's focus enters a surface, the pointer image
+ When a seat's focus enters a surface, the pointer image
is undefined and a client should respond to this event by setting
an appropriate pointer image with the set_cursor request.
</description>
-
- <arg name="serial" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- <arg name="surface_x" type="fixed" summary="x coordinate in surface-relative coordinates"/>
- <arg name="surface_y" type="fixed" summary="y coordinate in surface-relative coordinates"/>
+ <arg name="serial" type="uint" summary="serial number of the enter event"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface entered by the pointer"/>
+ <arg name="surface_x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="surface_y" type="fixed" summary="surface-local y coordinate"/>
</event>
<event name="leave">
@@ -1423,8 +1849,8 @@
The leave notification is sent before the enter notification
for the new focus.
</description>
- <arg name="serial" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="serial" type="uint" summary="serial number of the leave event"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface left by the pointer"/>
</event>
<event name="motion">
@@ -1433,19 +1859,18 @@
surface_x and surface_y are the location relative to the
focused surface.
</description>
-
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="surface_x" type="fixed" summary="x coordinate in surface-relative coordinates"/>
- <arg name="surface_y" type="fixed" summary="y coordinate in surface-relative coordinates"/>
+ <arg name="surface_x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="surface_y" type="fixed" summary="surface-local y coordinate"/>
</event>
<enum name="button_state">
<description summary="physical button state">
- Describes the physical state of a button which provoked the button
+ Describes the physical state of a button that produced the button
event.
</description>
- <entry name="released" value="0" summary="The button is not pressed"/>
- <entry name="pressed" value="1" summary="The button is pressed"/>
+ <entry name="released" value="0" summary="the button is not pressed"/>
+ <entry name="pressed" value="1" summary="the button is pressed"/>
</enum>
<event name="button">
@@ -1454,22 +1879,29 @@
The location of the click is given by the last motion or
enter event.
- The time argument is a timestamp with millisecond
- granularity, with an undefined base.
- </description>
+ The time argument is a timestamp with millisecond
+ granularity, with an undefined base.
+
+ The button is a button code as defined in the Linux kernel's
+ linux/input-event-codes.h header file, e.g. BTN_LEFT.
- <arg name="serial" type="uint"/>
+ Any 16-bit button code value is reserved for future additions to the
+ kernel's event code list. All other button codes above 0xFFFF are
+ currently undefined but may be used in future versions of this
+ protocol.
+ </description>
+ <arg name="serial" type="uint" summary="serial number of the button event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="button" type="uint"/>
- <arg name="state" type="uint"/>
+ <arg name="button" type="uint" summary="button that produced the event"/>
+ <arg name="state" type="uint" enum="button_state" summary="physical state of the button"/>
</event>
<enum name="axis">
<description summary="axis types">
Describes the axis types of scroll events.
</description>
- <entry name="vertical_scroll" value="0"/>
- <entry name="horizontal_scroll" value="1"/>
+ <entry name="vertical_scroll" value="0" summary="vertical axis"/>
+ <entry name="horizontal_scroll" value="1" summary="horizontal axis"/>
</enum>
<event name="axis">
@@ -1488,24 +1920,179 @@
choose to emit scroll events where the motion vector is
equivalent to a motion event vector.
- When applicable, clients can transform its view relative to the
+ When applicable, a client can transform its content relative to the
scroll distance.
</description>
-
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="axis" type="uint"/>
- <arg name="value" type="fixed"/>
+ <arg name="axis" type="uint" enum="axis" summary="axis type"/>
+ <arg name="value" type="fixed" summary="length of vector in surface-local coordinate space"/>
</event>
<!-- Version 3 additions -->
<request name="release" type="destructor" since="3">
- <description summary="release the pointer object"/>
+ <description summary="release the pointer object">
+ Using this request a client can tell the server that it is not going to
+ use the pointer object anymore.
+
+ This request destroys the pointer proxy object, so clients must not call
+ wl_pointer_destroy() after using this request.
+ </description>
</request>
+ <!-- Version 5 additions -->
+
+ <event name="frame" since="5">
+ <description summary="end of a pointer event sequence">
+ Indicates the end of a set of events that logically belong together.
+ A client is expected to accumulate the data in all events within the
+ frame before proceeding.
+
+ All wl_pointer events before a wl_pointer.frame event belong
+ logically together. For example, in a diagonal scroll motion the
+ compositor will send an optional wl_pointer.axis_source event, two
+ wl_pointer.axis events (horizontal and vertical) and finally a
+ wl_pointer.frame event. The client may use this information to
+ calculate a diagonal vector for scrolling.
+
+ When multiple wl_pointer.axis events occur within the same frame,
+ the motion vector is the combined motion of all events.
+ When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
+ the same frame, this indicates that axis movement in one axis has
+ stopped but continues in the other axis.
+ When multiple wl_pointer.axis_stop events occur within the same
+ frame, this indicates that these axes stopped in the same instance.
+
+ A wl_pointer.frame event is sent for every logical event group,
+ even if the group only contains a single wl_pointer event.
+ Specifically, a client may get a sequence: motion, frame, button,
+ frame, axis, frame, axis_stop, frame.
+
+ The wl_pointer.enter and wl_pointer.leave events are logical events
+ generated by the compositor and not the hardware. These events are
+ also grouped by a wl_pointer.frame. When a pointer moves from one
+ surface to another, a compositor should group the
+ wl_pointer.leave event within the same wl_pointer.frame.
+ However, a client must not rely on wl_pointer.leave and
+ wl_pointer.enter being in the same wl_pointer.frame.
+ Compositor-specific policies may require the wl_pointer.leave and
+ wl_pointer.enter event being split across multiple wl_pointer.frame
+ groups.
+ </description>
+ </event>
+
+ <enum name="axis_source">
+ <description summary="axis source types">
+ Describes the source types for axis events. This indicates to the
+ client how an axis event was physically generated; a client may
+ adjust the user interface accordingly. For example, scroll events
+ from a "finger" source may be in a smooth coordinate space with
+ kinetic scrolling whereas a "wheel" source may be in discrete steps
+ of a number of lines.
+
+ The "continuous" axis source is a device generating events in a
+ continuous coordinate space, but using something other than a
+ finger. One example for this source is button-based scrolling where
+ the vertical motion of a device is converted to scroll events while
+ a button is held down.
+
+ The "wheel tilt" axis source indicates that the actual device is a
+ wheel but the scroll event is not caused by a rotation but a
+ (usually sideways) tilt of the wheel.
+ </description>
+ <entry name="wheel" value="0" summary="a physical wheel rotation" />
+ <entry name="finger" value="1" summary="finger on a touch surface" />
+ <entry name="continuous" value="2" summary="continuous coordinate space"/>
+ <entry name="wheel_tilt" value="3" summary="a physical wheel tilt" since="6"/>
+ </enum>
+
+ <event name="axis_source" since="5">
+ <description summary="axis source event">
+ Source information for scroll and other axes.
+
+ This event does not occur on its own. It is sent before a
+ wl_pointer.frame event and carries the source information for
+ all events within that frame.
+
+ The source specifies how this event was generated. If the source is
+ wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
+ sent when the user lifts the finger off the device.
+
+ If the source is wl_pointer.axis_source.wheel,
+ wl_pointer.axis_source.wheel_tilt or
+ wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
+ or may not be sent. Whether a compositor sends an axis_stop event
+ for these sources is hardware-specific and implementation-dependent;
+ clients must not rely on receiving an axis_stop event for these
+ scroll sources and should treat scroll sequences from these scroll
+ sources as unterminated by default.
+
+ This event is optional. If the source is unknown for a particular
+ axis event sequence, no event is sent.
+ Only one wl_pointer.axis_source event is permitted per frame.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+ </description>
+ <arg name="axis_source" type="uint" enum="axis_source" summary="source of the axis event"/>
+ </event>
+
+ <event name="axis_stop" since="5">
+ <description summary="axis stop event">
+ Stop notification for scroll and other axes.
+
+ For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
+ is sent to notify a client that the axis sequence has terminated.
+ This enables the client to implement kinetic scrolling.
+ See the wl_pointer.axis_source documentation for information on when
+ this event may be generated.
+
+ Any wl_pointer.axis events with the same axis_source after this
+ event should be considered as the start of a new axis motion.
+
+ The timestamp is to be interpreted identical to the timestamp in the
+ wl_pointer.axis event. The timestamp value may be the same as a
+ preceding wl_pointer.axis event.
+ </description>
+ <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+ <arg name="axis" type="uint" enum="axis" summary="the axis stopped with this event"/>
+ </event>
+
+ <event name="axis_discrete" since="5">
+ <description summary="axis click event">
+ Discrete step information for scroll and other axes.
+
+ This event carries the axis value of the wl_pointer.axis event in
+ discrete steps (e.g. mouse wheel clicks).
+
+ This event does not occur on its own, it is coupled with a
+ wl_pointer.axis event that represents this axis value on a
+ continuous scale. The protocol guarantees that each axis_discrete
+ event is always followed by exactly one axis event with the same
+ axis number within the same wl_pointer.frame. Note that the protocol
+ allows for other events to occur between the axis_discrete and
+ its coupled axis event, including other axis_discrete or axis
+ events.
+
+ This event is optional; continuous scrolling devices
+ like two-finger scrolling on touchpads do not have discrete
+ steps and do not generate this event.
+
+ The discrete value carries the directional information. e.g. a value
+ of -2 is two steps towards the negative direction of this axis.
+
+ The axis number is identical to the axis number in the associated
+ axis event.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+ </description>
+ <arg name="axis" type="uint" enum="axis" summary="axis type"/>
+ <arg name="discrete" type="int" summary="number of steps"/>
+ </event>
</interface>
- <interface name="wl_keyboard" version="4">
+ <interface name="wl_keyboard" version="6">
<description summary="keyboard input device">
The wl_keyboard interface represents one or more keyboards
associated with a seat.
@@ -1519,7 +2106,7 @@
<entry name="no_keymap" value="0"
summary="no keymap; client must understand how to interpret the raw keycode"/>
<entry name="xkb_v1" value="1"
- summary="libxkbcommon compatible; to determine the xkb keycode, clients must add 8 to the key event keycode"/>
+ summary="libxkbcommon compatible; to determine the xkb keycode, clients must add 8 to the key event keycode"/>
</enum>
<event name="keymap">
@@ -1527,9 +2114,9 @@
This event provides a file descriptor to the client which can be
memory-mapped to provide a keyboard mapping description.
</description>
- <arg name="format" type="uint"/>
- <arg name="fd" type="fd"/>
- <arg name="size" type="uint"/>
+ <arg name="format" type="uint" enum="keymap_format" summary="keymap format"/>
+ <arg name="fd" type="fd" summary="keymap file descriptor"/>
+ <arg name="size" type="uint" summary="keymap size, in bytes"/>
</event>
<event name="enter">
@@ -1537,8 +2124,8 @@
Notification that this seat's keyboard focus is on a certain
surface.
</description>
- <arg name="serial" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="serial" type="uint" summary="serial number of the enter event"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface gaining keyboard focus"/>
<arg name="keys" type="array" summary="the currently pressed keys"/>
</event>
@@ -1550,13 +2137,13 @@
The leave notification is sent before the enter notification
for the new focus.
</description>
- <arg name="serial" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="serial" type="uint" summary="serial number of the leave event"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface that lost keyboard focus"/>
</event>
<enum name="key_state">
<description summary="physical key state">
- Describes the physical state of a key which provoked the key event.
+ Describes the physical state of a key that produced the key event.
</description>
<entry name="released" value="0" summary="key is not pressed"/>
<entry name="pressed" value="1" summary="key is pressed"/>
@@ -1565,14 +2152,13 @@
<event name="key">
<description summary="key event">
A key was pressed or released.
- The time argument is a timestamp with millisecond
- granularity, with an undefined base.
+ The time argument is a timestamp with millisecond
+ granularity, with an undefined base.
</description>
-
- <arg name="serial" type="uint"/>
+ <arg name="serial" type="uint" summary="serial number of the key event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="key" type="uint"/>
- <arg name="state" type="uint"/>
+ <arg name="key" type="uint" summary="key that produced the event"/>
+ <arg name="state" type="uint" enum="key_state" summary="physical state of the key"/>
</event>
<event name="modifiers">
@@ -1580,12 +2166,11 @@
Notifies clients that the modifier and/or group state has
changed, and it should update its local state.
</description>
-
- <arg name="serial" type="uint"/>
- <arg name="mods_depressed" type="uint"/>
- <arg name="mods_latched" type="uint"/>
- <arg name="mods_locked" type="uint"/>
- <arg name="group" type="uint"/>
+ <arg name="serial" type="uint" summary="serial number of the modifiers event"/>
+ <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+ <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+ <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+ <arg name="group" type="uint" summary="keyboard layout"/>
</event>
<!-- Version 3 additions -->
@@ -1598,28 +2183,27 @@
<event name="repeat_info" since="4">
<description summary="repeat rate and delay">
- Informs the client about the keyboard's repeat rate and delay.
+ Informs the client about the keyboard's repeat rate and delay.
- This event is sent as soon as the wl_keyboard object has been created,
- and is guaranteed to be received by the client before any key press
- event.
+ This event is sent as soon as the wl_keyboard object has been created,
+ and is guaranteed to be received by the client before any key press
+ event.
- Negative values for either rate or delay are illegal. A rate of zero
- will disable any repeating (regardless of the value of delay).
+ Negative values for either rate or delay are illegal. A rate of zero
+ will disable any repeating (regardless of the value of delay).
- This event can be sent later on as well with a new value if necessary,
- so clients should continue listening for the event past the creation
- of wl_keyboard.
+ This event can be sent later on as well with a new value if necessary,
+ so clients should continue listening for the event past the creation
+ of wl_keyboard.
</description>
-
<arg name="rate" type="int"
- summary="the rate of repeating keys in characters per second"/>
+ summary="the rate of repeating keys in characters per second"/>
<arg name="delay" type="int"
- summary="delay in milliseconds since key down until repeating starts"/>
+ summary="delay in milliseconds since key down until repeating starts"/>
</event>
</interface>
- <interface name="wl_touch" version="3">
+ <interface name="wl_touch" version="6">
<description summary="touchscreen input device">
The wl_touch interface represents a touchscreen
associated with a seat.
@@ -1634,42 +2218,49 @@
<event name="down">
<description summary="touch down event and beginning of a touch sequence">
A new touch point has appeared on the surface. This touch point is
- assigned a unique @id. Future events from this touchpoint reference
+ assigned a unique ID. Future events from this touch point reference
this ID. The ID ceases to be valid after a touch up event and may be
- re-used in the future.
+ reused in the future.
</description>
- <arg name="serial" type="uint"/>
+ <arg name="serial" type="uint" summary="serial number of the touch down event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="surface" type="object" interface="wl_surface" summary="surface touched"/>
<arg name="id" type="int" summary="the unique ID of this touch point"/>
- <arg name="x" type="fixed" summary="x coordinate in surface-relative coordinates"/>
- <arg name="y" type="fixed" summary="y coordinate in surface-relative coordinates"/>
+ <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="y" type="fixed" summary="surface-local y coordinate"/>
</event>
<event name="up">
<description summary="end of a touch event sequence">
The touch point has disappeared. No further events will be sent for
- this touchpoint and the touch point's ID is released and may be
- re-used in a future touch down event.
+ this touch point and the touch point's ID is released and may be
+ reused in a future touch down event.
</description>
- <arg name="serial" type="uint"/>
+ <arg name="serial" type="uint" summary="serial number of the touch up event"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="id" type="int" summary="the unique ID of this touch point"/>
</event>
<event name="motion">
<description summary="update of touch point coordinates">
- A touchpoint has changed coordinates.
+ A touch point has changed coordinates.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="id" type="int" summary="the unique ID of this touch point"/>
- <arg name="x" type="fixed" summary="x coordinate in surface-relative coordinates"/>
- <arg name="y" type="fixed" summary="y coordinate in surface-relative coordinates"/>
+ <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+ <arg name="y" type="fixed" summary="surface-local y coordinate"/>
</event>
<event name="frame">
<description summary="end of touch frame event">
- Indicates the end of a contact point list.
+ Indicates the end of a set of events that logically belong together.
+ A client is expected to accumulate the data in all events within the
+ frame before proceeding.
+
+ A wl_touch.frame terminates at least one event but otherwise no
+ guarantee is provided about the set of events within a frame. A client
+ must assume that any state not updated in a frame is unchanged from the
+ previously known state.
</description>
</event>
@@ -1680,7 +2271,7 @@
particular gesture. Touch cancellation applies to all touch points
currently active on this client's surface. The client is
responsible for finalizing the touch points, future touch points on
- this surface may re-use the touch point ID.
+ this surface may reuse the touch point ID.
</description>
</event>
@@ -1689,13 +2280,78 @@
<request name="release" type="destructor" since="3">
<description summary="release the touch object"/>
</request>
+
+ <!-- Version 6 additions -->
+
+ <event name="shape" since="6">
+ <description summary="update shape of touch point">
+ Sent when a touchpoint has changed its shape.
+
+ This event does not occur on its own. It is sent before a
+ wl_touch.frame event and carries the new shape information for
+ any previously reported, or new touch points of that frame.
+
+ Other events describing the touch point such as wl_touch.down,
+ wl_touch.motion or wl_touch.orientation may be sent within the
+ same wl_touch.frame. A client should treat these events as a single
+ logical touch point update. The order of wl_touch.shape,
+ wl_touch.orientation and wl_touch.motion is not guaranteed.
+ A wl_touch.down event is guaranteed to occur before the first
+ wl_touch.shape event for this touch ID but both events may occur within
+ the same wl_touch.frame.
+
+ A touchpoint shape is approximated by an ellipse through the major and
+ minor axis length. The major axis length describes the longer diameter
+ of the ellipse, while the minor axis length describes the shorter
+ diameter. Major and minor are orthogonal and both are specified in
+ surface-local coordinates. The center of the ellipse is always at the
+ touchpoint location as reported by wl_touch.down or wl_touch.move.
+
+ This event is only sent by the compositor if the touch device supports
+ shape reports. The client has to make reasonable assumptions about the
+ shape if it did not receive this event.
+ </description>
+ <arg name="id" type="int" summary="the unique ID of this touch point"/>
+ <arg name="major" type="fixed" summary="length of the major axis in surface-local coordinates"/>
+ <arg name="minor" type="fixed" summary="length of the minor axis in surface-local coordinates"/>
+ </event>
+
+ <event name="orientation" since="6">
+ <description summary="update orientation of touch point">
+ Sent when a touchpoint has changed its orientation.
+
+ This event does not occur on its own. It is sent before a
+ wl_touch.frame event and carries the new shape information for
+ any previously reported, or new touch points of that frame.
+
+ Other events describing the touch point such as wl_touch.down,
+ wl_touch.motion or wl_touch.shape may be sent within the
+ same wl_touch.frame. A client should treat these events as a single
+ logical touch point update. The order of wl_touch.shape,
+ wl_touch.orientation and wl_touch.motion is not guaranteed.
+ A wl_touch.down event is guaranteed to occur before the first
+ wl_touch.orientation event for this touch ID but both events may occur
+ within the same wl_touch.frame.
+
+ The orientation describes the clockwise angle of a touchpoint's major
+ axis to the positive surface y-axis and is normalized to the -180 to
+ +180 degree range. The granularity of orientation depends on the touch
+ device, some devices only support binary rotation values between 0 and
+ 90 degrees.
+
+ This event is only sent by the compositor if the touch device supports
+ orientation reports.
+ </description>
+ <arg name="id" type="int" summary="the unique ID of this touch point"/>
+ <arg name="orientation" type="fixed" summary="angle between major axis and positive surface y-axis in degrees"/>
+ </event>
</interface>
- <interface name="wl_output" version="2">
+ <interface name="wl_output" version="3">
<description summary="compositor output region">
An output describes part of the compositor geometry. The
compositor works in the 'compositor coordinate system' and an
- output corresponds to rectangular area in that space that is
+ output corresponds to a rectangular area in that space that is
actually visible. This typically corresponds to a monitor that
displays part of the compositor space. This object is published
as global during start up, or when a monitor is hotplugged.
@@ -1704,14 +2360,14 @@
<enum name="subpixel">
<description summary="subpixel geometry information">
This enumeration describes how the physical
- pixels on an output are layed out.
- </description>
- <entry name="unknown" value="0"/>
- <entry name="none" value="1"/>
- <entry name="horizontal_rgb" value="2"/>
- <entry name="horizontal_bgr" value="3"/>
- <entry name="vertical_rgb" value="4"/>
- <entry name="vertical_bgr" value="5"/>
+ pixels on an output are laid out.
+ </description>
+ <entry name="unknown" value="0" summary="unknown geometry"/>
+ <entry name="none" value="1" summary="no geometry"/>
+ <entry name="horizontal_rgb" value="2" summary="horizontal RGB"/>
+ <entry name="horizontal_bgr" value="3" summary="horizontal BGR"/>
+ <entry name="vertical_rgb" value="4" summary="vertical RGB"/>
+ <entry name="vertical_bgr" value="5" summary="vertical BGR"/>
</enum>
<enum name="transform">
@@ -1723,20 +2379,19 @@
The flipped values correspond to an initial flip around a
vertical axis followed by rotation.
- The purpose is mainly to allow clients render accordingly and
+ The purpose is mainly to allow clients to render accordingly and
tell the compositor, so that for fullscreen surfaces, the
compositor will still be able to scan out directly from client
surfaces.
</description>
-
- <entry name="normal" value="0"/>
- <entry name="90" value="1"/>
- <entry name="180" value="2"/>
- <entry name="270" value="3"/>
- <entry name="flipped" value="4"/>
- <entry name="flipped_90" value="5"/>
- <entry name="flipped_180" value="6"/>
- <entry name="flipped_270" value="7"/>
+ <entry name="normal" value="0" summary="no transform"/>
+ <entry name="90" value="1" summary="90 degrees counter-clockwise"/>
+ <entry name="180" value="2" summary="180 degrees counter-clockwise"/>
+ <entry name="270" value="3" summary="270 degrees counter-clockwise"/>
+ <entry name="flipped" value="4" summary="180 degree flip around a vertical axis"/>
+ <entry name="flipped_90" value="5" summary="flip and rotate 90 degrees counter-clockwise"/>
+ <entry name="flipped_180" value="6" summary="flip and rotate 180 degrees counter-clockwise"/>
+ <entry name="flipped_270" value="7" summary="flip and rotate 270 degrees counter-clockwise"/>
</enum>
<event name="geometry">
@@ -1744,6 +2399,9 @@
The geometry event describes geometric properties of the output.
The event is sent when binding to the output object and whenever
any of the properties change.
+
+ The physical size can be set to zero if it doesn't make sense for this
+ output (e.g. for projectors or virtual outputs).
</description>
<arg name="x" type="int"
summary="x position within the global compositor space"/>
@@ -1753,17 +2411,17 @@
summary="width in millimeters of the output"/>
<arg name="physical_height" type="int"
summary="height in millimeters of the output"/>
- <arg name="subpixel" type="int"
+ <arg name="subpixel" type="int" enum="subpixel"
summary="subpixel orientation of the output"/>
<arg name="make" type="string"
summary="textual description of the manufacturer"/>
<arg name="model" type="string"
summary="textual description of the model"/>
- <arg name="transform" type="int"
+ <arg name="transform" type="int" enum="transform"
summary="transform that maps framebuffer to output"/>
</event>
- <enum name="mode">
+ <enum name="mode" bitfield="true">
<description summary="mode information">
These flags describe properties of an output mode.
They are used in the flags bitfield of the mode event.
@@ -1785,33 +2443,35 @@
mode that was received with the current flag set.
The size of a mode is given in physical hardware units of
- the output device. This is not necessarily the same as
- the output size in the global compositor space. For instance,
- the output may be scaled, as described in wl_output.scale,
- or transformed , as described in wl_output.transform.
+ the output device. This is not necessarily the same as
+ the output size in the global compositor space. For instance,
+ the output may be scaled, as described in wl_output.scale,
+ or transformed, as described in wl_output.transform.
</description>
- <arg name="flags" type="uint" summary="bitfield of mode flags"/>
+ <arg name="flags" type="uint" enum="mode" summary="bitfield of mode flags"/>
<arg name="width" type="int" summary="width of the mode in hardware units"/>
<arg name="height" type="int" summary="height of the mode in hardware units"/>
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
</event>
+ <!-- Version 2 additions -->
+
<event name="done" since="2">
<description summary="sent all information about output">
- This event is sent after all other properties has been
- sent after binding to the output object and after any
- other property changes done after that. This allows
- changes to the output properties to be seen as
- atomic, even if they happen via multiple events.
+ This event is sent after all other properties have been
+ sent after binding to the output object and after any
+ other property changes done after that. This allows
+ changes to the output properties to be seen as
+ atomic, even if they happen via multiple events.
</description>
</event>
<event name="scale" since="2">
<description summary="output scaling properties">
This event contains scaling geometry information
- that is not in the geometry event. It may be sent after
- binding the output object or if the output scale changes
- later. If it is not sent, the client should assume a
+ that is not in the geometry event. It may be sent after
+ binding the output object or if the output scale changes
+ later. If it is not sent, the client should assume a
scale of 1.
A scale larger than 1 means that the compositor will
@@ -1829,6 +2489,15 @@
</description>
<arg name="factor" type="int" summary="scaling factor of output"/>
</event>
+
+ <!-- Version 3 additions -->
+
+ <request name="release" type="destructor" since="3">
+ <description summary="release the output object">
+ Using this request a client can tell the server that it is not going to
+ use the output object anymore.
+ </description>
+ </request>
</interface>
<interface name="wl_region" version="1">
@@ -1849,24 +2518,21 @@
<description summary="add rectangle to region">
Add the specified rectangle to the region.
</description>
-
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
+ <arg name="x" type="int" summary="region-local x coordinate"/>
+ <arg name="y" type="int" summary="region-local y coordinate"/>
+ <arg name="width" type="int" summary="rectangle width"/>
+ <arg name="height" type="int" summary="rectangle height"/>
</request>
<request name="subtract">
<description summary="subtract rectangle from region">
Subtract the specified rectangle from the region.
</description>
-
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
+ <arg name="x" type="int" summary="region-local x coordinate"/>
+ <arg name="y" type="int" summary="region-local y coordinate"/>
+ <arg name="width" type="int" summary="rectangle width"/>
+ <arg name="height" type="int" summary="rectangle height"/>
</request>
-
</interface>
<interface name="wl_subcompositor" version="1">
@@ -1902,7 +2568,7 @@
<enum name="error">
<entry name="bad_surface" value="0"
- summary="the to-be sub-surface is invalid"/>
+ summary="the to-be sub-surface is invalid"/>
</enum>
<request name="get_subsurface">
@@ -1911,17 +2577,24 @@
associate it with the given parent surface. This turns a
plain wl_surface into a sub-surface.
- The to-be sub-surface must not already have a dedicated
- purpose, like any shell surface type, cursor image, drag icon,
- or sub-surface. Otherwise a protocol error is raised.
- </description>
+ The to-be sub-surface must not already have another role, and it
+ must not have an existing wl_subsurface object. Otherwise a protocol
+ error is raised.
+ Adding sub-surfaces to a parent is a double-buffered operation on the
+ parent (see wl_surface.commit). The effect of adding a sub-surface
+ becomes visible on the next time the state of the parent surface is
+ applied.
+
+ This request modifies the behaviour of wl_surface.commit request on
+ the sub-surface, see the documentation on wl_subsurface interface.
+ </description>
<arg name="id" type="new_id" interface="wl_subsurface"
- summary="the new subsurface object id"/>
+ summary="the new sub-surface object ID"/>
<arg name="surface" type="object" interface="wl_surface"
- summary="the surface to be turned into a sub-surface"/>
+ summary="the surface to be turned into a sub-surface"/>
<arg name="parent" type="object" interface="wl_surface"
- summary="the parent surface"/>
+ summary="the parent surface"/>
</request>
</interface>
@@ -1939,7 +2612,7 @@
hidden, or if a NULL wl_buffer is applied. These rules apply
recursively through the tree of surfaces.
- The behaviour of wl_surface.commit request on a sub-surface
+ The behaviour of a wl_surface.commit request on a sub-surface
depends on the sub-surface's mode. The possible modes are
synchronized and desynchronized, see methods
wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
@@ -1981,28 +2654,30 @@
<request name="destroy" type="destructor">
<description summary="remove sub-surface interface">
The sub-surface interface is removed from the wl_surface object
- that was turned into a sub-surface with
+ that was turned into a sub-surface with a
wl_subcompositor.get_subsurface request. The wl_surface's association
to the parent is deleted, and the wl_surface loses its role as
- a sub-surface. The wl_surface is unmapped.
+ a sub-surface. The wl_surface is unmapped immediately.
</description>
</request>
<enum name="error">
<entry name="bad_surface" value="0"
- summary="wl_surface is not a sibling or the parent"/>
+ summary="wl_surface is not a sibling or the parent"/>
</enum>
<request name="set_position">
<description summary="reposition the sub-surface">
This schedules a sub-surface position change.
- The sub-surface will be moved so, that its origin (top-left
+ The sub-surface will be moved so that its origin (top left
corner pixel) will be at the location x, y of the parent surface
coordinate system. The coordinates are not restricted to the parent
surface area. Negative values are allowed.
- The next wl_surface.commit on the parent surface will reset
- the sub-surface's position to the scheduled coordinates.
+ The scheduled coordinates will take effect whenever the state of the
+ parent surface is applied. When this happens depends on whether the
+ parent surface is in synchronized mode or not. See
+ wl_subsurface.set_sync and wl_subsurface.set_desync for details.
If more than one set_position request is invoked by the client before
the commit of the parent surface, the position of a new request always
@@ -2010,9 +2685,8 @@
The initial position is 0, 0.
</description>
-
- <arg name="x" type="int" summary="coordinate in the parent surface"/>
- <arg name="y" type="int" summary="coordinate in the parent surface"/>
+ <arg name="x" type="int" summary="x coordinate in the parent surface"/>
+ <arg name="y" type="int" summary="y coordinate in the parent surface"/>
</request>
<request name="place_above">
@@ -2024,32 +2698,32 @@
will cause a protocol error.
The z-order is double-buffered. Requests are handled in order and
- applied immediately to a pending state, then committed to the active
- state on the next commit of the parent surface.
- See wl_surface.commit and wl_subcompositor.get_subsurface.
+ applied immediately to a pending state. The final pending state is
+ copied to the active state the next time the state of the parent
+ surface is applied. When this happens depends on whether the parent
+ surface is in synchronized mode or not. See wl_subsurface.set_sync and
+ wl_subsurface.set_desync for details.
A new sub-surface is initially added as the top-most in the stack
of its siblings and parent.
</description>
-
<arg name="sibling" type="object" interface="wl_surface"
- summary="the reference surface"/>
+ summary="the reference surface"/>
</request>
<request name="place_below">
<description summary="restack the sub-surface">
- The sub-surface is placed just below of the reference surface.
+ The sub-surface is placed just below the reference surface.
See wl_subsurface.place_above.
</description>
-
<arg name="sibling" type="object" interface="wl_surface"
- summary="the reference surface"/>
+ summary="the reference surface"/>
</request>
<request name="set_sync">
<description summary="set sub-surface to synchronized mode">
Change the commit behaviour of the sub-surface to synchronized
- mode, also described as the parent dependant mode.
+ mode, also described as the parent dependent mode.
In synchronized mode, wl_surface.commit on a sub-surface will
accumulate the committed state in a cache, but the state will
@@ -2077,7 +2751,7 @@
If cached state exists when wl_surface.commit is called in
desynchronized mode, the pending state is added to the cached
- state, and applied as whole. This invalidates the cache.
+ state, and applied as a whole. This invalidates the cache.
Note: even if a sub-surface is set to desynchronized, a parent
sub-surface may override it to behave as synchronized. For details,
@@ -2087,7 +2761,6 @@
the cached state is applied on set_desync.
</description>
</request>
-
</interface>
</protocol>
diff --git a/src/client/client.pro b/src/client/client.pro
index 30f32dd7e..db91bd691 100644
--- a/src/client/client.pro
+++ b/src/client/client.pro
@@ -15,8 +15,12 @@ use_gold_linker: CONFIG += no_linker_version_script
CONFIG -= precompile_header
CONFIG += link_pkgconfig wayland-scanner
-qtConfig(xkbcommon): \
- QMAKE_USE_PRIVATE += xkbcommon
+qtConfig(xkbcommon) {
+ QT_FOR_PRIVATE += xkbcommon_support-private
+}
+
+qtHaveModule(linuxaccessibility_support_private): \
+ QT += linuxaccessibility_support_private
QMAKE_USE += wayland-client
@@ -29,9 +33,7 @@ WAYLANDCLIENTSOURCES += \
../extensions/qt-windowmanager.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
../3rdparty/protocol/xdg-output-unstable-v1.xml \
-
-WAYLANDCLIENTSOURCES_SYSTEM += \
- ../3rdparty/protocol/wayland.xml \
+ ../3rdparty/protocol/wayland.xml
SOURCES += qwaylandintegration.cpp \
qwaylandnativeinterface.cpp \
@@ -47,7 +49,6 @@ SOURCES += qwaylandintegration.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
- ../shared/qwaylandxkb.cpp \
../shared/qwaylandinputmethodeventbuilder.cpp \
qwaylandabstractdecoration.cpp \
qwaylanddecorationfactory.cpp \
@@ -81,7 +82,6 @@ HEADERS += qwaylandintegration_p.h \
qtwaylandclientglobal_p.h \
../shared/qwaylandinputmethodeventbuilder_p.h \
../shared/qwaylandmimehelper_p.h \
- ../shared/qwaylandxkb_p.h \
../shared/qwaylandsharedmemoryformathelper_p.h \
qtConfig(clipboard) {
diff --git a/src/client/configure.json b/src/client/configure.json
index 586da6f66..93c5d4e49 100644
--- a/src/client/configure.json
+++ b/src/client/configure.json
@@ -88,6 +88,36 @@
"condition": "features.draganddrop || features.clipboard",
"output": [ "privateFeature" ]
},
+ "wayland-client-fullscreen-shell-v1": {
+ "label": "fullscreen-shell-v1",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
+ "wayland-client-ivi-shell": {
+ "label": "ivi-shell",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
+ "wayland-client-wl-shell": {
+ "label": "wl-shell (deprecated)",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
+ "wayland-client-xdg-shell": {
+ "label": "xdg-shell",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
+ "wayland-client-xdg-shell-v5": {
+ "label": "xdg-shell unstable v5 (deprecated)",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
+ "wayland-client-xdg-shell-v6": {
+ "label": "xdg-shell unstable v6",
+ "condition": "features.wayland-client",
+ "output": [ "privateFeature" ]
+ },
"wayland-egl": {
"label": "EGL",
"condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl",
@@ -152,6 +182,17 @@
"wayland-shm-emulation-server-buffer"
]
},
+ {
+ "section": "Qt Wayland Client Shell Integrations",
+ "condition": "features.wayland-client",
+ "entries": [
+ "wayland-client-xdg-shell",
+ "wayland-client-xdg-shell-v5",
+ "wayland-client-xdg-shell-v6",
+ "wayland-client-ivi-shell",
+ "wayland-client-wl-shell"
+ ]
+ },
"wayland-client"
]
}
diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
index 7439087d8..632429bef 100644
--- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
+++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h
@@ -85,7 +85,7 @@ public:
void *userData() const;
protected:
- Format m_format;
+ Format m_format = RGBA32;
QSize m_size;
private:
diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp
index 98a0b17d5..87dd6cea0 100644
--- a/src/client/qwaylandabstractdecoration.cpp
+++ b/src/client/qwaylandabstractdecoration.cpp
@@ -120,16 +120,17 @@ const QImage &QWaylandAbstractDecoration::contentImage()
{
Q_D(QWaylandAbstractDecoration);
if (d->m_isDirty) {
- //Update the decoration backingstore
+ // Update the decoration backingstore
- const int scale = waylandWindow()->scale();
- const QSize imageSize = window()->frameGeometry().size() * scale;
+ const int bufferScale = waylandWindow()->scale();
+ const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale;
d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
- d->m_decorationContentImage.setDevicePixelRatio(scale);
+ // Only scale by buffer scale, not QT_SCALE_FACTOR etc.
+ d->m_decorationContentImage.setDevicePixelRatio(bufferScale);
d->m_decorationContentImage.fill(Qt::transparent);
this->paint(&d->m_decorationContentImage);
- QRegion damage = marginsRegion(window()->geometry().size(), window()->frameMargins());
+ QRegion damage = marginsRegion(waylandWindow()->surfaceSize(), waylandWindow()->frameMargins());
for (QRect r : damage)
waylandWindow()->damage(r);
@@ -151,11 +152,11 @@ void QWaylandAbstractDecoration::setMouseButtons(Qt::MouseButtons mb)
d->m_mouseButtons = mb;
}
-void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize resize, Qt::MouseButtons buttons)
+void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons)
{
Q_D(QWaylandAbstractDecoration);
if (isLeftClicked(buttons) && d->m_wayland_window->shellSurface()) {
- d->m_wayland_window->shellSurface()->resize(inputDevice, resize);
+ d->m_wayland_window->shellSurface()->resize(inputDevice, edges);
inputDevice->removeMouseButtonFromState(Qt::LeftButton);
}
}
@@ -169,20 +170,29 @@ void QWaylandAbstractDecoration::startMove(QWaylandInputDevice *inputDevice, Qt:
}
}
+void QWaylandAbstractDecoration::showWindowMenu(QWaylandInputDevice *inputDevice)
+{
+ Q_D(QWaylandAbstractDecoration);
+ if (auto *s = d->m_wayland_window->shellSurface())
+ s->showWindowMenu(inputDevice);
+}
+
bool QWaylandAbstractDecoration::isLeftClicked(Qt::MouseButtons newMouseButtonState)
{
Q_D(QWaylandAbstractDecoration);
- if (!(d->m_mouseButtons & Qt::LeftButton) && (newMouseButtonState & Qt::LeftButton))
- return true;
- return false;
+ return !(d->m_mouseButtons & Qt::LeftButton) && (newMouseButtonState & Qt::LeftButton);
+}
+
+bool QWaylandAbstractDecoration::isRightClicked(Qt::MouseButtons newMouseButtonState)
+{
+ Q_D(QWaylandAbstractDecoration);
+ return !(d->m_mouseButtons & Qt::RightButton) && (newMouseButtonState & Qt::RightButton);
}
bool QWaylandAbstractDecoration::isLeftReleased(Qt::MouseButtons newMouseButtonState)
{
Q_D(QWaylandAbstractDecoration);
- if ((d->m_mouseButtons & Qt::LeftButton) && !(newMouseButtonState & Qt::LeftButton))
- return true;
- return false;
+ return (d->m_mouseButtons & Qt::LeftButton) && !(newMouseButtonState & Qt::LeftButton);
}
bool QWaylandAbstractDecoration::isDirty() const
diff --git a/src/client/qwaylandabstractdecoration_p.h b/src/client/qwaylandabstractdecoration_p.h
index 84a6d4dd7..81c8e1771 100644
--- a/src/client/qwaylandabstractdecoration_p.h
+++ b/src/client/qwaylandabstractdecoration_p.h
@@ -61,8 +61,6 @@
#include <QtGui/QImage>
#include <QtWaylandClient/qtwaylandclientglobal.h>
-#include <wayland-client.h>
-
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
@@ -105,10 +103,12 @@ protected:
void setMouseButtons(Qt::MouseButtons mb);
- void startResize(QWaylandInputDevice *inputDevice,enum wl_shell_surface_resize resize, Qt::MouseButtons buttons);
+ void startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons);
void startMove(QWaylandInputDevice *inputDevice, Qt::MouseButtons buttons);
+ void showWindowMenu(QWaylandInputDevice *inputDevice);
bool isLeftClicked(Qt::MouseButtons newMouseButtonState);
+ bool isRightClicked(Qt::MouseButtons newMouseButtonState);
bool isLeftReleased(Qt::MouseButtons newMouseButtonState);
};
diff --git a/src/client/qwaylandbuffer_p.h b/src/client/qwaylandbuffer_p.h
index eea090f35..945f1279a 100644
--- a/src/client/qwaylandbuffer_p.h
+++ b/src/client/qwaylandbuffer_p.h
@@ -56,8 +56,7 @@
#include <QtCore/QSize>
#include <QtCore/QRect>
-#include <wayland-client.h>
-#include <wayland-client-protocol.h>
+#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
QT_BEGIN_NAMESPACE
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp
index 6947e97f1..8b2ed036d 100644
--- a/src/client/qwaylandcursor.cpp
+++ b/src/client/qwaylandcursor.cpp
@@ -41,7 +41,6 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandinputdevice_p.h"
-#include "qwaylandscreen_p.h"
#include "qwaylandshmbackingstore_p.h"
#include <QtGui/QImageReader>
@@ -53,12 +52,6 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
-QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size)
-{
- static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
- return create(shm, size, themeName);
-}
-
QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName)
{
QByteArray nameBytes = themeName.toLocal8Bit();
@@ -244,56 +237,32 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
return image;
}
-QWaylandCursor::QWaylandCursor(QWaylandScreen *screen)
- : mDisplay(screen->display())
- , mCursorTheme(mDisplay->loadCursorTheme(screen->devicePixelRatio()))
+QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)
+ : mDisplay(display)
{
}
-QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapImage(const QCursor *cursor)
+QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor)
{
- if (cursor->shape() != Qt::BitmapCursor)
- return QSharedPointer<QWaylandShmBuffer>();
-
+ Q_ASSERT(cursor->shape() == Qt::BitmapCursor);
const QImage &img = cursor->pixmap().toImage();
- QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(mDisplay, img.size(), img.format()));
- memcpy(buffer->image()->bits(), img.bits(), img.sizeInBytes());
- return buffer;
-}
-
-struct wl_cursor_image *QWaylandCursor::cursorImage(Qt::CursorShape shape)
-{
- if (!mCursorTheme)
- return nullptr;
- return mCursorTheme->cursorImage(shape);
+ QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format()));
+ memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes()));
+ return std::move(buffer);
}
void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window)
{
- const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
-
- if (newShape == Qt::BlankCursor) {
- mDisplay->setCursor(nullptr, nullptr, 1);
- return;
- }
-
- if (newShape == Qt::BitmapCursor) {
- mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot(), window->screen()->devicePixelRatio());
- return;
- }
-
- if (!mCursorTheme) {
- qCWarning(lcQpaWayland) << "Can't set cursor from shape with no cursor theme";
- return;
- }
-
- if (struct ::wl_cursor_image *image = mCursorTheme->cursorImage(newShape)) {
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
- mDisplay->setCursor(buffer, image, window->screen()->devicePixelRatio());
- return;
- }
-
- qCWarning(lcQpaWayland) << "Unable to change to cursor" << cursor;
+ Q_UNUSED(window);
+ // Create the buffer here so we don't have to create one per input device
+ QSharedPointer<QWaylandBuffer> bitmapBuffer;
+ if (cursor && cursor->shape() == Qt::BitmapCursor)
+ bitmapBuffer = cursorBitmapBuffer(mDisplay, cursor);
+
+ int fallbackOutputScale = int(window->devicePixelRatio());
+ const auto seats = mDisplay->inputDevices();
+ for (auto *seat : seats)
+ seat->setCursor(cursor, bitmapBuffer, fallbackOutputScale);
}
void QWaylandCursor::pointerEvent(const QMouseEvent &event)
@@ -312,6 +281,6 @@ void QWaylandCursor::setPos(const QPoint &pos)
qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland";
}
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h
index 71f9cd1b8..6c48fb628 100644
--- a/src/client/qwaylandcursor_p.h
+++ b/src/client/qwaylandcursor_p.h
@@ -73,7 +73,6 @@ class QWaylandShm;
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
{
public:
- static QWaylandCursorTheme *create(QWaylandShm *shm, int size);
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
~QWaylandCursorTheme();
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
@@ -122,19 +121,18 @@ private:
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor
{
public:
- QWaylandCursor(QWaylandScreen *screen);
+ explicit QWaylandCursor(QWaylandDisplay *display);
void changeCursor(QCursor *cursor, QWindow *window) override;
void pointerEvent(const QMouseEvent &event) override;
QPoint pos() const override;
void setPos(const QPoint &pos) override;
- QSharedPointer<QWaylandBuffer> cursorBitmapImage(const QCursor *cursor);
+ static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor);
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
private:
QWaylandDisplay *mDisplay = nullptr;
- QWaylandCursorTheme *mCursorTheme = nullptr;
QPoint mLastPos;
};
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 82003a308..4a91c1c96 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -50,6 +50,7 @@
#endif
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevicemanager_p.h"
+#include "qwaylanddatadevice_p.h"
#endif
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
@@ -71,6 +72,7 @@
#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/QAbstractEventDispatcher>
+#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtCore/QDebug>
@@ -141,7 +143,18 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
mWindowManagerIntegration.reset(new QWaylandWindowManagerIntegration(this));
+#if QT_CONFIG(xkbcommon)
+ mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
+ if (!mXkbContext)
+ qCWarning(lcQpaWayland, "failed to create xkb context");
+#endif
+
forceRoundTrip();
+
+ if (!mWaitingScreens.isEmpty()) {
+ // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
+ forceRoundTrip();
+ }
}
QWaylandDisplay::~QWaylandDisplay(void)
@@ -153,15 +166,16 @@ QWaylandDisplay::~QWaylandDisplay(void)
mInputDevices.clear();
foreach (QWaylandScreen *screen, mScreens) {
- mWaylandIntegration->destroyScreen(screen);
+ QWindowSystemInterface::handleScreenRemoved(screen);
}
mScreens.clear();
+ qDeleteAll(mWaitingScreens);
#if QT_CONFIG(wayland_datadevice)
delete mDndSelectionHandler.take();
#endif
#if QT_CONFIG(cursor)
- qDeleteAll(mCursorThemesBySize);
+ qDeleteAll(mCursorThemes);
#endif
if (mDisplay)
wl_display_disconnect(mDisplay);
@@ -250,6 +264,14 @@ QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
return nullptr;
}
+void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
+{
+ if (!mWaitingScreens.removeOne(screen))
+ return;
+ mScreens.append(screen);
+ QWindowSystemInterface::handleScreenAdded(screen);
+}
+
void QWaylandDisplay::waitForScreens()
{
flushRequests();
@@ -271,16 +293,10 @@ void QWaylandDisplay::waitForScreens()
void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version)
{
- Q_UNUSED(version);
-
struct ::wl_registry *registry = object();
if (interface == QStringLiteral("wl_output")) {
- QWaylandScreen *screen = new QWaylandScreen(this, version, id);
- mScreens.append(screen);
- // We need to get the output events before creating surfaces
- forceRoundTrip();
- mWaylandIntegration->screenAdded(screen);
+ mWaitingScreens << new QWaylandScreen(this, version, id);
} else if (interface == QStringLiteral("wl_compositor")) {
mCompositorVersion = qMin((int)version, 3);
mCompositor.init(registry, id, mCompositorVersion);
@@ -301,19 +317,22 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
} else if (interface == QStringLiteral("zqt_key_v1")) {
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
- } else if (interface == QStringLiteral("zwp_text_input_manager_v2")) {
+ } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
- foreach (QWaylandInputDevice *inputDevice, mInputDevices) {
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat())));
- }
+ mWaylandIntegration->reconfigureInputContext();
} else if (interface == QStringLiteral("qt_hardware_integration")) {
- mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
- // make a roundtrip here since we need to receive the events sent by
- // qt_hardware_integration before creating windows
- forceRoundTrip();
+ bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
+ if (!disableHardwareIntegration) {
+ mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
+ // make a roundtrip here since we need to receive the events sent by
+ // qt_hardware_integration before creating windows
+ forceRoundTrip();
+ }
} else if (interface == QLatin1String("zxdg_output_manager_v1")) {
- mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, 1));
- for (auto *screen : qAsConst(mScreens))
+ mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version))));
+ for (auto *screen : qAsConst(mWaitingScreens))
screen->initXdgOutput(xdgOutputManager());
forceRoundTrip();
}
@@ -330,14 +349,28 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
RegistryGlobal &global = mGlobals[i];
if (global.id == id) {
if (global.interface == QStringLiteral("wl_output")) {
+ for (auto *screen : mWaitingScreens) {
+ if (screen->outputId() == id) {
+ mWaitingScreens.removeOne(screen);
+ delete screen;
+ break;
+ }
+ }
+
foreach (QWaylandScreen *screen, mScreens) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
- mWaylandIntegration->destroyScreen(screen);
+ QWindowSystemInterface::handleScreenRemoved(screen);
break;
}
}
}
+ if (global.interface == QStringLiteral("zwp_text_input_manager_v2")) {
+ mTextInputManager.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
mGlobals.removeAt(i);
break;
}
@@ -539,40 +572,20 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
#if QT_CONFIG(cursor)
-void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr)
-{
- /* Qt doesn't tell us which input device we should set the cursor
- * for, so set it for all devices. */
- for (int i = 0; i < mInputDevices.count(); i++) {
- QWaylandInputDevice *inputDevice = mInputDevices.at(i);
- inputDevice->setCursor(buffer, image, dpr);
- }
-}
-
-void QWaylandDisplay::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr)
+QWaylandCursor *QWaylandDisplay::waylandCursor()
{
- /* Qt doesn't tell us which input device we should set the cursor
- * for, so set it for all devices. */
- for (int i = 0; i < mInputDevices.count(); i++) {
- QWaylandInputDevice *inputDevice = mInputDevices.at(i);
- inputDevice->setCursor(buffer, hotSpot, dpr);
- }
+ if (!mCursor)
+ mCursor.reset(new QWaylandCursor(this));
+ return mCursor.data();
}
-QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
+QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
{
- constexpr int defaultCursorSize = 32;
- static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
- int cursorSize = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
-
- if (compositorVersion() >= 3) // set_buffer_scale is not supported on earlier versions
- cursorSize *= devicePixelRatio;
-
- if (auto *theme = mCursorThemesBySize.value(cursorSize, nullptr))
+ if (auto *theme = mCursorThemes.value({name, pixelSize}, nullptr))
return theme;
- if (auto *theme = QWaylandCursorTheme::create(shm(), cursorSize)) {
- mCursorThemesBySize[cursorSize] = theme;
+ if (auto *theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) {
+ mCursorThemes[{name, pixelSize}] = theme;
return theme;
}
@@ -581,6 +594,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
#endif // QT_CONFIG(cursor)
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 6bf6abd5d..558d8d9b5 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -59,12 +59,16 @@
#include <QtCore/QWaitCondition>
#include <QtCore/QLoggingCategory>
-#include <wayland-client.h>
-
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
#include <QtWaylandClient/private/qwaylandshm_p.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
+
+#if QT_CONFIG(xkbcommon)
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
+#endif
+
struct wl_cursor_image;
QT_BEGIN_NAMESPACE
@@ -95,6 +99,7 @@ class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
class QWaylandShellIntegration;
+class QWaylandCursor;
class QWaylandCursorTheme;
typedef void (*RegistryListener)(void *data,
@@ -110,9 +115,14 @@ public:
QWaylandDisplay(QWaylandIntegration *waylandIntegration);
~QWaylandDisplay(void) override;
+#if QT_CONFIG(xkbcommon)
+ struct xkb_context *xkbContext() const { return mXkbContext.get(); }
+#endif
+
QList<QWaylandScreen *> screens() const { return mScreens; }
QWaylandScreen *screenForOutput(struct wl_output *output) const;
+ void handleScreenInitialized(QWaylandScreen *screen);
struct wl_surface *createSurface(void *handle);
struct ::wl_region *createRegion(const QRegion &qregion);
@@ -123,9 +133,8 @@ public:
QWaylandWindowManagerIntegration *windowManagerIntegration() const;
#if QT_CONFIG(cursor)
- void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr);
- void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr);
- QWaylandCursorTheme *loadCursorTheme(qreal devicePixelRatio);
+ QWaylandCursor *waylandCursor();
+ QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize);
#endif
struct wl_display *wl_display() const { return mDisplay; }
struct ::wl_registry *wl_registry() { return object(); }
@@ -146,6 +155,7 @@ public:
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QtWayland::zxdg_output_manager_v1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
+ bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; }
struct RegistryGlobal {
uint32_t id;
@@ -210,12 +220,14 @@ private:
struct wl_display *mDisplay = nullptr;
QtWayland::wl_compositor mCompositor;
QScopedPointer<QWaylandShm> mShm;
+ QList<QWaylandScreen *> mWaitingScreens;
QList<QWaylandScreen *> mScreens;
QList<QWaylandInputDevice *> mInputDevices;
QList<Listener> mRegistryListeners;
QWaylandIntegration *mWaylandIntegration = nullptr;
#if QT_CONFIG(cursor)
- QMap<int, QWaylandCursorTheme *> mCursorThemesBySize;
+ QMap<std::pair<QString, int>, QWaylandCursorTheme *> mCursorThemes; // theme name and size
+ QScopedPointer<QWaylandCursor> mCursor;
#endif
#if QT_CONFIG(wayland_datadevice)
QScopedPointer<QWaylandDataDeviceManager> mDndSelectionHandler;
@@ -229,10 +241,10 @@ private:
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
QSocketNotifier *mReadNotifier = nullptr;
- int mFd;
- int mWritableNotificationFd;
+ int mFd = -1;
+ int mWritableNotificationFd = -1;
QList<RegistryGlobal> mGlobals;
- int mCompositorVersion;
+ int mCompositorVersion = -1;
uint32_t mLastInputSerial = 0;
QWaylandInputDevice *mLastInputDevice = nullptr;
QPointer<QWaylandWindow> mLastInputWindow;
@@ -241,8 +253,17 @@ private:
struct wl_callback *mSyncCallback = nullptr;
static const wl_callback_listener syncCallbackListener;
+ bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
+ bool mUsingInputContextFromCompositor = false;
+
void registry_global(uint32_t id, const QString &interface, uint32_t version) override;
void registry_global_remove(uint32_t id) override;
+
+#if QT_CONFIG(xkbcommon)
+ QXkbCommon::ScopedXKBContext mXkbContext;
+#endif
+
+ friend class QWaylandIntegration;
};
}
diff --git a/src/client/qwaylandextendedsurface_p.h b/src/client/qwaylandextendedsurface_p.h
index cd604f342..d71ac6be9 100644
--- a/src/client/qwaylandextendedsurface_p.h
+++ b/src/client/qwaylandextendedsurface_p.h
@@ -56,7 +56,6 @@
#include <QtWaylandClient/qtwaylandclientglobal.h>
-#include <wayland-client.h>
#include <QtWaylandClient/private/qwayland-surface-extension.h>
QT_BEGIN_NAMESPACE
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index e85faaf8e..c6f287dda 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -50,7 +50,6 @@
#include "qwaylandinputdevice_p.h"
#include "qwaylandinputmethodeventbuilder_p.h"
#include "qwaylandwindow_p.h"
-#include "qwaylandxkb_p.h"
QT_BEGIN_NAMESPACE
@@ -315,6 +314,7 @@ void QWaylandTextInput::zwp_text_input_v2_delete_surrounding_text(uint32_t befor
void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers)
{
+#if QT_CONFIG(xkbcommon)
if (m_resetCallback) {
qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed";
return;
@@ -325,13 +325,18 @@ void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, ui
Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers);
- QEvent::Type type = QWaylandXkb::toQtEventType(state);
- QString text;
- int qtkey;
- std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, qtModifiers);
+ QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease;
+ QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym);
+ int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers);
QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(),
time, type, qtkey, qtModifiers, text);
+#else
+ Q_UNUSED(time);
+ Q_UNUSED(sym);
+ Q_UNUSED(state);
+ Q_UNUSED(modifiers);
+#endif
}
void QWaylandTextInput::zwp_text_input_v2_language(const QString &language)
diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h
index 93300e1f5..10132dfe1 100644
--- a/src/client/qwaylandinputcontext_p.h
+++ b/src/client/qwaylandinputcontext_p.h
@@ -62,6 +62,9 @@
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
#include <qwaylandinputmethodeventbuilder_p.h>
+struct wl_callback;
+struct wl_callback_listener;
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods)
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 90e138a3d..17c408a3d 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -51,7 +51,6 @@
#include "qwaylandcursor_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
-#include "../shared/qwaylandxkb_p.h"
#include "qwaylandinputcontext_p.h"
#include <QtGui/private/qpixmap_raster_p.h>
@@ -70,10 +69,6 @@
#include <QtGui/QGuiApplication>
-#if QT_CONFIG(xkbcommon)
-#include <xkbcommon/xkbcommon-compose.h>
-#endif
-
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -81,85 +76,51 @@ namespace QtWaylandClient {
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
- connect(&mRepeatTimer, SIGNAL(timeout()), this, SLOT(repeatKey()));
+ mRepeatTimer.callOnTimeout([&]() {
+ if (!focusWindow()) {
+ // We destroyed the keyboard focus surface, but the server didn't get the message yet...
+ // or the server didn't send an enter event first.
+ return;
+ }
+ mRepeatTimer.setInterval(mRepeatRate);
+ handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers,
+ mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers,
+ mRepeatKey.text, true);
+ handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, mRepeatKey.modifiers,
+ mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers,
+ mRepeatKey.text, true);
+ });
}
#if QT_CONFIG(xkbcommon)
-bool QWaylandInputDevice::Keyboard::createDefaultKeyMap()
+bool QWaylandInputDevice::Keyboard::createDefaultKeymap()
{
- if (mXkbContext && mXkbMap && mXkbState) {
- return true;
- }
+ struct xkb_context *ctx = mParent->mQDisplay->xkbContext();
+ if (!ctx)
+ return false;
- xkb_rule_names names;
- names.rules = strdup("evdev");
- names.model = strdup("pc105");
- names.layout = strdup("us");
- names.variant = strdup("");
- names.options = strdup("");
+ struct xkb_rule_names names;
+ names.rules = "evdev";
+ names.model = "pc105";
+ names.layout = "us";
+ names.variant = "";
+ names.options = "";
- mXkbContext = xkb_context_new(xkb_context_flags(0));
- if (mXkbContext) {
- mXkbMap = xkb_map_new_from_names(mXkbContext, &names, xkb_map_compile_flags(0));
- if (mXkbMap) {
- mXkbState = xkb_state_new(mXkbMap);
- }
- }
+ mXkbKeymap.reset(xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS));
+ if (mXkbKeymap)
+ mXkbState.reset(xkb_state_new(mXkbKeymap.get()));
- if (!mXkbContext || !mXkbMap || !mXkbState) {
- qWarning() << "xkb_map_new_from_names failed, no key input";
+ if (!mXkbKeymap || !mXkbState) {
+ qCWarning(lcQpaWayland, "failed to create default keymap");
return false;
}
- createComposeState();
- return true;
-}
-
-void QWaylandInputDevice::Keyboard::releaseKeyMap()
-{
- if (mXkbState)
- xkb_state_unref(mXkbState);
- if (mXkbMap)
- xkb_map_unref(mXkbMap);
- if (mXkbContext)
- xkb_context_unref(mXkbContext);
-}
-
-void QWaylandInputDevice::Keyboard::createComposeState()
-{
- static const char *locale = nullptr;
- if (!locale) {
- locale = getenv("LC_ALL");
- if (!locale)
- locale = getenv("LC_CTYPE");
- if (!locale)
- locale = getenv("LANG");
- if (!locale)
- locale = "C";
- }
-
- mXkbComposeTable = xkb_compose_table_new_from_locale(mXkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
- if (mXkbComposeTable)
- mXkbComposeState = xkb_compose_state_new(mXkbComposeTable, XKB_COMPOSE_STATE_NO_FLAGS);
-}
-void QWaylandInputDevice::Keyboard::releaseComposeState()
-{
- if (mXkbComposeState)
- xkb_compose_state_unref(mXkbComposeState);
- if (mXkbComposeTable)
- xkb_compose_table_unref(mXkbComposeTable);
- mXkbComposeState = nullptr;
- mXkbComposeTable = nullptr;
+ return true;
}
-
#endif
QWaylandInputDevice::Keyboard::~Keyboard()
{
-#if QT_CONFIG(xkbcommon)
- releaseComposeState();
- releaseKeyMap();
-#endif
if (mFocus)
QWindowSystemInterface::handleWindowActivated(nullptr);
if (mParent->mVersion >= 3)
@@ -168,13 +129,13 @@ QWaylandInputDevice::Keyboard::~Keyboard()
wl_keyboard_destroy(object());
}
-void QWaylandInputDevice::Keyboard::stopRepeat()
+QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const
{
- mRepeatTimer.stop();
+ return mFocus ? QWaylandWindow::fromWlSurface(mFocus) : nullptr;
}
-QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *p)
- : mParent(p)
+QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
+ : mParent(seat)
{
}
@@ -186,6 +147,191 @@ QWaylandInputDevice::Pointer::~Pointer()
wl_pointer_destroy(object());
}
+#if QT_CONFIG(cursor)
+
+class CursorSurface : public QObject, public QtWayland::wl_surface
+{
+public:
+ explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
+ : m_pointer(pointer)
+ {
+ init(display->createSurface(this));
+ //TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead.
+ m_version = display->compositorVersion();
+ connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) {
+ int oldScale = outputScale();
+ if (!m_screens.removeOne(static_cast<QWaylandScreen *>(screen->handle())))
+ return;
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ });
+ }
+
+ void hide()
+ {
+ uint serial = m_pointer->mEnterSerial;
+ Q_ASSERT(serial);
+ m_pointer->set_cursor(serial, nullptr, 0, 0);
+ m_setSerial = 0;
+ }
+
+ // Size and hotspot are in surface coordinates
+ void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale)
+ {
+ // Calling code needs to ensure buffer scale is supported if != 1
+ Q_ASSERT(bufferScale == 1 || m_version >= 3);
+
+ auto enterSerial = m_pointer->mEnterSerial;
+ if (m_setSerial < enterSerial || m_hotspot != hotspot) {
+ m_pointer->set_cursor(m_pointer->mEnterSerial, object(), hotspot.x(), hotspot.y());
+ m_setSerial = enterSerial;
+ m_hotspot = hotspot;
+ }
+
+ if (m_version >= 3)
+ set_buffer_scale(bufferScale);
+
+ attach(buffer, 0, 0);
+ damage(0, 0, size.width(), size.height());
+ commit();
+ }
+
+ int outputScale() const
+ {
+ int scale = 0;
+ for (auto *screen : m_screens)
+ scale = qMax(scale, screen->scale());
+ return scale;
+ }
+
+protected:
+ void surface_enter(struct ::wl_output *output) override
+ {
+ int oldScale = outputScale();
+ auto *screen = QWaylandScreen::fromWlOutput(output);
+ if (m_screens.contains(screen))
+ return;
+
+ m_screens.append(screen);
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ }
+
+ void surface_leave(struct ::wl_output *output) override
+ {
+ int oldScale = outputScale();
+ auto *screen = QWaylandScreen::fromWlOutput(output);
+
+ if (!m_screens.removeOne(screen))
+ return;
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ }
+
+private:
+ QWaylandInputDevice::Pointer *m_pointer = nullptr;
+ uint m_version = 0;
+ uint m_setSerial = 0;
+ QPoint m_hotspot;
+ QVector<QWaylandScreen *> m_screens;
+};
+
+QString QWaylandInputDevice::Pointer::cursorThemeName() const
+{
+ static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
+ return themeName;
+}
+
+int QWaylandInputDevice::Pointer::cursorSize() const
+{
+ constexpr int defaultCursorSize = 32;
+ static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
+ return xCursorSize > 0 ? xCursorSize : defaultCursorSize;
+}
+
+int QWaylandInputDevice::Pointer::idealCursorScale() const
+{
+ // set_buffer_scale is not supported on earlier versions
+ if (seat()->mQDisplay->compositorVersion() < 3)
+ return 1;
+
+ if (auto *s = mCursor.surface.data()) {
+ if (s->outputScale() > 0)
+ return s->outputScale();
+ }
+
+ return seat()->mCursor.fallbackOutputScale;
+}
+
+void QWaylandInputDevice::Pointer::updateCursorTheme()
+{
+ int scale = idealCursorScale();
+ int pixelSize = cursorSize() * scale;
+ auto *display = seat()->mQDisplay;
+ mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize);
+ if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) {
+ int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square
+ while (scale > 1 && arrowPixelSize / scale < cursorSize())
+ --scale;
+ } else {
+ qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
+ }
+ mCursor.themeBufferScale = scale;
+}
+
+void QWaylandInputDevice::Pointer::updateCursor()
+{
+ if (mEnterSerial == 0)
+ return;
+
+ auto shape = seat()->mCursor.shape;
+
+ if (shape == Qt::BlankCursor) {
+ if (mCursor.surface)
+ mCursor.surface->hide();
+ return;
+ }
+
+ if (shape == Qt::BitmapCursor) {
+ auto buffer = seat()->mCursor.bitmapBuffer;
+ if (!buffer) {
+ qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
+ return;
+ }
+ auto hotspot = seat()->mCursor.hotspot;
+ int bufferScale = seat()->mCursor.bitmapScale;
+ getOrCreateCursorSurface()->update(buffer->buffer(), hotspot, buffer->size(), bufferScale);
+ return;
+ }
+
+ if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale)
+ updateCursorTheme();
+
+ // Set from shape using theme
+ if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) {
+ struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
+ int bufferScale = mCursor.themeBufferScale;
+ QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
+ QSize size = QSize(image->width, image->height) / bufferScale;
+ getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale);
+ return;
+ }
+
+ qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
+}
+
+CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface()
+{
+ if (!mCursor.surface)
+ mCursor.surface.reset(new CursorSurface(this, seat()->mQDisplay));
+ return mCursor.surface.get();
+}
+
+#endif // QT_CONFIG(cursor)
+
QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
: mParent(p)
{
@@ -211,9 +357,9 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
}
#endif
- if (mQDisplay->textInputManager()) {
- mTextInput = new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat()));
- }
+ if (mQDisplay->textInputManager())
+ mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
+
}
QWaylandInputDevice::~QWaylandInputDevice()
@@ -238,7 +384,6 @@ void QWaylandInputDevice::seat_capabilities(uint32_t caps)
if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) {
mPointer = createPointer(this);
mPointer->init(get_pointer());
- pointerSurface = mQDisplay->createSurface(this);
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
delete mPointer;
mPointer = nullptr;
@@ -275,12 +420,6 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice
return new Touch(device);
}
-void QWaylandInputDevice::handleWindowDestroyed(QWaylandWindow *window)
-{
- if (mKeyboard && window == mKeyboard->mFocus)
- mKeyboard->stopRepeat();
-}
-
void QWaylandInputDevice::handleEndDrag()
{
if (mTouch)
@@ -303,12 +442,12 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
{
- mTextInput = textInput;
+ mTextInput.reset(textInput);
}
QWaylandTextInput *QWaylandInputDevice::textInput() const
{
- return mTextInput;
+ return mTextInput.data();
}
void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
@@ -324,7 +463,7 @@ QWaylandWindow *QWaylandInputDevice::pointerFocus() const
QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
{
- return mKeyboard ? mKeyboard->mFocus : nullptr;
+ return mKeyboard ? mKeyboard->focusWindow() : nullptr;
}
QWaylandWindow *QWaylandInputDevice::touchFocus() const
@@ -332,6 +471,11 @@ QWaylandWindow *QWaylandInputDevice::touchFocus() const
return mTouch ? mTouch->mFocus : nullptr;
}
+QPointF QWaylandInputDevice::pointerSurfacePosition() const
+{
+ return mPointer ? mPointer->mSurfacePos : QPointF();
+}
+
Qt::KeyboardModifiers QWaylandInputDevice::modifiers() const
{
if (!mKeyboard)
@@ -348,91 +492,40 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
if (!mXkbState)
return ret;
- ret = QWaylandXkb::modifiers(mXkbState);
+ ret = QXkbCommon::modifiers(mXkbState.get());
#endif
return ret;
}
#if QT_CONFIG(cursor)
-uint32_t QWaylandInputDevice::cursorSerial() const
+void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer, int fallbackOutputScale)
{
- if (mPointer)
- return mPointer->mCursorSerial;
- return 0;
-}
-
-void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *screen)
-{
- struct wl_cursor_image *image = screen->waylandCursor()->cursorImage(newShape);
- if (!image) {
- return;
+ CursorState oldCursor = mCursor;
+ mCursor = CursorState(); // Clear any previous state
+ mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
+ mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
+ mCursor.fallbackOutputScale = fallbackOutputScale;
+
+ if (mCursor.shape == Qt::BitmapCursor) {
+ mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor);
+ qreal dpr = cursor->pixmap().devicePixelRatio();
+ mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
+ // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
+ if (mCursor.bitmapScale < dpr)
+ mCursor.hotspot *= dpr / mCursor.bitmapScale;
}
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
- setCursor(buffer, image, screen->devicePixelRatio());
-}
-
-void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen)
-{
- if (mPointer->mCursorSerial >= mPointer->mEnterSerial && (cursor.shape() != Qt::BitmapCursor && cursor.shape() == mPointer->mCursorShape))
- return;
-
- mPointer->mCursorShape = cursor.shape();
- if (cursor.shape() == Qt::BitmapCursor) {
- setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot(), screen->devicePixelRatio());
+ // Return early if setCursor was called redundantly (mostly happens from decorations)
+ if (mCursor.shape != Qt::BitmapCursor
+ && mCursor.shape == oldCursor.shape
+ && mCursor.hotspot == oldCursor.hotspot
+ && mCursor.fallbackOutputScale == oldCursor.fallbackOutputScale) {
return;
}
- setCursor(cursor.shape(), screen);
-}
-
-void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale)
-{
- if (image) {
- // Convert from pixel coordinates to surface coordinates
- QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
- QSize size = QSize(image->width, image->height) / bufferScale;
- setCursor(buffer, hotspot, size, bufferScale);
- } else {
- setCursor(buffer, QPoint(), QSize(), bufferScale);
- }
-}
-
-// size and hotspot are in surface coordinates
-void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale)
-{
- if (mCaps & WL_SEAT_CAPABILITY_POINTER) {
- bool force = mPointer->mEnterSerial > mPointer->mCursorSerial;
-
- if (!force && mPointer->mCursorBuffer == buffer)
- return;
-
- mPixmapCursor.clear();
- mPointer->mCursorSerial = mPointer->mEnterSerial;
-
- mPointer->mCursorBuffer = buffer;
-
- /* Hide cursor */
- if (!buffer)
- {
- mPointer->set_cursor(mPointer->mEnterSerial, nullptr, 0, 0);
- return;
- }
-
- mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface,
- hotSpot.x(), hotSpot.y());
- wl_surface_attach(pointerSurface, buffer, 0, 0);
- if (mQDisplay->compositorVersion() >= 3)
- wl_surface_set_buffer_scale(pointerSurface, bufferScale);
- wl_surface_damage(pointerSurface, 0, 0, size.width(), size.height());
- wl_surface_commit(pointerSurface);
- }
-}
-void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale)
-{
- setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale);
- mPixmapCursor = buffer;
+ if (mPointer)
+ mPointer->updateCursor();
}
#endif
@@ -451,7 +544,16 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
+
+ if (mFocus) {
+ qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
+ << "leave event first, this is not allowed by the wayland protocol"
+ << "attempting to work around it by invalidating the current focus";
+ invalidateFocus();
+ }
mFocus = window;
+ connect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
@@ -460,7 +562,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
#if QT_CONFIG(cursor)
// Depends on mEnterSerial being updated
- window->window()->setCursor(window->window()->cursor());
+ updateCursor();
#endif
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
@@ -481,7 +583,8 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
window->handleMouseLeave(mParent);
}
- mFocus = nullptr;
+
+ invalidateFocus();
mButtons = Qt::NoButton;
mParent->mTime = time;
@@ -584,6 +687,13 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
}
}
+void QWaylandInputDevice::Pointer::invalidateFocus()
+{
+ disconnect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ mFocus = nullptr;
+ mEnterSerial = 0;
+}
+
void QWaylandInputDevice::Pointer::releaseButtons()
{
mButtons = Qt::NoButton;
@@ -630,8 +740,10 @@ void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, in
void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
{
+ mKeymapFormat = format;
#if QT_CONFIG(xkbcommon)
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
close(fd);
return;
}
@@ -642,21 +754,19 @@ void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd,
return;
}
- // Release the old keymap resources in the case they were already created in
- // the key event or when the compositor issues a new map
- releaseComposeState();
- releaseKeyMap();
+ mXkbKeymap.reset(xkb_keymap_new_from_string(mParent->mQDisplay->xkbContext(), map_str,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS));
+ QXkbCommon::verifyHasLatinLayout(mXkbKeymap.get());
- mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- mXkbMap = xkb_map_new_from_string(mXkbContext, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_str, size);
close(fd);
- mXkbState = xkb_state_new(mXkbMap);
- createComposeState();
-
+ if (mXkbKeymap)
+ mXkbState.reset(xkb_state_new(mXkbKeymap.get()));
+ else
+ mXkbState.reset(nullptr);
#else
- Q_UNUSED(format);
Q_UNUSED(fd);
Q_UNUSED(size);
#endif
@@ -667,12 +777,19 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf
Q_UNUSED(time);
Q_UNUSED(keys);
- if (!surface)
+ if (!surface) {
+ // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
+ // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
return;
+ }
+ if (mFocus) {
+ qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
+ disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ }
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- mFocus = window;
+ mFocus = surface;
+ connect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
}
@@ -680,144 +797,124 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf
void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface)
{
Q_UNUSED(time);
- Q_UNUSED(surface);
- if (surface) {
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- window->unfocus();
+ if (!surface) {
+ // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
+ return;
}
- mFocus = nullptr;
-
- mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
-
- mRepeatTimer.stop();
+ if (surface != mFocus) {
+ qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
+ << "wl_surface argument does not match the current focus"
+ << "This is most likely a compositor bug";
+ return;
+ }
+ disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ handleFocusLost();
}
-static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
- quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
- const QString& text = QString(), bool autorep = false, ushort count = 1)
+void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type, int key,
+ Qt::KeyboardModifiers modifiers, quint32 nativeScanCode,
+ quint32 nativeVirtualKey, quint32 nativeModifiers,
+ const QString &text, bool autorepeat, ushort count)
{
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
bool filtered = false;
- if (inputContext) {
- QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
- text, autorep, count);
+ if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) {
+ QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey,
+ nativeModifiers, text, autorepeat, count);
event.setTimestamp(timestamp);
filtered = inputContext->filterEvent(&event);
}
if (!filtered) {
- QWindowSystemInterface::handleExtendedKeyEvent(tlw, timestamp, type, key, modifiers,
- nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
+ QWindowSystemInterface::handleExtendedKeyEvent(focusWindow()->window(), timestamp, type, key, modifiers,
+ nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count);
}
}
void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
- QWaylandWindow *window = mFocus;
+ if (mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 && mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
+ qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
+ return;
+ }
+
+ auto *window = focusWindow();
if (!window) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first. In either case, ignore the event.
return;
}
- uint32_t code = key + 8;
- bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
- QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
- QString text;
- int qtkey = key + 8; // qt-compositor substracts 8 for some reason
mParent->mSerial = serial;
+ const bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
if (isDown)
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
+ if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
#if QT_CONFIG(xkbcommon)
- if (!createDefaultKeyMap()) {
- return;
- }
+ if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
+ return;
- QString composedText;
- xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, code);
- if (mXkbComposeState) {
- if (isDown)
- xkb_compose_state_feed(mXkbComposeState, sym);
- xkb_compose_status status = xkb_compose_state_get_status(mXkbComposeState);
-
- switch (status) {
- case XKB_COMPOSE_COMPOSED: {
- int size = xkb_compose_state_get_utf8(mXkbComposeState, nullptr, 0);
- QVarLengthArray<char, 32> buffer(size + 1);
- xkb_compose_state_get_utf8(mXkbComposeState, buffer.data(), buffer.size());
- composedText = QString::fromUtf8(buffer.constData());
- sym = xkb_compose_state_get_one_sym(mXkbComposeState);
- xkb_compose_state_reset(mXkbComposeState);
- } break;
- case XKB_COMPOSE_COMPOSING:
- case XKB_COMPOSE_CANCELLED:
- return;
- case XKB_COMPOSE_NOTHING:
- break;
- }
- }
+ auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
- Qt::KeyboardModifiers modifiers = mParent->modifiers();
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState.get(), code);
- std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers);
+ Qt::KeyboardModifiers modifiers = mParent->modifiers();
- if (!composedText.isNull())
- text = composedText;
+ int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, mXkbState.get(), code);
+ QString text = QXkbCommon::lookupString(mXkbState.get(), code);
- sendKey(window->window(), time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
-#else
- // Generic fallback for single hard keys: Assume 'key' is a Qt key code.
- sendKey(window->window(), time, type, qtkey, Qt::NoModifier, code, 0, 0);
-#endif
+ QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
+ handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED
-#if QT_CONFIG(xkbcommon)
- && xkb_keymap_key_repeats(mXkbMap, code)
-#endif
- ) {
- mRepeatKey = qtkey;
- mRepeatCode = code;
- mRepeatTime = time;
- mRepeatText = text;
-#if QT_CONFIG(xkbcommon)
- mRepeatSym = sym;
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) {
+ mRepeatKey.key = qtkey;
+ mRepeatKey.code = code;
+ mRepeatKey.time = time;
+ mRepeatKey.text = text;
+ mRepeatKey.modifiers = modifiers;
+ mRepeatKey.nativeModifiers = mNativeModifiers;
+ mRepeatKey.nativeVirtualKey = sym;
+ mRepeatTimer.setInterval(mRepeatDelay);
+ mRepeatTimer.start();
+ } else if (mRepeatKey.code == code) {
+ mRepeatTimer.stop();
+ }
+#else
+ Q_UNUSED(time);
+ Q_UNUSED(key);
+ qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
+ return;
#endif
- mRepeatTimer.setInterval(400);
- mRepeatTimer.start();
- } else if (mRepeatCode == code) {
- mRepeatTimer.stop();
+ } else if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
+ // raw scan code
+ return;
}
}
-void QWaylandInputDevice::Keyboard::repeatKey()
+void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
{
- if (!mFocus) {
- // We destroyed the keyboard focus surface, but the server didn't get the message yet...
- // or the server didn't send an enter event first.
- return;
- }
-
- mRepeatTimer.setInterval(25);
- sendKey(mFocus->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode,
-#if QT_CONFIG(xkbcommon)
- mRepeatSym, mNativeModifiers,
-#else
- 0, 0,
-#endif
- mRepeatText, true);
+ // The signal is emitted by QWaylandWindow, which is not necessarily destroyed along with the
+ // surface, so we still need to disconnect the signal
+ auto *window = qobject_cast<QWaylandWindow *>(sender());
+ disconnect(window, &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ Q_ASSERT(window->object() == mFocus);
+ handleFocusLost();
+}
- sendKey(mFocus->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode,
-#if QT_CONFIG(xkbcommon)
- mRepeatSym, mNativeModifiers,
-#else
- 0, 0,
+void QWaylandInputDevice::Keyboard::handleFocusLost()
+{
+ mFocus = nullptr;
+#if QT_CONFIG(clipboard)
+ if (auto *dataDevice = mParent->dataDevice())
+ dataDevice->invalidateSelectionOffer();
#endif
- mRepeatText, true);
+ mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
+ mRepeatTimer.stop();
}
void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
@@ -829,12 +926,11 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
Q_UNUSED(serial);
#if QT_CONFIG(xkbcommon)
if (mXkbState)
- xkb_state_update_mask(mXkbState,
+ xkb_state_update_mask(mXkbState.get(),
mods_depressed, mods_latched, mods_locked,
0, 0, group);
mNativeModifiers = mods_depressed | mods_latched | mods_locked;
#else
- Q_UNUSED(serial);
Q_UNUSED(mods_depressed);
Q_UNUSED(mods_latched);
Q_UNUSED(mods_locked);
@@ -842,6 +938,12 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
#endif
}
+void QWaylandInputDevice::Keyboard::keyboard_repeat_info(int32_t rate, int32_t delay)
+{
+ mRepeatRate = rate;
+ mRepeatDelay = delay;
+}
+
void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
uint32_t time,
struct wl_surface *surface,
@@ -915,7 +1017,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch
if (!win && mPointer)
win = mPointer->mFocus;
if (!win && mKeyboard)
- win = mKeyboard->mFocus;
+ win = mKeyboard->focusWindow();
if (!win || !win->window())
return;
diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h
index 7aa86539b..2dc3ddc27 100644
--- a/src/client/qwaylandinputdevice_p.h
+++ b/src/client/qwaylandinputdevice_p.h
@@ -54,6 +54,7 @@
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
+#include <QtCore/QScopedPointer>
#include <QSocketNotifier>
#include <QObject>
#include <QTimer>
@@ -61,13 +62,10 @@
#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
-#include <wayland-client.h>
-
#include <QtWaylandClient/private/qwayland-wayland.h>
#if QT_CONFIG(xkbcommon)
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-keysyms.h>
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif
#include <QtCore/QDebug>
@@ -77,11 +75,6 @@
struct wl_cursor_image;
#endif
-#if QT_CONFIG(xkbcommon)
-struct xkb_compose_state;
-struct xkb_compose_table;
-#endif
-
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -90,6 +83,10 @@ class QWaylandWindow;
class QWaylandDisplay;
class QWaylandDataDevice;
class QWaylandTextInput;
+#if QT_CONFIG(cursor)
+class QWaylandCursorTheme;
+class CursorSurface;
+#endif
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice
: public QObject
@@ -109,12 +106,8 @@ public:
struct ::wl_seat *wl_seat() { return QtWayland::wl_seat::object(); }
#if QT_CONFIG(cursor)
- void setCursor(const QCursor &cursor, QWaylandScreen *screen);
- void setCursor(struct wl_buffer *buffer, struct ::wl_cursor_image *image, int bufferScale);
- void setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale);
- void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale);
+ void setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer = {}, int fallbackOutputScale = 1);
#endif
- void handleWindowDestroyed(QWaylandWindow *window);
void handleEndDrag();
#if QT_CONFIG(wayland_datadevice)
@@ -131,25 +124,32 @@ public:
QWaylandWindow *keyboardFocus() const;
QWaylandWindow *touchFocus() const;
+ QPointF pointerSurfacePosition() const;
+
Qt::KeyboardModifiers modifiers() const;
uint32_t serial() const;
- uint32_t cursorSerial() const;
virtual Keyboard *createKeyboard(QWaylandInputDevice *device);
virtual Pointer *createPointer(QWaylandInputDevice *device);
virtual Touch *createTouch(QWaylandInputDevice *device);
private:
- void setCursor(Qt::CursorShape cursor, QWaylandScreen *screen);
-
QWaylandDisplay *mQDisplay = nullptr;
struct wl_display *mDisplay = nullptr;
int mVersion;
uint32_t mCaps = 0;
- struct wl_surface *pointerSurface = nullptr;
+#if QT_CONFIG(cursor)
+ struct CursorState {
+ QSharedPointer<QWaylandBuffer> bitmapBuffer; // not used with shape cursors
+ int bitmapScale = 1;
+ Qt::CursorShape shape = Qt::ArrowCursor;
+ int fallbackOutputScale = 1;
+ QPoint hotspot;
+ } mCursor;
+#endif
#if QT_CONFIG(wayland_datadevice)
QWaylandDataDevice *mDataDevice = nullptr;
@@ -159,7 +159,7 @@ private:
Pointer *mPointer = nullptr;
Touch *mTouch = nullptr;
- QWaylandTextInput *mTextInput = nullptr;
+ QScopedPointer<QWaylandTextInput> mTextInput;
uint32_t mTime = 0;
uint32_t mSerial = 0;
@@ -169,8 +169,6 @@ private:
QTouchDevice *mTouchDevice = nullptr;
- QSharedPointer<QWaylandBuffer> mPixmapCursor;
-
friend class QWaylandTouchExtension;
friend class QWaylandQtKeyExtension;
};
@@ -189,7 +187,7 @@ public:
Keyboard(QWaylandInputDevice *p);
~Keyboard() override;
- void stopRepeat();
+ QWaylandWindow *focusWindow() const;
void keyboard_keymap(uint32_t format,
int32_t fd,
@@ -206,49 +204,66 @@ public:
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group) override;
+ void keyboard_repeat_info(int32_t rate, int32_t delay) override;
QWaylandInputDevice *mParent = nullptr;
- QPointer<QWaylandWindow> mFocus;
-#if QT_CONFIG(xkbcommon)
- xkb_context *mXkbContext = nullptr;
- xkb_keymap *mXkbMap = nullptr;
- xkb_state *mXkbState = nullptr;
- xkb_compose_table *mXkbComposeTable = nullptr;
- xkb_compose_state *mXkbComposeState = nullptr;
-#endif
+ ::wl_surface *mFocus = nullptr;
+
uint32_t mNativeModifiers = 0;
- int mRepeatKey;
- uint32_t mRepeatCode;
- uint32_t mRepeatTime;
- QString mRepeatText;
-#if QT_CONFIG(xkbcommon)
- xkb_keysym_t mRepeatSym;
-#endif
+ struct repeatKey {
+ int key;
+ uint32_t code;
+ uint32_t time;
+ QString text;
+ Qt::KeyboardModifiers modifiers;
+ uint32_t nativeVirtualKey;
+ uint32_t nativeModifiers;
+ } mRepeatKey;
+
QTimer mRepeatTimer;
+ int mRepeatRate = 25;
+ int mRepeatDelay = 400;
+
+ uint32_t mKeymapFormat = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1;
Qt::KeyboardModifiers modifiers() const;
private slots:
- void repeatKey();
+ void handleFocusDestroyed();
+ void handleFocusLost();
private:
#if QT_CONFIG(xkbcommon)
- bool createDefaultKeyMap();
- void releaseKeyMap();
- void createComposeState();
- void releaseComposeState();
+ bool createDefaultKeymap();
#endif
+ void handleKey(ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
+ quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+ const QString &text, bool autorepeat = false, ushort count = 1);
+#if QT_CONFIG(xkbcommon)
+ QXkbCommon::ScopedXKBKeymap mXkbKeymap;
+ QXkbCommon::ScopedXKBState mXkbState;
+#endif
};
-class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QtWayland::wl_pointer
+class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer
{
-
+ Q_OBJECT
public:
- Pointer(QWaylandInputDevice *p);
+ explicit Pointer(QWaylandInputDevice *seat);
~Pointer() override;
+#if QT_CONFIG(cursor)
+ QString cursorThemeName() const;
+ int cursorSize() const; // in surface coordinates
+ int idealCursorScale() const;
+ void updateCursorTheme();
+ void updateCursor();
+ CursorSurface *getOrCreateCursorSurface();
+#endif
+ QWaylandInputDevice *seat() const { return mParent; }
+protected:
void pointer_enter(uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy) override;
void pointer_leave(uint32_t time, struct wl_surface *surface) override;
@@ -260,13 +275,24 @@ public:
uint32_t axis,
wl_fixed_t value) override;
+private slots:
+ void handleFocusDestroyed() { invalidateFocus(); }
+
+private:
+ void invalidateFocus();
+
+public:
void releaseButtons();
QWaylandInputDevice *mParent = nullptr;
QPointer<QWaylandWindow> mFocus;
uint32_t mEnterSerial = 0;
#if QT_CONFIG(cursor)
- uint32_t mCursorSerial = 0;
+ struct {
+ QWaylandCursorTheme *theme = nullptr;
+ int themeBufferScale = 0;
+ QScopedPointer<CursorSurface> surface;
+ } mCursor;
#endif
QPointF mSurfacePos;
QPointF mGlobalPos;
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 97e0203cd..3a389d9ec 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -86,6 +86,14 @@
#include "qwaylandinputdeviceintegration_p.h"
#include "qwaylandinputdeviceintegrationfactory_p.h"
+#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
+#include <QtLinuxAccessibilitySupport/private/bridge_p.h>
+#endif
+
+#if QT_CONFIG(xkbcommon)
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -129,9 +137,6 @@ QWaylandIntegration::QWaylandIntegration()
: mFontDb(new QGenericUnixFontDatabase())
#endif
, mNativeInterface(new QWaylandNativeInterface(this))
-#if QT_CONFIG(accessibility)
- , mAccessibility(new QPlatformAccessibility())
-#endif
{
initializeInputDeviceIntegration();
mDisplay.reset(new QWaylandDisplay(this));
@@ -145,20 +150,8 @@ QWaylandIntegration::QWaylandIntegration()
#if QT_CONFIG(draganddrop)
mDrag.reset(new QWaylandDrag(mDisplay.data()));
#endif
- QString icStr = QPlatformInputContextFactory::requested();
- if (!icStr.isNull()) {
- mInputContext.reset(QPlatformInputContextFactory::create(icStr));
- } else {
- //try to use the input context using the wl_text_input interface
- QPlatformInputContext *ctx = new QWaylandInputContext(mDisplay.data());
- mInputContext.reset(ctx);
-
- //use the traditional way for on screen keyboards for now
- if (!mInputContext.data()->isValid()) {
- ctx = QPlatformInputContextFactory::create();
- mInputContext.reset(ctx);
- }
- }
+
+ reconfigureInputContext();
}
QWaylandIntegration::~QWaylandIntegration()
@@ -277,6 +270,15 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const
#if QT_CONFIG(accessibility)
QPlatformAccessibility *QWaylandIntegration::accessibility() const
{
+ if (!mAccessibility) {
+#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE
+ Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration",
+ "Initializing accessibility without event-dispatcher!");
+ mAccessibility.reset(new QSpiAccessibleBridge());
+#else
+ mAccessibility.reset(new QPlatformAccessibility());
+#endif
+ }
return mAccessibility.data();
}
#endif
@@ -329,18 +331,16 @@ void QWaylandIntegration::initializeClientBufferIntegration()
{
mClientBufferIntegrationInitialized = true;
- QString targetKey;
- bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION");
- disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration();
- if (disableHardwareIntegration) {
- QByteArray clientBufferIntegrationName = qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION");
- if (clientBufferIntegrationName.isEmpty())
- clientBufferIntegrationName = QByteArrayLiteral("wayland-egl");
- targetKey = QString::fromLocal8Bit(clientBufferIntegrationName);
- } else {
- targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration();
- if (targetKey == QLatin1String("wayland-eglstream-controller"))
- targetKey = QLatin1String("wayland-egl");
+ QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"));
+
+ if (targetKey.isEmpty()) {
+ if (mDisplay->hardwareIntegration()
+ && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("wayland-eglstream-controller")
+ && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) {
+ targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration();
+ } else {
+ targetKey = QLatin1Literal("wayland-egl");
+ }
}
if (targetKey.isEmpty()) {
@@ -349,29 +349,28 @@ void QWaylandIntegration::initializeClientBufferIntegration()
}
QStringList keys = QWaylandClientBufferIntegrationFactory::keys();
- if (keys.contains(targetKey)) {
+ qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys;
+
+ if (keys.contains(targetKey))
mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList()));
- }
- if (mClientBufferIntegration)
+
+ if (mClientBufferIntegration) {
+ qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey;
mClientBufferIntegration->initialize(mDisplay.data());
- else
- qWarning("Failed to load client buffer integration: %s\n", qPrintable(targetKey));
+ } else {
+ qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey;
+ qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys;
+ }
}
void QWaylandIntegration::initializeServerBufferIntegration()
{
mServerBufferIntegrationInitialized = true;
- QString targetKey;
+ QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"));
- bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION");
- disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration();
- if (disableHardwareIntegration) {
- QByteArray serverBufferIntegrationName = qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION");
- targetKey = QString::fromLocal8Bit(serverBufferIntegrationName);
- } else {
+ if (targetKey.isEmpty() && mDisplay->hardwareIntegration())
targetKey = mDisplay->hardwareIntegration()->serverBufferIntegration();
- }
if (targetKey.isEmpty()) {
qWarning("Failed to determine what server buffer integration to use");
@@ -379,13 +378,18 @@ void QWaylandIntegration::initializeServerBufferIntegration()
}
QStringList keys = QWaylandServerBufferIntegrationFactory::keys();
- if (keys.contains(targetKey)) {
+ qCDebug(lcQpaWayland) << "Available server buffer integrations:" << keys;
+
+ if (keys.contains(targetKey))
mServerBufferIntegration.reset(QWaylandServerBufferIntegrationFactory::create(targetKey, QStringList()));
- }
- if (mServerBufferIntegration)
+
+ if (mServerBufferIntegration) {
+ qCDebug(lcQpaWayland) << "Initializing server buffer integration" << targetKey;
mServerBufferIntegration->initialize(mDisplay.data());
- else
- qWarning("Failed to load server buffer integration %s\n", qPrintable(targetKey));
+ } else {
+ qCWarning(lcQpaWayland) << "Failed to load server buffer integration: " << targetKey;
+ qCWarning(lcQpaWayland) << "Available server buffer integrations:" << keys;
+ }
}
void QWaylandIntegration::initializeShellIntegration()
@@ -413,7 +417,7 @@ void QWaylandIntegration::initializeShellIntegration()
Q_FOREACH (QString preferredShell, preferredShells) {
mShellIntegration.reset(createShellIntegration(preferredShell));
if (mShellIntegration) {
- qDebug("Using the '%s' shell integration", qPrintable(preferredShell));
+ qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell));
break;
}
}
@@ -450,6 +454,42 @@ void QWaylandIntegration::initializeInputDeviceIntegration()
}
}
+void QWaylandIntegration::reconfigureInputContext()
+{
+ if (!mDisplay) {
+ // This function can be called from QWaylandDisplay::registry_global() when we
+ // are in process of constructing QWaylandDisplay. Configuring input context
+ // in that case is done by calling reconfigureInputContext() from QWaylandIntegration
+ // constructor, after QWaylandDisplay has been constructed.
+ return;
+ }
+
+ const QString &requested = QPlatformInputContextFactory::requested();
+ if (requested == QLatin1String("qtvirtualkeyboard"))
+ qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side,"
+ " use QT_IM_MODULE=qtvirtualkeyboard at compositor-side.";
+
+ if (requested.isNull())
+ mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
+ else
+ mInputContext.reset(QPlatformInputContextFactory::create(requested));
+
+ const QString defaultInputContext(QStringLiteral("compose"));
+ if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext)
+ mInputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
+
+#if QT_CONFIG(xkbcommon)
+ QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext());
+#endif
+
+ // Even if compositor-side input context handling has been requested, we fallback to
+ // client-side handling if compositor does not provide the text-input extension. This
+ // is why we need to check here which input context actually is being used.
+ mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data());
+
+ qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
+}
+
QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName)
{
if (QWaylandShellIntegrationFactory::keys().contains(integrationName)) {
diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h
index a5a3d7b69..5e6f16d09 100644
--- a/src/client/qwaylandintegration_p.h
+++ b/src/client/qwaylandintegration_p.h
@@ -116,6 +116,8 @@ public:
virtual QWaylandServerBufferIntegration *serverBufferIntegration() const;
virtual QWaylandShellIntegration *shellIntegration() const;
+ void reconfigureInputContext();
+
private:
// NOTE: mDisplay *must* be destructed after mDrag and mClientBufferIntegration
// and mShellIntegration.
@@ -145,7 +147,7 @@ private:
QScopedPointer<QPlatformNativeInterface> mNativeInterface;
QScopedPointer<QPlatformInputContext> mInputContext;
#if QT_CONFIG(accessibility)
- QScopedPointer<QPlatformAccessibility> mAccessibility;
+ mutable QScopedPointer<QPlatformAccessibility> mAccessibility;
#endif
bool mFailed = false;
bool mClientBufferIntegrationInitialized = false;
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index 38d61f88c..5b04ae609 100644
--- a/src/client/qwaylandscreen.cpp
+++ b/src/client/qwaylandscreen.cpp
@@ -40,6 +40,7 @@
#include "qwaylandscreen_p.h"
#include "qwaylanddisplay_p.h"
+#include "qwaylandintegration_p.h"
#include "qwaylandcursor_p.h"
#include "qwaylandwindow_p.h"
@@ -60,6 +61,14 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin
{
if (auto *xdgOutputManager = waylandDisplay->xdgOutputManager())
initXdgOutput(xdgOutputManager);
+
+ if (version < WL_OUTPUT_DONE_SINCE_VERSION) {
+ qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
+ << "QScreen may not work correctly";
+ mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
+ mOutputDone = true; // Fake the done event
+ maybeInitialize();
+ }
}
QWaylandScreen::~QWaylandScreen()
@@ -68,6 +77,24 @@ QWaylandScreen::~QWaylandScreen()
zxdg_output_v1::destroy();
}
+void QWaylandScreen::maybeInitialize()
+{
+ Q_ASSERT(!mInitialized);
+
+ if (!mOutputDone)
+ return;
+
+ if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone)
+ return;
+
+ mInitialized = true;
+ mWaylandDisplay->handleScreenInitialized(this);
+
+ updateOutputProperties();
+ if (zxdg_output_v1::isInitialized())
+ updateXdgOutputProperties();
+}
+
void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager)
{
Q_ASSERT(xdgOutputManager);
@@ -176,19 +203,10 @@ qreal QWaylandScreen::refreshRate() const
}
#if QT_CONFIG(cursor)
-
QPlatformCursor *QWaylandScreen::cursor() const
{
- return const_cast<QWaylandScreen *>(this)->waylandCursor();
-}
-
-QWaylandCursor *QWaylandScreen::waylandCursor()
-{
- if (!mWaylandCursor)
- mWaylandCursor.reset(new QWaylandCursor(this));
- return mWaylandCursor.data();
+ return mWaylandDisplay->waylandCursor();
}
-
#endif // QT_CONFIG(cursor)
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
@@ -241,10 +259,15 @@ void QWaylandScreen::output_scale(int32_t factor)
void QWaylandScreen::output_done()
{
- // the done event is sent after all the geometry and the mode events are sent,
- // and the last mode event to be sent is the active one, so we can trust the
- // values of mGeometry and mRefreshRate here
+ mOutputDone = true;
+ if (mInitialized)
+ updateOutputProperties();
+ else
+ maybeInitialize();
+}
+void QWaylandScreen::updateOutputProperties()
+{
if (mTransform >= 0) {
bool isPortrait = mGeometry.height() > mGeometry.width();
switch (mTransform) {
@@ -271,7 +294,9 @@ void QWaylandScreen::output_done()
QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation);
mTransform = -1;
}
+
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), refreshRate());
+
if (!zxdg_output_v1::isInitialized())
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
}
@@ -289,9 +314,24 @@ void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height)
void QWaylandScreen::zxdg_output_v1_done()
{
- QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
+ mXdgOutputDone = true;
+ if (mInitialized)
+ updateXdgOutputProperties();
+ else
+ maybeInitialize();
}
+void QWaylandScreen::zxdg_output_v1_name(const QString &name)
+{
+ mOutputName = name;
}
+void QWaylandScreen::updateXdgOutputProperties()
+{
+ Q_ASSERT(zxdg_output_v1::isInitialized());
+ QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
+}
+
+} // namespace QtWaylandClient
+
QT_END_NAMESPACE
diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h
index 6e4ed94f7..e9e07d9cd 100644
--- a/src/client/qwaylandscreen_p.h
+++ b/src/client/qwaylandscreen_p.h
@@ -71,6 +71,8 @@ public:
QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id);
~QWaylandScreen() override;
+ void maybeInitialize();
+
void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager);
QWaylandDisplay *display() const;
@@ -98,7 +100,6 @@ public:
#if QT_CONFIG(cursor)
QPlatformCursor *cursor() const override;
- QWaylandCursor *waylandCursor();
#endif
uint32_t outputId() const { return m_outputId; }
@@ -117,11 +118,14 @@ private:
int32_t transform) override;
void output_scale(int32_t factor) override;
void output_done() override;
+ void updateOutputProperties();
// XdgOutput
void zxdg_output_v1_logical_position(int32_t x, int32_t y) override;
void zxdg_output_v1_logical_size(int32_t width, int32_t height) override;
void zxdg_output_v1_done() override;
+ void zxdg_output_v1_name(const QString &name) override;
+ void updateXdgOutputProperties();
int m_outputId;
QWaylandDisplay *mWaylandDisplay = nullptr;
@@ -137,6 +141,9 @@ private:
QSize mPhysicalSize;
QString mOutputName;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
+ bool mOutputDone = false;
+ bool mXdgOutputDone = false;
+ bool mInitialized = false;
#if QT_CONFIG(cursor)
QScopedPointer<QWaylandCursor> mWaylandCursor;
diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h
index 6bc3258c7..f5f202d08 100644
--- a/src/client/qwaylandshellsurface_p.h
+++ b/src/client/qwaylandshellsurface_p.h
@@ -54,8 +54,6 @@
#include <QtCore/QSize>
#include <QObject>
-#include <wayland-client.h>
-
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/qtwaylandclientglobal.h>
@@ -75,10 +73,10 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandShellSurface : public QObject
public:
explicit QWaylandShellSurface(QWaylandWindow *window);
~QWaylandShellSurface() override {}
- virtual void resize(QWaylandInputDevice * /*inputDevice*/, enum wl_shell_surface_resize /*edges*/)
- {}
+ virtual void resize(QWaylandInputDevice * /*inputDevice*/, Qt::Edges /*edges*/) {}
virtual bool move(QWaylandInputDevice *) { return false; }
+ virtual bool showWindowMenu(QWaylandInputDevice *seat) { Q_UNUSED(seat); return false; }
virtual void setTitle(const QString & /*title*/) {}
virtual void setAppId(const QString & /*appId*/) {}
@@ -99,6 +97,10 @@ public:
virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);}
virtual bool wantsDecorations() const { return false; }
+ virtual void propagateSizeHints() {}
+
+ virtual void setWindowGeometry(const QRect &rect) { Q_UNUSED(rect); }
+
private:
QWaylandWindow *m_window = nullptr;
friend class QWaylandWindow;
diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
index 3fe2ce80c..34044ec9b 100644
--- a/src/client/qwaylandshmbackingstore.cpp
+++ b/src/client/qwaylandshmbackingstore.cpp
@@ -49,8 +49,7 @@
#include <QtGui/QPainter>
#include <QMutexLocker>
-#include <wayland-client.h>
-#include <wayland-client-protocol.h>
+#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
#include <unistd.h>
#include <sys/mman.h>
diff --git a/src/client/qwaylandsubsurface_p.h b/src/client/qwaylandsubsurface_p.h
index e9a7cb20e..76da10b24 100644
--- a/src/client/qwaylandsubsurface_p.h
+++ b/src/client/qwaylandsubsurface_p.h
@@ -51,8 +51,6 @@
// We mean it.
//
-#include <wayland-client.h>
-
#include <QtCore/qglobal.h>
#include <QtCore/qmutex.h>
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 58e0fc585..4532bc236 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -52,11 +52,6 @@
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandshellintegration_p.h"
-#if QT_CONFIG(wayland_datadevice)
-#include "qwaylanddatadevice_p.h"
-#endif
-
-
#include <QtCore/QFileInfo>
#include <QtCore/QPointer>
#include <QtCore/QRegularExpression>
@@ -69,8 +64,6 @@
#include <QtCore/QDebug>
#include <QtCore/QThread>
-#include <wayland-client.h>
-
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -100,10 +93,6 @@ QWaylandWindow::~QWaylandWindow()
if (isInitialized())
reset(false);
- QList<QWaylandInputDevice *> inputDevices = mDisplay->inputDevices();
- for (int i = 0; i < inputDevices.size(); ++i)
- inputDevices.at(i)->handleWindowDestroyed(this);
-
const QWindow *parent = window();
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
if (w->transientParent() == parent)
@@ -245,8 +234,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mShellSurface = nullptr;
delete mSubSurfaceWindow;
mSubSurfaceWindow = nullptr;
- if (isInitialized())
+ if (isInitialized()) {
+ emit wlSurfaceDestroyed();
destroy();
+ }
mScreens.clear();
if (mFrameCallback) {
@@ -348,6 +339,9 @@ void QWaylandWindow::setGeometry(const QRect &rect)
QRect exposeGeometry(QPoint(), geometry().size());
if (exposeGeometry != mLastExposeGeometry)
sendExposeEvent(exposeGeometry);
+
+ if (mShellSurface)
+ mShellSurface->setWindowGeometry(windowContentGeometry());
}
void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
@@ -502,9 +496,11 @@ void QWaylandWindow::surface_enter(wl_output *output)
auto addedScreen = QWaylandScreen::fromWlOutput(output);
if (mScreens.contains(addedScreen)) {
- qWarning() << "Unexpected wl_surface.enter received for output with id:"
- << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
- << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model();
+ qCWarning(lcQpaWayland)
+ << "Ignoring unexpected wl_surface.enter received for output with id:"
+ << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
+ << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model()
+ << "This is most likely a bug in the compositor.";
return;
}
@@ -521,9 +517,12 @@ void QWaylandWindow::surface_leave(wl_output *output)
auto *removedScreen = QWaylandScreen::fromWlOutput(output);
bool wasRemoved = mScreens.removeOne(removedScreen);
if (!wasRemoved) {
- qWarning() << "Unexpected wl_surface.leave received for output with id:"
- << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
- << "screen name:" << removedScreen->name() << "screen model:" << removedScreen->model();
+ qCWarning(lcQpaWayland)
+ << "Ignoring unexpected wl_surface.leave received for output with id:"
+ << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
+ << "screen name:" << removedScreen->name()
+ << "screen model:" << removedScreen->model()
+ << "This is most likely a bug in the compositor.";
return;
}
@@ -605,6 +604,11 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
wl_surface::commit();
}
+void QWaylandWindow::commit()
+{
+ wl_surface::commit();
+}
+
const wl_callback_listener QWaylandWindow::callbackListener = {
[](void *data, wl_callback *callback, uint32_t time) {
Q_UNUSED(callback);
@@ -670,6 +674,23 @@ QMargins QWaylandWindow::frameMargins() const
return QPlatformWindow::frameMargins();
}
+/*!
+ * Size, with decorations (including including eventual shadows) in wl_surface coordinates
+ */
+QSize QWaylandWindow::surfaceSize() const
+{
+ return geometry().marginsAdded(frameMargins()).size();
+}
+
+/*!
+ * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
+ * topLeft is where the shadow stops and the decorations border start.
+ */
+QRect QWaylandWindow::windowContentGeometry() const
+{
+ return QRect(QPoint(), surfaceSize());
+}
+
QWaylandShellSurface *QWaylandWindow::shellSurface() const
{
return mShellSurface;
@@ -864,9 +885,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
#if QT_CONFIG(cursor)
if (e.type == QWaylandPointerEvent::Enter) {
- QRect windowGeometry = window()->frameGeometry();
- windowGeometry.moveTopLeft({0, 0}); // convert to wayland surface coordinates
- QRect contentGeometry = windowGeometry.marginsRemoved(frameMargins());
+ QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
if (contentGeometry.contains(e.local.toPoint()))
restoreMouseCursor(inputDevice);
}
@@ -963,7 +982,8 @@ void QWaylandWindow::handleScreenChanged()
#if QT_CONFIG(cursor)
void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor)
{
- device->setCursor(cursor, waylandScreen());
+ int fallbackBufferScale = int(devicePixelRatio());
+ device->setCursor(&cursor, {}, fallbackBufferScale);
}
void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)
@@ -977,16 +997,6 @@ void QWaylandWindow::requestActivateWindow()
qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()";
}
-void QWaylandWindow::unfocus()
-{
-#if QT_CONFIG(clipboard)
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (inputDevice && inputDevice->dataDevice()) {
- inputDevice->dataDevice()->invalidateSelectionOffer();
- }
-#endif
-}
-
bool QWaylandWindow::isExposed() const
{
if (!window()->isVisible())
@@ -1178,6 +1188,12 @@ void QWaylandWindow::addAttachOffset(const QPoint point)
mOffset += point;
}
+void QWaylandWindow::propagateSizeHints()
+{
+ if (mShellSurface)
+ mShellSurface->propagateSizeHints();
+}
+
bool QtWaylandClient::QWaylandWindow::startSystemMove(const QPoint &pos)
{
Q_UNUSED(pos);
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index c47123dc9..8c1ebe167 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -120,9 +120,13 @@ public:
void handleExpose(const QRegion &region);
void commit(QWaylandBuffer *buffer, const QRegion &damage);
+ void commit();
+
bool waitForFrameSync(int timeout);
QMargins frameMargins() const override;
+ QSize surfaceSize() const;
+ QRect windowContentGeometry() const;
static QWaylandWindow *fromWlSurface(::wl_surface *surface);
@@ -149,7 +153,6 @@ public:
void requestActivateWindow() override;
bool isExposed() const override;
bool isActive() const override;
- void unfocus();
QWaylandAbstractDecoration *decoration() const;
@@ -186,7 +189,7 @@ public:
QWaylandShmBackingStore *backingStore() const { return mBackingStore; }
bool setKeyboardGrabEnabled(bool) override { return false; }
- void propagateSizeHints() override { }
+ void propagateSizeHints() override;
void addAttachOffset(const QPoint point);
bool startSystemMove(const QPoint &pos) override;
@@ -199,6 +202,9 @@ public:
public slots:
void applyConfigure();
+signals:
+ void wlSurfaceDestroyed();
+
protected:
void surface_enter(struct ::wl_output *output) override;
void surface_leave(struct ::wl_output *output) override;
diff --git a/src/client/qwaylandwindowmanagerintegration_p.h b/src/client/qwaylandwindowmanagerintegration_p.h
index 1319abd91..31de6ddd3 100644
--- a/src/client/qwaylandwindowmanagerintegration_p.h
+++ b/src/client/qwaylandwindowmanagerintegration_p.h
@@ -54,7 +54,6 @@
#include <QtCore/QObject>
#include <QtCore/QScopedPointer>
-#include <wayland-client.h>
#include <QtServiceSupport/private/qgenericunixservices_p.h>
#include <QtWaylandClient/private/qwayland-qt-windowmanager.h>
diff --git a/src/compositor/compositor.pro b/src/compositor/compositor.pro
index 47be591d7..b887cf281 100644
--- a/src/compositor/compositor.pro
+++ b/src/compositor/compositor.pro
@@ -3,6 +3,10 @@ MODULE = waylandcompositor
QT = core gui-private
+qtConfig(xkbcommon) {
+ QT_FOR_PRIVATE += xkbcommon_support-private
+}
+
qtHaveModule(quick): QT += quick
CONFIG -= precompile_header
diff --git a/src/compositor/compositor_api/qwaylandclient.cpp b/src/compositor/compositor_api/qwaylandclient.cpp
index 7f0b225b2..d26dfc6d5 100644
--- a/src/compositor/compositor_api/qwaylandclient.cpp
+++ b/src/compositor/compositor_api/qwaylandclient.cpp
@@ -44,7 +44,7 @@
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
#include <wayland-util.h>
QT_BEGIN_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp
index a0d69c52e..8ff9e0905 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandcompositor.cpp
@@ -72,7 +72,6 @@
#include "extensions/qwaylandqtwindowmanager.h"
-#include "qwaylandxkb_p.h"
#include "qwaylandsharedmemoryformathelper_p.h"
#include <QtCore/QCoreApplication>
@@ -129,12 +128,12 @@ public:
bool isDown = ke->keyType == QEvent::KeyPress;
#if QT_CONFIG(xkbcommon)
- QString text;
- Qt::KeyboardModifiers modifiers = QWaylandXkb::modifiers(keyb->xkbState());
+ xkb_state *xkbState = keyb->xkbState();
+ Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(xkbState);
- const xkb_keysym_t sym = xkb_state_key_get_one_sym(keyb->xkbState(), code);
- int qtkey;
- std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers);
+ const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code);
+ int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code);
+ QString text = QXkbCommon::lookupString(xkbState, code);
ke->key = qtkey;
ke->modifiers = modifiers;
@@ -162,12 +161,24 @@ QWaylandCompositorPrivate::QWaylandCompositorPrivate(QWaylandCompositor *composi
{
if (QGuiApplication::platformNativeInterface())
display = static_cast<wl_display*>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("server_wl_display"));
- if (!display)
+
+ if (!display) {
display = wl_display_create();
+ ownsDisplay = true;
+ }
+
eventHandler.reset(new QtWayland::WindowSystemEventHandler(compositor));
timer.start();
QWindowSystemInterfacePrivate::installWindowSystemEventHandler(eventHandler.data());
+
+#if QT_CONFIG(xkbcommon)
+ mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
+ if (!mXkbContext) {
+ qWarning("Failed to create a XKB context: keymap will not be supported");
+ return;
+ }
+#endif
}
void QWaylandCompositorPrivate::init()
@@ -246,7 +257,8 @@ QWaylandCompositorPrivate::~QWaylandCompositorPrivate()
// Some client buffer integrations need to clean up before the destroying the wl_display
client_buffer_integration.reset();
- wl_display_destroy(display);
+ if (ownsDisplay)
+ wl_display_destroy(display);
}
void QWaylandCompositorPrivate::preInit()
@@ -586,7 +598,7 @@ QByteArray QWaylandCompositor::socketName() const
* \qmlmethod QtWaylandCompositor::WaylandCompositor::addSocketDescriptor(fd)
* \since 5.12
*
- * Listen for client connections on a file descriptor referring to a
+ * Listen for client connections on a file descriptor, \a fd, referring to a
* server socket already bound and listening.
*
* Does not take ownership of the file descriptor; it must be closed
@@ -598,7 +610,7 @@ QByteArray QWaylandCompositor::socketName() const
*/
/*!
- * Listen for client connections on a file descriptor referring to a
+ * Listen for client connections on a file descriptor, \a fd, referring to a
* server socket already bound and listening.
*
* Does not take ownership of the file descriptor; it must be closed
diff --git a/src/compositor/compositor_api/qwaylandcompositor_p.h b/src/compositor/compositor_api/qwaylandcompositor_p.h
index cdf4be6b4..2c9624216 100644
--- a/src/compositor/compositor_api/qwaylandcompositor_p.h
+++ b/src/compositor/compositor_api/qwaylandcompositor_p.h
@@ -60,6 +60,10 @@
#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#if QT_CONFIG(xkbcommon)
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
namespace QtWayland {
@@ -81,6 +85,10 @@ public:
QWaylandCompositorPrivate(QWaylandCompositor *compositor);
~QWaylandCompositorPrivate() override;
+#if QT_CONFIG(xkbcommon)
+ struct xkb_context *xkbContext() const { return mXkbContext.get(); }
+#endif
+
void preInit();
void init();
@@ -137,6 +145,7 @@ protected:
QList<int> externally_added_socket_fds;
#endif
struct wl_display *display = nullptr;
+ bool ownsDisplay = false;
QList<QWaylandSeat *> seats;
QList<QWaylandOutput *> outputs;
@@ -168,6 +177,10 @@ protected:
bool initialized = false;
QList<QPointer<QObject> > polish_objects;
+#if QT_CONFIG(xkbcommon)
+ QXkbCommon::ScopedXKBContext mXkbContext;
+#endif
+
Q_DECLARE_PUBLIC(QWaylandCompositor)
Q_DISABLE_COPY(QWaylandCompositorPrivate)
};
diff --git a/src/compositor/compositor_api/qwaylanddestroylistener_p.h b/src/compositor/compositor_api/qwaylanddestroylistener_p.h
index 7c6001c36..0bbeb69c0 100644
--- a/src/compositor/compositor_api/qwaylanddestroylistener_p.h
+++ b/src/compositor/compositor_api/qwaylanddestroylistener_p.h
@@ -55,7 +55,7 @@
#include <QtCore/private/qobject_p.h>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp
index 68d855a66..2302c0b6a 100644
--- a/src/compositor/compositor_api/qwaylandkeyboard.cpp
+++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp
@@ -54,7 +54,6 @@
#if QT_CONFIG(xkbcommon)
#include <sys/mman.h>
#include <sys/types.h>
-#include <qwaylandxkb_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -67,12 +66,11 @@ QWaylandKeyboardPrivate::QWaylandKeyboardPrivate(QWaylandSeat *seat)
QWaylandKeyboardPrivate::~QWaylandKeyboardPrivate()
{
#if QT_CONFIG(xkbcommon)
- if (xkb_context) {
+ if (xkbContext()) {
if (keymap_area)
munmap(keymap_area, keymap_size);
- close(keymap_fd);
- xkb_context_unref(xkb_context);
- xkb_state_unref(xkb_state);
+ if (keymap_fd >= 0)
+ close(keymap_fd);
}
#endif
}
@@ -137,14 +135,14 @@ void QWaylandKeyboardPrivate::keyboard_bind_resource(wl_keyboard::Resource *reso
send_repeat_info(resource->handle, repeatRate, repeatDelay);
#if QT_CONFIG(xkbcommon)
- if (xkb_context) {
+ if (xkbContext()) {
send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keymap_fd, keymap_size);
} else
#endif
{
int null_fd = open("/dev/null", O_RDONLY);
- send_keymap(resource->handle, 0 /* WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP */,
+ send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
null_fd, 0);
close(null_fd);
}
@@ -164,11 +162,8 @@ void QWaylandKeyboardPrivate::keyboard_release(wl_keyboard::Resource *resource)
void QWaylandKeyboardPrivate::keyEvent(uint code, uint32_t state)
{
-#if QT_CONFIG(xkbcommon)
- uint key = toWaylandXkbV1Key(code);
-#else
- uint key = code;
-#endif
+ uint key = toWaylandKey(code);
+
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
keys << key;
} else {
@@ -180,30 +175,18 @@ void QWaylandKeyboardPrivate::sendKeyEvent(uint code, uint32_t state)
{
uint32_t time = compositor()->currentTimeMsecs();
uint32_t serial = compositor()->nextSerial();
-#if QT_CONFIG(xkbcommon)
- uint key = toWaylandXkbV1Key(code);
-#else
- uint key = code;
-#endif
+ uint key = toWaylandKey(code);
if (focusResource)
send_key(focusResource->handle, serial, time, key, state);
}
-void QWaylandKeyboardPrivate::modifiers(uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
-{
- if (focusResource) {
- send_modifiers(focusResource->handle, serial, mods_depressed, mods_latched, mods_locked, group);
- }
-}
-
#if QT_CONFIG(xkbcommon)
void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable()
{
if (!scanCodesByQtKey.isEmpty() || !xkbState())
return;
- if (xkb_keymap *keymap = xkb_state_get_keymap(xkb_state)) {
+ if (xkb_keymap *keymap = xkb_state_get_keymap(xkbState())) {
xkb_keymap_key_for_each(keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d){
auto *scanCodesByQtKey = static_cast<QMap<ScanCodeKey, uint>*>(d);
uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode);
@@ -214,7 +197,7 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable()
continue;
Qt::KeyboardModifiers mods = {};
- int qtKey = QWaylandXkb::keysymToQtKey(syms[0], mods).first;
+ int qtKey = QXkbCommon::keysymToQtKey(syms[0], mods);
if (qtKey != 0)
scanCodesByQtKey->insert({layout, qtKey}, keycode);
}
@@ -226,15 +209,15 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable()
void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state)
{
#if QT_CONFIG(xkbcommon)
- if (!xkb_context)
+ if (!xkbContext())
return;
- xkb_state_update_key(xkb_state, code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
+ xkb_state_update_key(xkbState(), code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
- uint32_t modsDepressed = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_DEPRESSED);
- uint32_t modsLatched = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LATCHED);
- uint32_t modsLocked = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LOCKED);
- uint32_t group = xkb_state_serialize_group(xkb_state, (xkb_state_component)XKB_STATE_EFFECTIVE);
+ uint32_t modsDepressed = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_DEPRESSED);
+ uint32_t modsLatched = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LATCHED);
+ uint32_t modsLocked = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LOCKED);
+ uint32_t group = xkb_state_serialize_layout(xkbState(), XKB_STATE_LAYOUT_EFFECTIVE);
if (this->modsDepressed == modsDepressed
&& this->modsLatched == modsLatched
@@ -247,7 +230,10 @@ void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state)
this->modsLocked = modsLocked;
this->group = group;
- modifiers(compositor()->nextSerial(), modsDepressed, modsLatched, modsLocked, group);
+ if (focusResource) {
+ send_modifiers(focusResource->handle, compositor()->nextSerial(), modsDepressed,
+ modsLatched, modsLocked, group);
+ }
#else
Q_UNUSED(code);
Q_UNUSED(state);
@@ -266,7 +252,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap()
pendingKeymap = false;
#if QT_CONFIG(xkbcommon)
- if (!xkb_context)
+ if (!xkbContext())
return;
createXKBKeymap();
@@ -274,7 +260,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap()
send_keymap(res->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size);
}
- xkb_state_update_mask(xkb_state, 0, modsLatched, modsLocked, 0, 0, 0);
+ xkb_state_update_mask(xkbState(), 0, modsLatched, modsLocked, 0, 0, 0);
if (focusResource)
send_modifiers(focusResource->handle,
compositor()->nextSerial(),
@@ -285,6 +271,24 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap()
#endif
}
+uint QWaylandKeyboardPrivate::toWaylandKey(const uint nativeScanCode)
+{
+#if QT_CONFIG(xkbcommon)
+ // In all current XKB keymaps there's a constant offset of 8 (for historical
+ // reasons) from hardware/evdev scancodes to XKB keycodes. On X11, we pass
+ // XKB keycodes (as sent by X server) via QKeyEvent::nativeScanCode. eglfs+evdev
+ // adds 8 for consistency, see qtbase/05c07c7636012ebb4131ca099ca4ea093af76410.
+ // eglfs+libinput also adds 8, for the same reason. Wayland protocol uses
+ // hardware/evdev scancodes, thus we need to minus 8 before sending the event
+ // out.
+ const uint offset = 8;
+ Q_ASSERT(nativeScanCode >= offset);
+ return nativeScanCode - offset;
+#else
+ return nativeScanCode;
+#endif
+}
+
#if QT_CONFIG(xkbcommon)
static int createAnonymousFile(size_t size)
{
@@ -316,18 +320,6 @@ static int createAnonymousFile(size_t size)
return fd;
}
-void QWaylandKeyboardPrivate::initXKB()
-{
- xkb_context = xkb_context_new(static_cast<xkb_context_flags>(0));
- if (!xkb_context) {
- qWarning("Failed to create a XKB context: keymap will not be supported");
- return;
- }
-
- createXKBKeymap();
-}
-
-
void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap)
{
char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
@@ -356,46 +348,41 @@ void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap)
strcpy(keymap_area, keymap_str);
free(keymap_str);
- if (xkb_state)
- xkb_state_unref(xkb_state);
- xkb_state = xkb_state_new(keymap);
-}
-
-uint QWaylandKeyboardPrivate::toWaylandXkbV1Key(const uint nativeScanCode)
-{
- const uint offset = 8;
- Q_ASSERT(nativeScanCode >= offset);
- return nativeScanCode - offset;
+ mXkbState.reset(xkb_state_new(keymap));
+ if (!mXkbState)
+ qWarning("Failed to create XKB state");
}
void QWaylandKeyboardPrivate::createXKBKeymap()
{
- if (!xkb_context)
+ if (!xkbContext())
return;
- auto keymap = seat->keymap();
- struct xkb_rule_names rule_names = { strdup(qPrintable(keymap->rules())),
- strdup(qPrintable(keymap->model())),
- strdup(qPrintable(keymap->layout())),
- strdup(qPrintable(keymap->variant())),
- strdup(qPrintable(keymap->options())) };
- struct xkb_keymap *xkbKeymap = xkb_keymap_new_from_names(xkb_context, &rule_names, static_cast<xkb_keymap_compile_flags>(0));
-
+ QWaylandKeymap *keymap = seat->keymap();
+ QByteArray rules = keymap->rules().toLocal8Bit();
+ QByteArray model = keymap->model().toLocal8Bit();
+ QByteArray layout = keymap->layout().toLocal8Bit();
+ QByteArray variant = keymap->variant().toLocal8Bit();
+ QByteArray options = keymap->options().toLocal8Bit();
+
+ struct xkb_rule_names rule_names = {
+ rules.constData(),
+ model.constData(),
+ layout.constData(),
+ variant.constData(),
+ options.constData()
+ };
+
+ QXkbCommon::ScopedXKBKeymap xkbKeymap(xkb_keymap_new_from_names(xkbContext(), &rule_names,
+ XKB_KEYMAP_COMPILE_NO_FLAGS));
if (xkbKeymap) {
scanCodesByQtKey.clear();
- createXKBState(xkbKeymap);
- xkb_keymap_unref(xkbKeymap);
+ createXKBState(xkbKeymap.get());
} else {
qWarning("Failed to load the '%s' XKB keymap.", qPrintable(keymap->layout()));
}
-
- free((char *)rule_names.rules);
- free((char *)rule_names.model);
- free((char *)rule_names.layout);
- free((char *)rule_names.variant);
- free((char *)rule_names.options);
}
-#endif
+#endif // QT_CONFIG(xkbcommon)
void QWaylandKeyboardPrivate::sendRepeatInfo()
{
@@ -430,7 +417,7 @@ QWaylandKeyboard::QWaylandKeyboard(QWaylandSeat *seat, QObject *parent)
connect(keymap, &QWaylandKeymap::rulesChanged, this, &QWaylandKeyboard::updateKeymap);
connect(keymap, &QWaylandKeymap::modelChanged, this, &QWaylandKeyboard::updateKeymap);
#if QT_CONFIG(xkbcommon)
- d->initXKB();
+ d->createXKBKeymap();
#endif
}
@@ -486,7 +473,7 @@ QWaylandClient *QWaylandKeyboard::focusClient() const
/*!
* Sends the current key modifiers to \a client with the given \a serial.
*/
-void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint serial)
+void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint32_t serial)
{
Q_D(QWaylandKeyboard);
QtWaylandServer::wl_keyboard::Resource *resource = d->resourceMap().value(client->client());
diff --git a/src/compositor/compositor_api/qwaylandkeyboard_p.h b/src/compositor/compositor_api/qwaylandkeyboard_p.h
index 87e89e85e..4dfe0035e 100644
--- a/src/compositor/compositor_api/qwaylandkeyboard_p.h
+++ b/src/compositor/compositor_api/qwaylandkeyboard_p.h
@@ -51,6 +51,7 @@
//
// We mean it.
//
+#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
#include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h>
#include <QtWaylandCompositor/qwaylandseat.h>
@@ -64,6 +65,7 @@
#if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon.h>
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif
@@ -83,11 +85,12 @@ public:
QWaylandCompositor *compositor() const { return seat->compositor(); }
void focused(QWaylandSurface* surface);
- void modifiers(uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked, uint32_t group);
#if QT_CONFIG(xkbcommon)
- struct xkb_state *xkbState() const { return xkb_state; }
+ struct xkb_state *xkbState() const { return mXkbState.get(); }
+ struct xkb_context *xkbContext() const {
+ return QWaylandCompositorPrivate::get(seat->compositor())->xkbContext();
+ }
uint32_t xkbModsMask() const { return modsDepressed | modsLatched | modsLocked; }
void maybeUpdateXkbScanCodeTable();
#endif
@@ -107,11 +110,10 @@ protected:
private:
#if QT_CONFIG(xkbcommon)
- void initXKB();
void createXKBKeymap();
void createXKBState(xkb_keymap *keymap);
#endif
- static uint toWaylandXkbV1Key(const uint nativeScanCode);
+ static uint toWaylandKey(const uint nativeScanCode);
void sendRepeatInfo();
@@ -134,8 +136,7 @@ private:
char *keymap_area = nullptr;
using ScanCodeKey = std::pair<uint,int>; // group/layout and QtKey
QMap<ScanCodeKey, uint> scanCodesByQtKey;
- struct xkb_context *xkb_context = nullptr;
- struct xkb_state *xkb_state = nullptr;
+ QXkbCommon::ScopedXKBState mXkbState;
#endif
quint32 repeatRate = 40;
diff --git a/src/compositor/compositor_api/qwaylandoutput.cpp b/src/compositor/compositor_api/qwaylandoutput.cpp
index 1f34f4eac..006edbe6a 100644
--- a/src/compositor/compositor_api/qwaylandoutput.cpp
+++ b/src/compositor/compositor_api/qwaylandoutput.cpp
@@ -47,6 +47,7 @@
#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
#include <QtWaylandCompositor/private/qwaylandview_p.h>
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QtMath>
@@ -350,8 +351,8 @@ void QWaylandOutput::initialize()
*/
QWaylandOutput *QWaylandOutput::fromResource(wl_resource *resource)
{
- if (auto *r = QWaylandOutputPrivate::Resource::fromResource(resource))
- return static_cast<QWaylandOutputPrivate *>(r->output_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandOutputPrivate *>(resource))
+ return p->q_func();
return nullptr;
}
diff --git a/src/compositor/compositor_api/qwaylandpointer.cpp b/src/compositor/compositor_api/qwaylandpointer.cpp
index 77e736a58..96263e0c2 100644
--- a/src/compositor/compositor_api/qwaylandpointer.cpp
+++ b/src/compositor/compositor_api/qwaylandpointer.cpp
@@ -239,7 +239,7 @@ uint QWaylandPointer::sendMouseReleaseEvent(Qt::MouseButton button)
/*!
* Sets the current mouse focus to \a view and sends a mouse move event to it with the
- * local position \a localPos and output space position \a outputSpacePos.
+ * local position \a localPos in surface coordinates and output space position \a outputSpacePos.
*/
void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &localPos, const QPointF &outputSpacePos)
{
@@ -253,7 +253,7 @@ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &loca
if (view) {
// We adjust if the mouse position is on the edge
// to work around Qt's event propagation
- QSizeF size(view->surface()->size());
+ QSizeF size(view->surface()->destinationSize());
if (d->localPosition.x() == size.width())
d->localPosition.rx() -= 0.01;
if (d->localPosition.y() == size.height())
@@ -294,7 +294,7 @@ QWaylandView *QWaylandPointer::mouseFocus() const
}
/*!
- * Returns the current local position of the QWaylandPointer.
+ * Returns the current local position of the QWaylandPointer in surface coordinates.
*/
QPointF QWaylandPointer::currentLocalPosition() const
{
diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp
index 8e8a903e3..14b592efb 100644
--- a/src/compositor/compositor_api/qwaylandquickcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandquickcompositor.cpp
@@ -53,6 +53,7 @@
#include "qwaylandquickitem.h"
#include "qwaylandoutput.h"
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
+#include <QtWaylandCompositor/QWaylandViewporter>
#include "qwaylandsurfacegrabber.h"
QT_BEGIN_NAMESPACE
@@ -60,8 +61,9 @@ QT_BEGIN_NAMESPACE
class QWaylandQuickCompositorPrivate : public QWaylandCompositorPrivate
{
public:
- QWaylandQuickCompositorPrivate(QWaylandCompositor *compositor)
+ explicit QWaylandQuickCompositorPrivate(QWaylandCompositor *compositor)
: QWaylandCompositorPrivate(compositor)
+ , m_viewporter(new QWaylandViewporter(compositor))
{
}
protected:
@@ -69,6 +71,8 @@ protected:
{
return new QWaylandQuickSurface();
}
+private:
+ QScopedPointer<QWaylandViewporter> m_viewporter;
};
QWaylandQuickCompositor::QWaylandQuickCompositor(QObject *parent)
@@ -86,15 +90,15 @@ QWaylandQuickCompositor::QWaylandQuickCompositor(QObject *parent)
* For instance, the following code would allow the clients to request \c wl_shell
* surfaces in the compositor using the \c wl_shell interface.
*
- * \code
- * import QtWayland.Compositor 1.0
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* WlShell {
* // ...
* }
* }
- * \endcode
+ * \endqml
*/
void QWaylandQuickCompositor::create()
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 2b24c13b7..7b0d5c5d5 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -64,7 +64,7 @@
#include <QtCore/QMutexLocker>
#include <QtCore/QMutex>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
#include <QThread>
#ifndef GL_TEXTURE_EXTERNAL_OES
@@ -295,7 +295,8 @@ public:
}
auto texture = buffer.toOpenGLTexture();
- m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId() , QSize(surfaceItem->width(), surfaceItem->height()), opt);
+ auto size = surface->bufferSize();
+ m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId(), size, opt);
}
}
emit textureChanged();
@@ -414,7 +415,11 @@ QWaylandSurface *QWaylandQuickItem::surface() const
void QWaylandQuickItem::setSurface(QWaylandSurface *surface)
{
Q_D(QWaylandQuickItem);
+ QWaylandCompositor *oldComp = d->view->surface() ? d->view->surface()->compositor() : nullptr;
d->view->setSurface(surface);
+ QWaylandCompositor *newComp = d->view->surface() ? d->view->surface()->compositor() : nullptr;
+ if (oldComp != newComp)
+ emit compositorChanged();
update();
}
@@ -821,7 +826,15 @@ void QWaylandQuickItem::setOutput(QWaylandOutput *output)
}
/*!
- * \property QWaylandQuickItem::isBufferLocked
+ * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::bufferLocked
+ *
+ * This property holds whether the item's buffer is currently locked. As long as
+ * the buffer is locked, it will not be released and returned to the client.
+ *
+ * The default is false.
+ */
+/*!
+ * \property QWaylandQuickItem::bufferLocked
*
* This property holds whether the item's buffer is currently locked. As long as
* the buffer is locked, it will not be released and returned to the client.
@@ -886,7 +899,7 @@ void QWaylandQuickItem::handleSurfaceChanged()
if (d->oldSurface) {
disconnect(d->oldSurface.data(), &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged);
disconnect(d->oldSurface.data(), &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
- disconnect(d->oldSurface.data(), &QWaylandSurface::sizeChanged, this, &QWaylandQuickItem::updateSize);
+ disconnect(d->oldSurface.data(), &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
disconnect(d->oldSurface.data(), &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
disconnect(d->oldSurface.data(), &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
disconnect(d->oldSurface.data(), &QWaylandSurface::redraw, this, &QQuickItem::update);
@@ -903,7 +916,7 @@ void QWaylandQuickItem::handleSurfaceChanged()
if (QWaylandSurface *newSurface = d->view->surface()) {
connect(newSurface, &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged);
connect(newSurface, &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
- connect(newSurface, &QWaylandSurface::sizeChanged, this, &QWaylandQuickItem::updateSize);
+ connect(newSurface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
connect(newSurface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
connect(newSurface, &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
connect(newSurface, &QWaylandSurface::redraw, this, &QQuickItem::update);
@@ -992,7 +1005,7 @@ void QWaylandQuickItem::updateSize()
QSize size(0, 0);
if (surface())
- size = surface()->size() * (d->scaleFactor() / surface()->bufferScale());
+ size = surface()->destinationSize() * d->scaleFactor();
setImplicitSize(size.width(), size.height());
if (d->sizeFollowsSurface)
@@ -1054,6 +1067,14 @@ bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition)
}
/*!
+ * \qmlmethod point WaylandQuickItem::mapToSurface(point point)
+ *
+ * Maps the given \a point in this item's coordinate system to the equivalent
+ * point within the Wayland surface's coordinate system, and returns the mapped
+ * coordinate.
+ */
+
+/*!
* Maps the given \a point in this item's coordinate system to the equivalent
* point within the Wayland surface's coordinate system, and returns the mapped
* coordinate.
@@ -1061,16 +1082,42 @@ bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition)
QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const
{
Q_D(const QWaylandQuickItem);
- if (!surface() || surface()->size().isEmpty())
+ if (!surface() || surface()->destinationSize().isEmpty())
return point / d->scaleFactor();
- qreal xScale = width() / surface()->size().width() * surface()->bufferScale();
- qreal yScale = height() / surface()->size().height() * surface()->bufferScale();
+ qreal xScale = width() / surface()->destinationSize().width();
+ qreal yScale = height() / surface()->destinationSize().height();
return QPointF(point.x() / xScale, point.y() / yScale);
}
/*!
+ * \qmlmethod point WaylandQuickItem::mapFromSurface(point point)
+ * \since 5.13
+ *
+ * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
+ * point within this item's coordinate system, and returns the mapped coordinate.
+ */
+
+/*!
+ * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
+ * point within this item's coordinate system, and returns the mapped coordinate.
+ *
+ * \since 5.13
+ */
+QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const
+{
+ Q_D(const QWaylandQuickItem);
+ if (!surface() || surface()->destinationSize().isEmpty())
+ return point * d->scaleFactor();
+
+ qreal xScale = width() / surface()->destinationSize().width();
+ qreal yScale = height() / surface()->destinationSize().height();
+
+ return QPointF(point.x() * xScale, point.y() * yScale);
+}
+
+/*!
* \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::sizeFollowsSurface
*
* This property specifies whether the size of the item should always match
@@ -1245,7 +1292,7 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
* If an animation is started, bufferLocked should be set to ensure the item keeps its content
* until the animation finishes
*
- * \sa isBufferLocked
+ * \sa bufferLocked
*/
/*!
@@ -1258,7 +1305,7 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
* If an animation is started, bufferLocked should be set to ensure the item keeps its content
* until the animation finishes
*
- * \sa QWaylandQuickkItem::bufferLocked
+ * \sa QWaylandQuickItem::bufferLocked
*/
QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
@@ -1301,6 +1348,10 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
d->provider->setSmooth(smooth());
node->setRect(rect);
+ qreal scale = surface()->bufferScale();
+ QRectF source = surface()->sourceGeometry();
+ node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));
+
return node;
} else {
Q_ASSERT(!d->provider);
diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h
index 23708353e..2aa8e1b73 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.h
+++ b/src/compositor/compositor_api/qwaylandquickitem.h
@@ -61,7 +61,7 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickItem : public QQuickItem
{
Q_OBJECT
Q_DECLARE_PRIVATE(QWaylandQuickItem)
- Q_PROPERTY(QWaylandCompositor *compositor READ compositor)
+ Q_PROPERTY(QWaylandCompositor *compositor READ compositor NOTIFY compositorChanged)
Q_PROPERTY(QWaylandSurface *surface READ surface WRITE setSurface NOTIFY surfaceChanged)
Q_PROPERTY(bool paintEnabled READ paintEnabled WRITE setPaintEnabled)
Q_PROPERTY(bool touchEventsEnabled READ touchEventsEnabled WRITE setTouchEventsEnabled NOTIFY touchEventsEnabledChanged)
@@ -102,6 +102,7 @@ public:
bool inputRegionContains(const QPointF &localPosition) const;
bool inputRegionContains(const QPointF &localPosition);
Q_INVOKABLE QPointF mapToSurface(const QPointF &point) const;
+ Q_REVISION(13) Q_INVOKABLE QPointF mapFromSurface(const QPointF &point) const;
bool sizeFollowsSurface() const;
void setSizeFollowsSurface(bool sizeFollowsSurface);
@@ -176,6 +177,7 @@ private Q_SLOTS:
Q_SIGNALS:
void surfaceChanged();
+ void compositorChanged();
void touchEventsEnabledChanged();
void originChanged();
void surfaceDestroyed();
diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp
index a7b01ef03..bc5e1d8b2 100644
--- a/src/compositor/compositor_api/qwaylandseat.cpp
+++ b/src/compositor/compositor_api/qwaylandseat.cpp
@@ -54,6 +54,7 @@
#if QT_CONFIG(wayland_datadevice)
#include <QtWaylandCompositor/private/qwldatadevice_p.h>
#endif
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include "extensions/qwlqtkey_p.h"
#include "extensions/qwaylandtextinput.h"
@@ -176,7 +177,7 @@ void QWaylandSeatPrivate::seat_get_touch(wl_seat::Resource *resource, uint32_t i
*/
/*!
- * Constructs a QWaylandSeat for the given \a compositor and with the given \a capabilityFlags.
+ * Constructs a QWaylandSeat for the given \a compositor and \a capabilityFlags.
*/
QWaylandSeat::QWaylandSeat(QWaylandCompositor *compositor, CapabilityFlags capabilityFlags)
: QWaylandObject(*new QWaylandSeatPrivate(this))
@@ -195,6 +196,14 @@ QWaylandSeat::~QWaylandSeat()
{
}
+/*!
+ * Initializes parts of the seat corresponding to the capabilities set in the constructor, or
+ * through setCapabilities().
+ *
+ * \note Normally, this function is called automatically after the seat and compositor have been
+ * created, so calling it manually is usually unnecessary.
+ */
+
void QWaylandSeat::initialize()
{
Q_D(QWaylandSeat);
@@ -210,6 +219,11 @@ void QWaylandSeat::initialize()
d->isInitialized = true;
}
+/*!
+ * Returns true if the QWaylandSeat is initialized; false otherwise.
+ *
+ * The value \c true indicates that it's now possible for clients to start using the seat.
+ */
bool QWaylandSeat::isInitialized() const
{
Q_D(const QWaylandSeat);
@@ -534,6 +548,11 @@ bool QWaylandSeat::setKeyboardFocus(QWaylandSurface *surface)
return true;
}
+
+/*!
+ * Returns the keymap object for this QWaylandSeat.
+ */
+
QWaylandKeymap *QWaylandSeat::keymap()
{
Q_D(const QWaylandSeat);
@@ -631,8 +650,8 @@ bool QWaylandSeat::isOwner(QInputEvent *inputEvent) const
*/
QWaylandSeat *QWaylandSeat::fromSeatResource(struct ::wl_resource *resource)
{
- if (auto *r = QWaylandSeatPrivate::Resource::fromResource(resource))
- return static_cast<QWaylandSeatPrivate *>(r->seat_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandSeatPrivate *>(resource))
+ return p->q_func();
return nullptr;
}
@@ -653,4 +672,65 @@ void QWaylandSeat::handleMouseFocusDestroyed()
emit mouseFocusChanged(d->mouseFocus, oldFocus);
}
+
+/*! \qmlsignal void QtWaylandCompositor::QWaylandSeat::keyboardFocusChanged(QWaylandSurface newFocus, QWaylandSurface oldFocus)
+ *
+ * This signal is emitted when setKeyboardFocus() is called or when a WaylandQuickItem has focus
+ * and the user starts pressing keys.
+ *
+ * \a newFocus has the surface that received keyboard focus; or \c nullptr if no surface has
+ * focus.
+ * \a oldFocus has the surface that lost keyboard focus; or \c nullptr if no surface had focus.
+ */
+
+/*!
+ * \fn void QWaylandSeat::keyboardFocusChanged(QWaylandSurface *newFocus, QWaylandSurface *oldFocus)
+ *
+ * This signal is emitted when setKeyboardFocus() is called.
+ *
+ * \a newFocus has the surface that received keyboard focus; or \c nullptr if no surface has
+ * focus.
+ * \a oldFocus has the surface that lost keyboard focus; or \c nullptr if no surface had focus.
+ */
+
+/*! \qmlsignal void QtWaylandCompositor::QWaylandSeat::cursorSurfaceRequest(QWaylandSurface surface, int hotspotX, int hotspotY)
+ *
+ * This signal is emitted when the client has requested for a specific \a surface to be the mouse
+ * cursor. For example, when the user hovers over a particular surface, and you want the cursor
+ * to change into a resize arrow.
+ *
+ * Both \a hotspotX and \a hotspotY are offsets from the top-left of a pointer surface, where a
+ * click should happen. For example, if the requested cursor surface is an arrow, the parameters
+ * indicate where the arrow's tip is, on that surface.
+ */
+
+
+/*!
+ * \fn void QWaylandSeat::cursorSurfaceRequest(QWaylandSurface *surface, int hotspotX, int hotspotY)
+ *
+ * This signal is emitted when the client has requested for a specific \a surface to be the mouse
+ * cursor. For example, when the user hovers over a particular surface, and you want the cursor
+ * to change into a resize arrow.
+ */
+
+/*!
+ * \property QWaylandSeat::drag
+ *
+ * This property holds the drag and drop operations and sends signals when they start and end.
+ * The property stores details like what image should be under the mouse cursor when the user
+ * drags it.
+ */
+
+/*!
+ * \property QWaylandSeat::keymap
+ * This property holds the keymap object.
+ *
+ * A keymap provides a way to translate actual key scan codes into a meaningful value.
+ * For example, if you use a keymap with a Norwegian layout, the key to the right of
+ * the letter L produces an Ø.
+ *
+ * Keymaps can also be used to customize key functions, such as to specify whether
+ * Control and CAPS lock should be swapped, and so on.
+ */
+
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp
index f457c372c..c79787e6e 100644
--- a/src/compositor/compositor_api/qwaylandsurface.cpp
+++ b/src/compositor/compositor_api/qwaylandsurface.cpp
@@ -59,6 +59,7 @@
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
#include <QtWaylandCompositor/private/qwaylandview_p.h>
#include <QtWaylandCompositor/private/qwaylandseat_p.h>
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtCore/private/qobject_p.h>
@@ -234,21 +235,29 @@ void QWaylandSurfacePrivate::surface_commit(Resource *)
// Needed in order to know whether we want to emit signals later
QSize oldBufferSize = bufferSize;
+ QRectF oldSourceGeometry = sourceGeometry;
+ QSize oldDestinationSize = destinationSize;
bool oldHasContent = hasContent;
int oldBufferScale = bufferScale;
// Update all internal state
if (pending.buffer.hasBuffer() || pending.newlyAttached)
bufferRef = pending.buffer;
+ bufferScale = pending.bufferScale;
bufferSize = bufferRef.size();
- damage = pending.damage.intersected(QRect(QPoint(), bufferSize));
+ QSize surfaceSize = bufferSize / bufferScale;
+ sourceGeometry = !pending.sourceGeometry.isValid() ? QRect(QPoint(), surfaceSize) : pending.sourceGeometry;
+ destinationSize = pending.destinationSize.isEmpty() ? sourceGeometry.size().toSize() : pending.destinationSize;
+ damage = pending.damage.intersected(QRect(QPoint(), destinationSize));
hasContent = bufferRef.hasContent();
- bufferScale = pending.bufferScale;
frameCallbacks << pendingFrameCallbacks;
- inputRegion = pending.inputRegion.intersected(QRect(QPoint(), bufferSize));
- opaqueRegion = pending.opaqueRegion.intersected(QRect(QPoint(), bufferSize));
+ inputRegion = pending.inputRegion.intersected(QRect(QPoint(), destinationSize));
+ opaqueRegion = pending.opaqueRegion.intersected(QRect(QPoint(), destinationSize));
QPoint offsetForNextFrame = pending.offset;
+ if (viewport)
+ viewport->checkCommittedState();
+
// Clear per-commit state
pending.buffer = QWaylandBufferRef();
pending.offset = QPoint();
@@ -268,12 +277,22 @@ void QWaylandSurfacePrivate::surface_commit(Resource *)
emit q->damaged(damage);
- if (oldBufferSize != bufferSize)
+ if (oldBufferSize != bufferSize) {
+ emit q->bufferSizeChanged();
+#if QT_DEPRECATED_SINCE(5, 13)
emit q->sizeChanged();
+#endif
+ }
if (oldBufferScale != bufferScale)
emit q->bufferScaleChanged();
+ if (oldDestinationSize != destinationSize)
+ emit q->destinationSizeChanged();
+
+ if (oldSourceGeometry != sourceGeometry)
+ emit q->sourceGeometryChanged();
+
if (oldHasContent != hasContent)
emit q->hasContentChanged();
@@ -431,9 +450,7 @@ QWaylandClient *QWaylandSurface::client() const
}
/*!
- * \property QWaylandSurface::waylandClient
- *
- * This property holds the \c wl_client using this QWaylandSurface.
+ * Holds the \c wl_client using this QWaylandSurface.
*/
::wl_client *QWaylandSurface::waylandClient() const
{
@@ -461,21 +478,109 @@ bool QWaylandSurface::hasContent() const
}
/*!
+ * \qmlproperty rect QtWaylandCompositor::WaylandSurface::sourceGeometry
+ * \since 5.13
+ *
+ * This property describes the portion of the attached Wayland buffer that should
+ * be drawn on the screen. The coordinates are from the corner of the buffer and are
+ * scaled by \l bufferScale.
+ *
+ * \sa bufferScale
+ * \sa bufferSize
+ * \sa destinationSize
+ */
+
+/*!
+ * \property QWaylandSurface::sourceGeometry
+ * \since 5.13
+ *
+ * This property describes the portion of the attached QWaylandBuffer that should
+ * be drawn on the screen. The coordinates are from the corner of the buffer and are
+ * scaled by \l bufferScale.
+ *
+ * \sa bufferScale
+ * \sa bufferSize
+ * \sa destinationSize
+ */
+QRectF QWaylandSurface::sourceGeometry() const
+{
+ Q_D(const QWaylandSurface);
+ return d->sourceGeometry;
+}
+
+/*!
+ * \qmlproperty size QtWaylandCompositor::WaylandSurface::destinationSize
+ * \since 5.13
+ *
+ * This property holds the size of this WaylandSurface in surface coordinates.
+ *
+ * \sa bufferScale
+ * \sa bufferSize
+ */
+
+/*!
+ * \property QWaylandSurface::destinationSize
+ * \since 5.13
+ *
+ * This property holds the size of this WaylandSurface in surface coordinates.
+ *
+ * \sa bufferScale
+ * \sa bufferSize
+ */
+QSize QWaylandSurface::destinationSize() const
+{
+ Q_D(const QWaylandSurface);
+ return d->destinationSize;
+}
+
+/*!
+ * \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferSize
+ *
+ * This property holds the size of the current buffer of this WaylandSurface in pixels,
+ * not in surface coordinates.
+ *
+ * For the size in surface coordinates, use \l destinationSize instead.
+ *
+ * \sa destinationSize
+ * \sa bufferScale
+ */
+
+/*!
+ * \property QWaylandSurface::bufferSize
+ *
+ * This property holds the size of the current buffer of this QWaylandSurface in pixels,
+ * not in surface coordinates.
+ *
+ * For the size in surface coordinates, use \l destinationSize instead.
+ *
+ * \sa destinationSize
+ * \sa bufferScale
+ */
+QSize QWaylandSurface::bufferSize() const
+{
+ Q_D(const QWaylandSurface);
+ return d->bufferSize;
+}
+
+#if QT_DEPRECATED_SINCE(5, 13)
+/*!
* \qmlproperty size QtWaylandCompositor::WaylandSurface::size
+ * \obsolete use bufferSize or destinationSize instead
*
- * This property holds the WaylandSurface's size in pixels.
+ * This property has been deprecated, use \l bufferSize or \l destinationSize instead.
*/
/*!
* \property QWaylandSurface::size
+ * \obsolete use bufferSize or destinationSize instead
*
- * This property holds the QWaylandSurface's size in pixels.
+ * This property has been deprecated, use \l bufferSize or \l destinationSize instead.
*/
QSize QWaylandSurface::size() const
{
- Q_D(const QWaylandSurface);
- return d->bufferSize;
+ return bufferSize();
}
+#endif
/*!
* \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferScale
@@ -740,12 +845,12 @@ QList<QWaylandView *> QWaylandSurface::views() const
}
/*!
- * Returns the QWaylandSurface corresponding to the Wayland resource \a res.
+ * Returns the QWaylandSurface corresponding to the Wayland resource \a resource.
*/
-QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *res)
+QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *resource)
{
- if (auto *r = QWaylandSurfacePrivate::Resource::fromResource(res))
- return static_cast<QWaylandSurfacePrivate *>(r->surface_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandSurfacePrivate *>(resource))
+ return p->q_func();
return nullptr;
}
@@ -759,11 +864,12 @@ struct wl_resource *QWaylandSurface::resource() const
}
/*!
- * Sets a \a role on the surface. A role defines how a surface will be mapped on screen, without a role
- * a surface is supposed to be hidden. Only one role at all times can be set on a surface. Although
+ * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a role
+ * a surface is supposed to be hidden. Only one role can be set on a surface, at all times. Although
* setting the same role many times is allowed, attempting to change the role of a surface will trigger
* a protocol error to the \a errorResource and send an \a errorCode to the client.
*
+ * Returns true if a role can be assigned; false otherwise.
*/
bool QWaylandSurface::setRole(QWaylandSurfaceRole *role, wl_resource *errorResource, uint32_t errorCode)
{
@@ -904,4 +1010,21 @@ void QWaylandSurfacePrivate::Subsurface::subsurface_set_desync(wl_subsurface::Re
* This signal is emitted when a \a drag has started from this surface.
*/
+/*!
+ * \fn void damaged(const QRegion &rect)
+ *
+ * This signal is emitted when the client tells the compositor that a particular part of, or
+ * possibly the entire surface has been updated, so the compositor can redraw that part.
+ *
+ * While the compositor APIs take care of redrawing automatically, this function may be useful
+ * if you require a specific, custom behavior.
+ */
+
+/*!
+ * \fn void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent)
+ *
+ * This signal is emitted when the client has requested that this surface should be a
+ * subsurface of \a newParent.
+ */
+
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h
index a138b2af5..667f911c3 100644
--- a/src/compositor/compositor_api/qwaylandsurface.h
+++ b/src/compositor/compositor_api/qwaylandsurface.h
@@ -81,7 +81,12 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSurface : public QWaylandObject
Q_OBJECT
Q_DECLARE_PRIVATE(QWaylandSurface)
Q_PROPERTY(QWaylandClient *client READ client CONSTANT)
- Q_PROPERTY(QSize size READ size NOTIFY sizeChanged)
+ Q_PROPERTY(QRectF sourceGeometry READ sourceGeometry NOTIFY sourceGeometryChanged REVISION 13)
+ Q_PROPERTY(QSize destinationSize READ destinationSize NOTIFY destinationSizeChanged REVISION 13)
+ Q_PROPERTY(QSize bufferSize READ bufferSize NOTIFY bufferSizeChanged REVISION 13)
+#if QT_DEPRECATED_SINCE(5, 13)
+ Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) // Qt 6: Remove
+#endif
Q_PROPERTY(int bufferScale READ bufferScale NOTIFY bufferScaleChanged)
Q_PROPERTY(Qt::ScreenOrientation contentOrientation READ contentOrientation NOTIFY contentOrientationChanged)
Q_PROPERTY(QWaylandSurface::Origin origin READ origin NOTIFY originChanged)
@@ -110,7 +115,12 @@ public:
bool hasContent() const;
- QSize size() const;
+ QRectF sourceGeometry() const;
+ QSize destinationSize() const;
+#if QT_DEPRECATED_SINCE(5, 13)
+ QT_DEPRECATED QSize size() const;
+#endif
+ QSize bufferSize() const;
int bufferScale() const;
Qt::ScreenOrientation contentOrientation() const;
@@ -155,7 +165,12 @@ Q_SIGNALS:
void damaged(const QRegion &rect);
void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent);
void childAdded(QWaylandSurface *child);
- void sizeChanged();
+ Q_REVISION(13) void sourceGeometryChanged();
+ Q_REVISION(13) void destinationSizeChanged();
+#if QT_DEPRECATED_SINCE(5, 13)
+ QT_DEPRECATED void sizeChanged();
+#endif
+ Q_REVISION(13) void bufferSizeChanged();
void bufferScaleChanged();
void offsetForNextFrame(const QPoint &offset);
void contentOrientationChanged();
diff --git a/src/compositor/compositor_api/qwaylandsurface_p.h b/src/compositor/compositor_api/qwaylandsurface_p.h
index df868de63..1637d8704 100644
--- a/src/compositor/compositor_api/qwaylandsurface_p.h
+++ b/src/compositor/compositor_api/qwaylandsurface_p.h
@@ -73,6 +73,7 @@
#include <wayland-util.h>
#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <QtWaylandCompositor/private/qwaylandviewporter_p.h>
QT_BEGIN_NAMESPACE
@@ -144,14 +145,17 @@ public: //member variables
QRegion damage;
QWaylandBufferRef bufferRef;
QWaylandSurfaceRole *role = nullptr;
+ QWaylandViewporterPrivate::Viewport *viewport = nullptr;
struct {
QWaylandBufferRef buffer;
QRegion damage;
QPoint offset;
- bool newlyAttached;
+ bool newlyAttached = false;
QRegion inputRegion;
- int bufferScale;
+ int bufferScale = 1;
+ QRectF sourceGeometry;
+ QSize destinationSize;
QRegion opaqueRegion;
} pending;
@@ -166,6 +170,8 @@ public: //member variables
QRegion inputRegion;
QRegion opaqueRegion;
+ QRectF sourceGeometry;
+ QSize destinationSize;
QSize bufferSize;
int bufferScale = 1;
bool isCursorSurface = false;
diff --git a/src/compositor/configure.json b/src/compositor/configure.json
index 9a6655241..aabd2472f 100644
--- a/src/compositor/configure.json
+++ b/src/compositor/configure.json
@@ -80,6 +80,12 @@
"type": "compile",
"test": "dmabuf_server_buffer",
"use": "egl"
+ },
+ "dmabuf-client-buffer": {
+ "label": "Linux Client dma-buf Buffer Sharing",
+ "type": "compile",
+ "test": "dmabuf_client_buffer",
+ "use": "egl"
}
},
@@ -128,6 +134,11 @@
"condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-server-buffer",
"output": [ "privateFeature" ]
},
+ "wayland-dmabuf-client-buffer": {
+ "label": "Linux dma-buf client buffer integration",
+ "condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-client-buffer",
+ "output": [ "privateFeature" ]
+ },
"wayland-shm-emulation-server-buffer": {
"label": "Shm emulation server buffer",
"condition": "features.wayland-server && features.opengl",
diff --git a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc
index 6be6444cc..440a793cb 100644
--- a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc
+++ b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc
@@ -28,65 +28,53 @@
/*!
\page qtwaylandcompositor-index.html
\title Qt Wayland Compositor
- \brief An API to develop display servers supporting the Wayland protocol
-
- \l {https://wayland.freedesktop.org/} {Wayland} is a display server
- protocol to help in creating multi-process systems, where multiple client
- applications may render content on the same display, by going via
- a compositor process.
-
- Compared to a system with a single-process design, a multi-process system
- gives several benefits:
-
- \list
- \li Easier resource management, through the regular operating system mechanisms.
- \li Better security, as each application can run with its own permissions or
- sandbox.
- \li Clearer separation of application UI and compositor UI, so each
- can be modified independently.
- \endlist
-
- In a typical Wayland-based system, multiple client processes will render their
- own contents to off-screen buffers. The information about these buffers will
- then be passed to a display server process by using the Wayland protocol.
- Finally, the display server process will composite and position the contents
- on a physical display.
-
- Qt Wayland Compositor is a module that provides convenient and powerful
- QML and C++ APIs for developing custom display servers based on this protocol.
- The server displays content from client applications that support the Wayland
- protocol. The design philosophy of Wayland is to keep the core protocol simple
- and minimal, and to expand on this with use-case-specific extensions. Qt Wayland
- Compositor supports many common extensions by default, and also has APIs that
- enables the creation of new, custom extensions.
-
- In one typical use case, a display server written with Qt Wayland Compositor will
- be a subsystem inside a larger application manager process. Qt Wayland Compositor
- provides the APIs to communicate with clients and display their contents on screen,
- using C++ for low-level access and the full set of Qt Quick effects, animations and
- convenience when using the QML APIs. A typical application manager would, in addition
- to this, implement features such as application life cycle, virtual keyboard input,
- security and IPC. Qt provides APIs that can be used to develop the remaining parts
- of an application manager in other modules. The \l {https://www.qt.io/qt-automotive-suite/}
- {Qt Automotive Suite} provides a complete application manager which includes a
- display server developed using Qt Wayland Compositor.
+ \brief An API to develop display servers that support the Wayland protocol.
+
+ The Qt Wayland Compositor is a module that provides convenient and powerful
+ QML and C++ APIs for developing custom display servers based on the
+ \l {https://wayland.freedesktop.org/}{Wayland} protocol. The display server,
+ often called a compositor, displays content from client applications that
+ support the Wayland protocol.
+
+ Wayland's design philosophy is to keep the core protocol simple and minimal.
+ Developers can then expand on this core protocol with use-case-specific
+ extensions. Qt Wayland Compositor supports many common extensions by default,
+ and also has APIs to enable the creation of new, custom extensions.
+
+ Typically, a compositor written with Qt Wayland Compositor becomes a
+ subsystem inside a larger application manager process. Qt Wayland Compositor
+ provides the APIs to communicate with clients and display their content on
+ the screen. The QML APIs contain high-level APIs that easily integrate with
+ the rest of Qt, enabling convenient animations, effects, and UI through
+ Qt Quick. There are also C++ APIs available - if you need more low-level
+ access.
+
+ An application manager would typically implement additional features such as
+ application life cycle, virtual keyboard input, security, and Inter-Process
+ Communication (IPC). Qt provides the APIs that can be used to develop the
+ remaining parts of an application manager in other modules. The
+ \l {https://www.qt.io/qt-automotive-suite/}{Qt Automotive Suite} provides
+ \l{Qt Application Manager}, which is a complete application manager that
+ includes a compositor developed using Qt Wayland Compositor.
+
+ For more information on Wayland, see \l{Wayland and Qt}.
\section1 Features of Qt Wayland Compositor
- The Qt Wayland Compositor API includes features needed to create a display server.
+ The Qt Wayland Compositor includes features necessary to create a compositor:
\list
- \li A QML API that can be used to display and manipulate client content, fully
- integrated with all the features in Qt Quick.
- \li A C++ API for low-level access and control.
- \li Support for common extensions, including XDG Shell and IVI Application.
- \li APIs to easily expand support to include custom extensions.
+ \li A QML API to display and manipulate client content, fully integrated
+ with all the features in Qt Quick.
+ \li A C++ API for low-level access and control.
+ \li Support for common extensions, including XDG Shell and IVI Application.
+ \li APIs to easily expand the support for custom extensions.
\endlist
\section1 Environment Variables and Command-line Arguments
- The Qt Wayland Compositor API recognizes some environment variables and
- command-line arguments that can be used to customize its behavior.
+ The Qt Wayland Compositor recognizes the following environment variables and
+ command-line arguments:
\list
\li Environment variables:
@@ -107,8 +95,17 @@
\section1 Examples
- Take a look at the \l{Qt Wayland Compositor Examples} for a demonstration on
- how the APIs can be used to write custom display servers.
+ Take a look at the \l{Qt Wayland Compositor Examples} to learn how these APIs
+ can be used to write custom compositors.
+
+ \section1 API Reference
+
+ The Qt Wayland Compositor can be used from C++ or QML:
+
+ \list
+ \li \l{Qt Wayland Compositor QML Types}
+ \li \l{Qt Wayland Compositor C++ Classes}
+ \endlist
\section1 Licenses and Attributions
@@ -124,13 +121,4 @@
\generatelist{groupsbymodule attributions-qtwaylandcompositor}
- \section1 API Reference
-
- The Qt Wayland Compositor API can be used from C++ or QML.
-
- \list
- \li \l{Qt Wayland Compositor QML Types}
- \li \l{Qt Wayland Compositor C++ Classes}
- \endlist
-
*/
diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri
index 5c708f891..361f93984 100644
--- a/src/compositor/extensions/extensions.pri
+++ b/src/compositor/extensions/extensions.pri
@@ -9,6 +9,8 @@ WAYLANDSERVERSOURCES += \
../extensions/qt-key-unstable-v1.xml \
../extensions/qt-windowmanager.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
+ ../3rdparty/protocol/viewporter.xml \
+ ../3rdparty/protocol/scaler.xml \
../3rdparty/protocol/xdg-shell-unstable-v6.xml \
../3rdparty/protocol/xdg-shell.xml \
../3rdparty/protocol/xdg-decoration-unstable-v1.xml \
@@ -27,6 +29,10 @@ HEADERS += \
extensions/qwaylandtextinputmanager_p.h \
extensions/qwaylandqtwindowmanager.h \
extensions/qwaylandqtwindowmanager_p.h \
+ extensions/qwaylandviewporter.h \
+ extensions/qwaylandviewporter_p.h \
+ extensions/qwaylandwlscaler.h \
+ extensions/qwaylandwlscaler_p.h \
extensions/qwaylandxdgshellv5.h \
extensions/qwaylandxdgshellv5_p.h \
extensions/qwaylandxdgshellv6.h \
@@ -49,6 +55,8 @@ SOURCES += \
extensions/qwaylandtextinput.cpp \
extensions/qwaylandtextinputmanager.cpp \
extensions/qwaylandqtwindowmanager.cpp \
+ extensions/qwaylandviewporter.cpp \
+ extensions/qwaylandwlscaler.cpp \
extensions/qwaylandxdgshellv5.cpp \
extensions/qwaylandxdgshellv6.cpp \
extensions/qwaylandxdgshell.cpp \
diff --git a/src/compositor/extensions/pregenerated/3rdparty/.gitignore b/src/compositor/extensions/pregenerated/3rdparty/.gitignore
new file mode 100644
index 000000000..db253cfaf
--- /dev/null
+++ b/src/compositor/extensions/pregenerated/3rdparty/.gitignore
@@ -0,0 +1,3 @@
+!qwayland-server-*.cpp
+!qwayland-server-*.h
+!wayland-*-protocol.cpp
diff --git a/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h b/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h
index 63817a5e3..8124860b9 100644
--- a/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h
+++ b/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h
@@ -71,6 +71,7 @@ namespace QtWaylandServer {
virtual ~Resource() {}
xdg_shell_v5 *xdg_shell_object;
+ xdg_shell_v5 *object() { return xdg_shell_object; }
struct ::wl_resource *handle;
struct ::wl_client *client() const { return wl_resource_get_client(handle); }
@@ -191,6 +192,7 @@ namespace QtWaylandServer {
virtual ~Resource() {}
xdg_surface_v5 *xdg_surface_object;
+ xdg_surface_v5 *object() { return xdg_surface_object; }
struct ::wl_resource *handle;
struct ::wl_client *client() const { return wl_resource_get_client(handle); }
@@ -364,6 +366,7 @@ namespace QtWaylandServer {
virtual ~Resource() {}
xdg_popup_v5 *xdg_popup_object;
+ xdg_popup_v5 *object() { return xdg_popup_object; }
struct ::wl_resource *handle;
struct ::wl_client *client() const { return wl_resource_get_client(handle); }
diff --git a/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h b/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h
index b979f048c..493fd52d4 100644
--- a/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h
+++ b/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h
@@ -6,7 +6,7 @@
#include <stdint.h>
#include <stddef.h>
-#include "wayland-server.h"
+#include "wayland-server-core.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/compositor/extensions/qwaylandiviapplication.cpp b/src/compositor/extensions/qwaylandiviapplication.cpp
index 57b1627b5..a2e9842db 100644
--- a/src/compositor/extensions/qwaylandiviapplication.cpp
+++ b/src/compositor/extensions/qwaylandiviapplication.cpp
@@ -62,8 +62,9 @@ QT_BEGIN_NAMESPACE
* To provide the functionality of the shell extension in a compositor, create
* an instance of the IviApplication component and add it to the list of extensions
* supported by the compositor:
- * \code
- * import QtWayland.Compositor 1.0
+ *
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* IviApplication {
@@ -74,7 +75,7 @@ QT_BEGIN_NAMESPACE
* }
* }
* }
- * \endcode
+ * \endqml
*/
/*!
@@ -131,6 +132,9 @@ const struct wl_interface *QWaylandIviApplication::interface()
return QWaylandIviApplicationPrivate::interface();
}
+/*!
+ * \internal
+ */
QByteArray QWaylandIviApplication::interfaceName()
{
return QWaylandIviApplicationPrivate::interfaceName();
@@ -140,16 +144,17 @@ QByteArray QWaylandIviApplication::interfaceName()
* \qmlsignal void QtWaylandCompositor::IviApplication::iviSurfaceRequested(WaylandSurface surface, int iviId, WaylandResource resource)
*
* This signal is emitted when the client has requested an \c ivi_surface to be associated
- * with \a surface, which is identified by \a id. The handler for this signal is
- * expected to create the ivi surface and initialize it within the scope of the
+ * with \a surface, which is identified by \a iviId. The handler for this signal is
+ * expected to create the ivi surface for \a resource and initialize it within the scope of the
* signal emission. If no ivi surface is created, a default one will be created instead.
+ *
*/
/*!
* \fn void QWaylandIviApplication::iviSurfaceRequested(QWaylandSurface *surface, uint iviId, const QWaylandResource &resource)
*
* This signal is emitted when the client has requested an \c ivi_surface to be associated
- * with \a surface, which is identified by \a id. The handler for this signal is
+ * with \a surface, which is identified by \a iviId. The handler for this signal is
* expected to create the ivi surface and initialize it within the scope of the
* signal emission. If no ivi surface is created, a default one will be created instead.
*/
diff --git a/src/compositor/extensions/qwaylandivisurface.cpp b/src/compositor/extensions/qwaylandivisurface.cpp
index b6398f060..0ae488def 100644
--- a/src/compositor/extensions/qwaylandivisurface.cpp
+++ b/src/compositor/extensions/qwaylandivisurface.cpp
@@ -47,6 +47,8 @@
#include <QtWaylandCompositor/QWaylandResource>
#include <QDebug>
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
+
QT_BEGIN_NAMESPACE
QWaylandSurfaceRole QWaylandIviSurfacePrivate::s_role("ivi_surface");
@@ -182,10 +184,9 @@ QWaylandSurfaceRole *QWaylandIviSurface::role()
*/
QWaylandIviSurface *QWaylandIviSurface::fromResource(wl_resource *resource)
{
- auto iviSurfaceResource = QWaylandIviSurfacePrivate::Resource::fromResource(resource);
- if (!iviSurfaceResource)
- return nullptr;
- return static_cast<QWaylandIviSurfacePrivate *>(iviSurfaceResource->ivi_surface_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandIviSurfacePrivate *>(resource))
+ return p->q_func();
+ return nullptr;
}
/*!
diff --git a/src/compositor/extensions/qwaylandshellsurface.cpp b/src/compositor/extensions/qwaylandshellsurface.cpp
index 3cfe44895..cb6d03646 100644
--- a/src/compositor/extensions/qwaylandshellsurface.cpp
+++ b/src/compositor/extensions/qwaylandshellsurface.cpp
@@ -87,7 +87,7 @@
*/
/*!
- * \property QWaylandWlShellSurface::windowType
+ * \property QWaylandShellSurface::windowType
*
* This property holds the window type of the QWaylandShellSurface.
*/
diff --git a/src/compositor/extensions/qwaylandtextinput.cpp b/src/compositor/extensions/qwaylandtextinput.cpp
index 6cf33d18d..f60a32a14 100644
--- a/src/compositor/extensions/qwaylandtextinput.cpp
+++ b/src/compositor/extensions/qwaylandtextinput.cpp
@@ -45,12 +45,15 @@
#include "qwaylandsurface.h"
#include "qwaylandview.h"
-#include "qwaylandxkb_p.h"
#include "qwaylandinputmethodeventbuilder_p.h"
#include <QGuiApplication>
#include <QInputMethodEvent>
+#if QT_CONFIG(xkbcommon)
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
QWaylandTextInputClientState::QWaylandTextInputClientState()
@@ -203,11 +206,15 @@ void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event)
// TODO add support for modifiers
- foreach (xkb_keysym_t keysym, QWaylandXkb::toKeysym(event)) {
+#if QT_CONFIG(xkbcommon)
+ for (xkb_keysym_t keysym : QXkbCommon::toKeysym(event)) {
send_keysym(focusResource->handle, event->timestamp(), keysym,
event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
0);
}
+#else
+ Q_UNUSED(event);
+#endif
}
void QWaylandTextInputPrivate::sendInputPanelState()
diff --git a/src/compositor/extensions/qwaylandviewporter.cpp b/src/compositor/extensions/qwaylandviewporter.cpp
new file mode 100644
index 000000000..3856c135d
--- /dev/null
+++ b/src/compositor/extensions/qwaylandviewporter.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandviewporter_p.h"
+
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWaylandViewporter
+ \inmodule QtWaylandCompositor
+ \since 5.13
+ \brief Provides an extension for surface resizing and cropping.
+
+ The QWaylandViewporter extension provides a way for clients to resize and crop surface
+ contents.
+
+ QWaylandViewporter corresponds to the Wayland interface, \c wp_viewporter.
+*/
+
+/*!
+ Constructs a QWaylandViewporter object.
+*/
+QWaylandViewporter::QWaylandViewporter()
+ : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(*new QWaylandViewporterPrivate)
+{
+}
+
+/*!
+ * Constructs a QWaylandViewporter object for the provided \a compositor.
+ */
+QWaylandViewporter::QWaylandViewporter(QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(compositor, *new QWaylandViewporterPrivate())
+{
+}
+
+/*!
+ Initializes the extension.
+*/
+void QWaylandViewporter::initialize()
+{
+ Q_D(QWaylandViewporter);
+
+ QWaylandCompositorExtensionTemplate::initialize();
+ auto *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandViewporter";
+ return;
+ }
+ d->init(compositor->display(), 1);
+}
+
+/*!
+ Returns the Wayland interface for the QWaylandViewporter.
+*/
+const wl_interface *QWaylandViewporter::interface()
+{
+ return QWaylandViewporterPrivate::interface();
+}
+
+void QWaylandViewporterPrivate::wp_viewporter_destroy(Resource *resource)
+{
+ // Viewport objects are allowed ot outlive the viewporter
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandViewporterPrivate::wp_viewporter_get_viewport(Resource *resource, uint id, wl_resource *surfaceResource)
+{
+ auto *surface = QWaylandSurface::fromResource(surfaceResource);
+ if (!surface) {
+ qWarning() << "Couldn't find surface for viewporter";
+ return;
+ }
+
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(surface);
+ if (surfacePrivate->viewport) {
+ wl_resource_post_error(resource->handle, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS,
+ "viewport already exists for surface");
+ return;
+ }
+
+ surfacePrivate->viewport = new Viewport(surface, resource->client(), id);
+}
+
+QWaylandViewporterPrivate::Viewport::Viewport(QWaylandSurface *surface, wl_client *client, int id)
+ : QtWaylandServer::wp_viewport(client, id, /*version*/ 1)
+ , m_surface(surface)
+{
+ Q_ASSERT(surface);
+}
+
+QWaylandViewporterPrivate::Viewport::~Viewport()
+{
+ if (m_surface) {
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ Q_ASSERT(surfacePrivate->viewport == this);
+ surfacePrivate->viewport = nullptr;
+ }
+}
+
+// This function has to be called immediately after a surface is committed, before no
+// other client events have been dispatched, or we may incorrectly error out on an
+// incomplete pending state. See comment below.
+void QWaylandViewporterPrivate::Viewport::checkCommittedState()
+{
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+
+ // We can't use the current state for destination/source when checking,
+ // as that has fallbacks to the buffer size so we can't distinguish
+ // between the set/unset case. We use the pending state because no other
+ // requests has modified it yet.
+ QSize destination = surfacePrivate->pending.destinationSize;
+ QRectF source = surfacePrivate->pending.sourceGeometry;
+
+ if (!destination.isValid() && source.size() != source.size().toSize()) {
+ wl_resource_post_error(resource()->handle, error_bad_size,
+ "non-integer size (%fx%f) with unset destination",
+ source.width(), source.height());
+ return;
+ }
+
+ QRectF max = QRectF(QPointF(), m_surface->bufferSize() / m_surface->bufferScale());
+ // We can't use QRectF.contains, because that would return false for values on the border
+ if (max.united(source) != max) {
+ wl_resource_post_error(resource()->handle, error_out_of_buffer,
+ "source %f,%f, %fx%f extends outside attached buffer %fx%f",
+ source.x(), source.y(), source.width(), source.height(),
+ max.width(), max.height());
+ return;
+ }
+}
+
+
+void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy(Resource *resource)
+{
+ if (m_surface) {
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.destinationSize = QSize();
+ surfacePrivate->pending.sourceGeometry = QRectF();
+ }
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandViewporterPrivate::Viewport::wp_viewport_set_source(QtWaylandServer::wp_viewport::Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
+{
+ Q_UNUSED(resource);
+
+ if (!m_surface) {
+ wl_resource_post_error(resource->handle, error_no_surface,
+ "set_source requested for destroyed surface");
+ return;
+ }
+
+ QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
+ QSizeF size(wl_fixed_to_double(width), wl_fixed_to_double(height));
+ QRectF sourceGeometry(position, size);
+
+ if (sourceGeometry == QRectF(-1, -1, -1, -1)) {
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.sourceGeometry = QRectF();
+ return;
+ }
+
+ if (position.x() < 0 || position.y() < 0) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative position in set_source");
+ return;
+ }
+
+ if (!size.isValid()) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative size in set_source");
+ return;
+ }
+
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.sourceGeometry = sourceGeometry;
+}
+
+void QWaylandViewporterPrivate::Viewport::wp_viewport_set_destination(QtWaylandServer::wp_viewport::Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+
+ if (!m_surface) {
+ wl_resource_post_error(resource->handle, error_no_surface,
+ "set_destination requested for destroyed surface");
+ return;
+ }
+
+ QSize destinationSize(width, height);
+ if (!destinationSize.isValid() && destinationSize != QSize(-1, -1)) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative size in set_destination");
+ return;
+ }
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.destinationSize = destinationSize;
+}
+
+QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandviewporter.h b/src/compositor/extensions/qwaylandviewporter.h
new file mode 100644
index 000000000..811c74145
--- /dev/null
+++ b/src/compositor/extensions/qwaylandviewporter.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDVIEWPORTER_H
+#define QWAYLANDVIEWPORTER_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandViewporterPrivate;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandViewporter
+ : public QWaylandCompositorExtensionTemplate<QWaylandViewporter>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandViewporter)
+
+public:
+ explicit QWaylandViewporter();
+ explicit QWaylandViewporter(QWaylandCompositor *compositor);
+
+ void initialize() override;
+
+ static const struct wl_interface *interface();
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDVIEWPORTER_H
diff --git a/src/compositor/extensions/qwaylandviewporter_p.h b/src/compositor/extensions/qwaylandviewporter_p.h
new file mode 100644
index 000000000..d22da6990
--- /dev/null
+++ b/src/compositor/extensions/qwaylandviewporter_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDVIEWPORTER_P_H
+#define QWAYLANDVIEWPORTER_P_H
+
+#include "qwaylandviewporter.h"
+
+#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h>
+#include <QtWaylandCompositor/private/qwayland-server-viewporter.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandSurface;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandViewporterPrivate
+ : public QWaylandCompositorExtensionPrivate
+ , public QtWaylandServer::wp_viewporter
+{
+ Q_DECLARE_PUBLIC(QWaylandViewporter)
+public:
+ explicit QWaylandViewporterPrivate() = default;
+
+ class Q_WAYLAND_COMPOSITOR_EXPORT Viewport
+ : public QtWaylandServer::wp_viewport
+ {
+ public:
+ explicit Viewport(QWaylandSurface *surface, wl_client *client, int id);
+ ~Viewport() override;
+ void checkCommittedState();
+
+ protected:
+ void wp_viewport_destroy_resource(Resource *resource) override;
+ void wp_viewport_destroy(Resource *resource) override;
+ void wp_viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override;
+ void wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) override;
+
+ private:
+ QPointer<QWaylandSurface> m_surface = nullptr;
+ };
+
+protected:
+ void wp_viewporter_destroy(Resource *resource) override;
+ void wp_viewporter_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDVIEWPORTER_P_H
diff --git a/src/compositor/extensions/qwaylandwlscaler.cpp b/src/compositor/extensions/qwaylandwlscaler.cpp
new file mode 100644
index 000000000..5c8e4b270
--- /dev/null
+++ b/src/compositor/extensions/qwaylandwlscaler.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandwlscaler_p.h"
+
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_DEPRECATED_SINCE(5, 13)
+/*!
+ \qmltype WlScaler
+ \inqmlmodule QtWayland.Compositor
+ \since 5.13
+ \brief Provides an extension for surface resizing and cropping.
+
+ The WlScaler extension provides a way for clients to resize and crop surface contents.
+
+ WlScaler corresponds to the Wayland interface, \c wl_scaler.
+
+ \c wl_scaler is a non-standard and deprecated protocol that has largely been replaced by
+ \c wp_viewporter. I.e. This extensions is only useful for supporting legacy clients.
+ \c wp_viewporter support is enabled automatically for all Qml compositors.
+
+ To provide the functionality of the extension in a compositor, create an instance of the
+ WlScaler component and add it to the list of extensions supported by the compositor:
+
+ \qml \QtMinorVersion
+ import QtWayland.Compositor 1.\1
+
+ WaylandCompositor {
+ // ...
+ WlScaler {}
+ }
+ \endqml
+
+ \deprecated
+*/
+
+/*!
+ \class QWaylandWlScaler
+ \inmodule QtWaylandCompositor
+ \since 5.13
+ \brief Provides an extension for surface resizing and croping.
+
+ The QWaylandWlScaler extension provides a way for clients to resize and crop surface
+ contents.
+
+ QWaylandWlScaler corresponds to the Wayland interface, \c wl_scaler.
+
+ \c wl_scaler is a non-standard and deprecated protocol that has largely been replaced by
+ \c wp_viewporter. I.e. This extensions is only useful for supporting legacy clients.
+
+ \sa QWaylandViewporter
+
+ \deprecated
+*/
+
+/*!
+ Constructs a QWaylandWlScaler object.
+*/
+QWaylandWlScaler::QWaylandWlScaler()
+ : QWaylandCompositorExtensionTemplate<QWaylandWlScaler>(*new QWaylandWlScalerPrivate)
+{
+}
+
+/*!
+ * Constructs a QWaylandWlScaler object for the provided \a compositor.
+ */
+QWaylandWlScaler::QWaylandWlScaler(QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate<QWaylandWlScaler>(compositor, *new QWaylandWlScalerPrivate())
+{
+}
+
+/*!
+ Initializes the extension.
+*/
+void QWaylandWlScaler::initialize()
+{
+ Q_D(QWaylandWlScaler);
+
+ QWaylandCompositorExtensionTemplate::initialize();
+ auto *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandWlScaler";
+ return;
+ }
+ d->init(compositor->display(), 2);
+}
+
+/*!
+ Returns the Wayland interface for the QWaylandWlScaler.
+*/
+const wl_interface *QWaylandWlScaler::interface()
+{
+ return QWaylandWlScalerPrivate::interface();
+}
+
+void QWaylandWlScalerPrivate::scaler_destroy(Resource *resource)
+{
+ // Viewport objects are allowed ot outlive the scaler
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandWlScalerPrivate::scaler_get_viewport(Resource *resource, uint id, wl_resource *surfaceResource)
+{
+ auto *surface = QWaylandSurface::fromResource(surfaceResource);
+ if (!surface) {
+ qWarning() << "Couldn't find surface for viewporter";
+ return;
+ }
+
+ // Note: This will only protect us not creating scalers for surfaces with wp_viewport objects
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(surface);
+ if (surfacePrivate->viewport) {
+ wl_resource_post_error(resource->handle, WL_SCALER_ERROR_VIEWPORT_EXISTS,
+ "viewport already exists for surface");
+ return;
+ }
+
+ // We can't set viewport here, since it's of the new type for wp_viewporter
+// surfacePrivate->viewport = new Viewport(surface, resource->client(), id, resource->version());
+ new Viewport(surface, resource->client(), id, resource->version());
+}
+
+QWaylandWlScalerPrivate::Viewport::Viewport(QWaylandSurface *surface, wl_client *client, int id, int version)
+ : QtWaylandServer::wl_viewport(client, id, version)
+ , m_surface(surface)
+{
+ Q_ASSERT(surface);
+}
+
+//TODO: This isn't currently called
+// This function has to be called immediately after a surface is committed, before no
+// other client events have been dispatched, or we may incorrectly error out on an
+// incomplete pending state. See comment below.
+void QWaylandWlScalerPrivate::Viewport::checkCommittedState()
+{
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+
+ // We can't use the current state for destination/source when checking,
+ // as that has fallbacks to the buffer size so we can't distinguish
+ // between the set/unset case. We use the pending state because no other
+ // requests has modified it yet.
+ QSize destination = surfacePrivate->pending.destinationSize;
+ QRectF source = surfacePrivate->pending.sourceGeometry;
+
+ if (!destination.isValid() && source.size() != source.size().toSize()) {
+ //TODO: Do rounding to nearest integer
+ }
+
+ QRectF max = QRectF(QPointF(), m_surface->bufferSize() / m_surface->bufferScale());
+ // We can't use QRectF.contains, because that would return false for values on the border
+ if (max.united(source) != max) {
+ //TODO: surface contents are no undefined, surface size is still valid though
+ qCDebug(qLcWaylandCompositor) << "Source set outside buffer bounds (client error)";
+ }
+}
+
+
+void QWaylandWlScalerPrivate::Viewport::viewport_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+void QWaylandWlScalerPrivate::Viewport::viewport_destroy(Resource *resource)
+{
+ if (m_surface) {
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.destinationSize = QSize();
+ surfacePrivate->pending.sourceGeometry = QRectF();
+ }
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandWlScalerPrivate::Viewport::viewport_set(QtWaylandServer::wl_viewport::Resource *resource, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height, int32_t dst_width, int32_t dst_height)
+{
+ viewport_set_source(resource, src_x, src_y, src_width, src_height);
+ viewport_set_destination(resource, dst_width, dst_height);
+}
+
+void QWaylandWlScalerPrivate::Viewport::viewport_set_source(QtWaylandServer::wl_viewport::Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
+{
+ Q_UNUSED(resource);
+
+ if (!m_surface) {
+ qCDebug(qLcWaylandCompositor) << "set_source requested for destroyed surface";
+ return;
+ }
+
+ QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
+ QSizeF size(wl_fixed_to_double(width), wl_fixed_to_double(height));
+ QRectF sourceGeometry(position, size);
+
+ if (sourceGeometry == QRectF(-1, -1, -1, -1)) {
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.sourceGeometry = QRectF();
+ return;
+ }
+
+ if (position.x() < 0 || position.y() < 0) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative position in set_source");
+ return;
+ }
+
+ if (!size.isValid()) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative size in set_source");
+ return;
+ }
+
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.sourceGeometry = sourceGeometry;
+}
+
+void QWaylandWlScalerPrivate::Viewport::viewport_set_destination(QtWaylandServer::wl_viewport::Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+
+ if (!m_surface) {
+ qCDebug(qLcWaylandCompositor) << "set_destination requested for destroyed surface";
+ return;
+ }
+
+ QSize destinationSize(width, height);
+ if (!destinationSize.isValid() && destinationSize != QSize(-1, -1)) {
+ wl_resource_post_error(resource->handle, error_bad_value,
+ "negative size in set_destination");
+ return;
+ }
+ auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface);
+ surfacePrivate->pending.destinationSize = destinationSize;
+}
+#endif // QT_DEPRECATED_SINCE
+
+QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandwlscaler.h b/src/compositor/extensions/qwaylandwlscaler.h
new file mode 100644
index 000000000..4ecdf3968
--- /dev/null
+++ b/src/compositor/extensions/qwaylandwlscaler.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDWLSCALER_H
+#define QWAYLANDWLSCALER_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_DEPRECATED_SINCE(5, 13)
+class QWaylandWlScalerPrivate;
+
+// TODO: We should have used the QT_DEPRECATED macro here, but for some reason
+// header file generation stops working when that's added.
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandWlScaler
+ : public QWaylandCompositorExtensionTemplate<QWaylandWlScaler>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandWlScaler)
+
+public:
+ explicit QWaylandWlScaler();
+ explicit QWaylandWlScaler(QWaylandCompositor *compositor);
+
+ void initialize() override;
+
+ static const struct wl_interface *interface();
+};
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDWLSCALER_H
diff --git a/src/compositor/extensions/qwaylandwlscaler_p.h b/src/compositor/extensions/qwaylandwlscaler_p.h
new file mode 100644
index 000000000..d3c2edd76
--- /dev/null
+++ b/src/compositor/extensions/qwaylandwlscaler_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDWLSCALER_P_H
+#define QWAYLANDWLSCALER_P_H
+
+#include "qwaylandwlscaler.h"
+
+#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h>
+#include <QtWaylandCompositor/private/qwayland-server-scaler.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandSurface;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandWlScalerPrivate
+ : public QWaylandCompositorExtensionPrivate
+ , public QtWaylandServer::wl_scaler
+{
+ Q_DECLARE_PUBLIC(QWaylandWlScaler)
+public:
+ explicit QWaylandWlScalerPrivate() = default;
+
+protected:
+ void scaler_destroy(Resource *resource) override;
+ void scaler_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) override;
+
+private:
+ class Viewport : public QtWaylandServer::wl_viewport
+ {
+ public:
+ explicit Viewport(QWaylandSurface *surface, wl_client *client, int id, int version);
+ void checkCommittedState();
+
+ protected:
+ void viewport_destroy_resource(Resource *resource) override;
+ void viewport_destroy(Resource *resource) override;
+ void viewport_set(Resource *resource, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height, int32_t dst_width, int32_t dst_height) override;
+ void viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override;
+ void viewport_set_destination(Resource *resource, int32_t width, int32_t height) override;
+
+ private:
+ QPointer<QWaylandSurface> m_surface = nullptr;
+ };
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDWLSCALER_P_H
diff --git a/src/compositor/extensions/qwaylandwlshell.cpp b/src/compositor/extensions/qwaylandwlshell.cpp
index d932a06c9..9871a8a5a 100644
--- a/src/compositor/extensions/qwaylandwlshell.cpp
+++ b/src/compositor/extensions/qwaylandwlshell.cpp
@@ -44,6 +44,7 @@
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
#include "qwaylandwlshellintegration_p.h"
#endif
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/QWaylandView>
@@ -266,15 +267,16 @@ void QWaylandWlShellSurfacePrivate::shell_surface_set_class(Resource *resource,
* To provide the functionality of the shell extension in a compositor, create
* an instance of the WlShell component and add it to the list of extensions
* supported by the compositor:
- * \code
- * import QtWayland.Compositor 1.0
+ *
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* WlShell {
* // ...
* }
* }
- * \endcode
+ * \endqml
*/
/*!
@@ -699,9 +701,8 @@ void QWaylandWlShellSurface::ping()
*/
QWaylandWlShellSurface *QWaylandWlShellSurface::fromResource(wl_resource *resource)
{
- QWaylandWlShellSurfacePrivate::Resource *res = QWaylandWlShellSurfacePrivate::Resource::fromResource(resource);
- if (res)
- return static_cast<QWaylandWlShellSurfacePrivate *>(res->shell_surface_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandWlShellSurfacePrivate *>(resource))
+ return p->q_func();
return nullptr;
}
diff --git a/src/compositor/extensions/qwaylandwlshell_p.h b/src/compositor/extensions/qwaylandwlshell_p.h
index b2beca169..dbf6e794f 100644
--- a/src/compositor/extensions/qwaylandwlshell_p.h
+++ b/src/compositor/extensions/qwaylandwlshell_p.h
@@ -47,7 +47,7 @@
#include <QtWaylandCompositor/QWaylandWlShellSurface>
#include <QtWaylandCompositor/QWaylandSeat>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
#include <QHash>
#include <QPoint>
#include <QSet>
diff --git a/src/compositor/extensions/qwaylandwlshellintegration.cpp b/src/compositor/extensions/qwaylandwlshellintegration.cpp
index 896b1587d..99a2e7655 100644
--- a/src/compositor/extensions/qwaylandwlshellintegration.cpp
+++ b/src/compositor/extensions/qwaylandwlshellintegration.cpp
@@ -84,8 +84,7 @@ void WlShellIntegration::handleStartResize(QWaylandSeat *seat, QWaylandWlShellSu
grabberState = GrabberState::Resize;
resizeState.seat = seat;
resizeState.resizeEdges = edges;
- float scaleFactor = m_item->view()->output()->scaleFactor();
- resizeState.initialSize = m_shellSurface->surface()->size() / scaleFactor;
+ resizeState.initialSize = m_shellSurface->surface()->destinationSize();
resizeState.initialized = false;
}
@@ -217,9 +216,7 @@ void WlShellIntegration::handleSetPopup(QWaylandSeat *seat, QWaylandSurface *par
t.clear(&t);
m_item->setRotation(0);
m_item->setScale(1.0);
- auto scaleFactor = m_item->output()->scaleFactor() / devicePixelRatio();
- m_item->setX(relativeToParent.x() * scaleFactor);
- m_item->setY(relativeToParent.y() * scaleFactor);
+ m_item->setPosition(m_item->mapFromSurface(relativeToParent));
m_item->setParentItem(parentItem);
}
@@ -267,7 +264,7 @@ void WlShellIntegration::handleShellSurfaceDestroyed()
void WlShellIntegration::handleSurfaceHasContentChanged()
{
- if (m_shellSurface && m_shellSurface->surface()->size().isEmpty()
+ if (m_shellSurface && m_shellSurface->surface()->destinationSize().isEmpty()
&& m_shellSurface->windowType() == Qt::WindowType::Popup) {
handlePopupClosed();
}
@@ -287,9 +284,8 @@ void WlShellIntegration::adjustOffsetForNextFrame(const QPointF &offset)
if (!m_item->view()->isPrimary())
return;
- float scaleFactor = m_item->view()->output()->scaleFactor();
QQuickItem *moveItem = m_item->moveItem();
- moveItem->setPosition(moveItem->position() + offset * scaleFactor / devicePixelRatio());
+ moveItem->setPosition(moveItem->position() + m_item->mapFromSurface(offset));
}
bool WlShellIntegration::mouseMoveEvent(QMouseEvent *event)
diff --git a/src/compositor/extensions/qwaylandwlshellintegration_p.h b/src/compositor/extensions/qwaylandwlshellintegration_p.h
index ff236e636..8af54dfc4 100644
--- a/src/compositor/extensions/qwaylandwlshellintegration_p.h
+++ b/src/compositor/extensions/qwaylandwlshellintegration_p.h
@@ -100,14 +100,14 @@ private:
struct {
QWaylandSeat *seat = nullptr;
QPointF initialOffset;
- bool initialized;
+ bool initialized = false;
} moveState;
struct {
QWaylandSeat *seat = nullptr;
QWaylandWlShellSurface::ResizeEdge resizeEdges;
QSizeF initialSize;
QPointF initialMousePos;
- bool initialized;
+ bool initialized = false;
} resizeState;
bool isPopup = false;
diff --git a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
index 1abd5e3fc..2d283ddf9 100644
--- a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
+++ b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
@@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE
\qmltype XdgDecorationManagerV1
\inqmlmodule QtWayland.Compositor
\since 5.12
- \brief Provides an extension for negotiation of server-side and client-side window decorations
+ \brief Provides an extension for negotiation of server-side and client-side window decorations.
The XdgDecorationManagerV1 extension provides a way for a compositor to announce support for
server-side window decorations, and for xdg-shell clients to communicate whether they prefer
@@ -59,8 +59,8 @@ QT_BEGIN_NAMESPACE
To provide the functionality of the extension in a compositor, create an instance of the
XdgDecorationManagerV1 component and add it to the list of extensions supported by the compositor:
- \code
- import QtWayland.Compositor 1.3
+ \qml \QtMinorVersion
+ import QtWayland.Compositor 1.\1
WaylandCompositor {
// Xdg decoration manager assumes xdg-shell is being used
@@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE
preferredMode: XdgToplevel.ServerSideDecoration
}
}
- \endcode
+ \endqml
\sa XdgToplevel::decorationMode
*/
@@ -82,7 +82,7 @@ QT_BEGIN_NAMESPACE
\class QWaylandXdgDecorationManagerV1
\inmodule QtWaylandCompositor
\since 5.12
- \brief Provides an extension for negotiation of server-side and client-side window decorations
+ \brief Provides an extension for negotiation of server-side and client-side window decorations.
The QWaylandXdgDecorationManagerV1 extension provides a way for a compositor to announce support
for server-side window decorations, and for xdg-shell clients to communicate whether they prefer
diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp
index bd332287e..eb7b958b4 100644
--- a/src/compositor/extensions/qwaylandxdgshell.cpp
+++ b/src/compositor/extensions/qwaylandxdgshell.cpp
@@ -40,6 +40,7 @@
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
#include "qwaylandxdgshellintegration_p.h"
#endif
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/QWaylandSeat>
@@ -153,15 +154,16 @@ void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t seri
* To provide the functionality of the shell extension in a compositor, create
* an instance of the XdgShell component and add it to the list of extensions
* supported by the compositor:
- * \code
- * import QtWayland.Compositor 1.3
+ *
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* XdgShell {
* // ...
* }
* }
- * \endcode
+ * \endqml
*/
/*!
@@ -311,7 +313,7 @@ QRect QWaylandXdgSurfacePrivate::calculateFallbackWindowGeometry() const
{
// TODO: The unset window geometry should include subsurfaces as well, so this solution
// won't work too well on those kinds of clients.
- return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale());
+ return QRect(QPoint(), m_surface->destinationSize());
}
void QWaylandXdgSurfacePrivate::updateFallbackWindowGeometry()
@@ -385,6 +387,7 @@ void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surfa
"xdg_surface.get_popup without positioner");
return;
}
+
if (!positioner->m_data.isComplete()) {
QWaylandXdgPositionerData p = positioner->m_data;
wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
@@ -393,6 +396,17 @@ void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surfa
return;
}
+ QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size());
+ if (!anchorBounds.contains(positioner->m_data.anchorRect)) {
+ // TODO: this is a protocol error and should ideally be handled like this:
+ //wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
+ // "xdg_positioner anchor rect extends beyound its parent's window geometry");
+ //return;
+ // However, our own clients currently do this, so we'll settle for a gentle warning instead.
+ qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: xdg_positioner anchor"
+ << "rect extends beyond its parent's window geometry";
+ }
+
if (!m_surface->setRole(QWaylandXdgPopup::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE))
return;
@@ -510,7 +524,7 @@ void QWaylandXdgSurface::initialize(QWaylandXdgShell *xdgShell, QWaylandSurface
d->init(resource.resource());
setExtensionContainer(surface);
d->m_windowGeometry = d->calculateFallbackWindowGeometry();
- connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged);
+ connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged);
connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurface::handleBufferScaleChanged);
emit shellChanged();
emit surfaceChanged();
@@ -674,10 +688,9 @@ QByteArray QWaylandXdgSurface::interfaceName()
*/
QWaylandXdgSurface *QWaylandXdgSurface::fromResource(wl_resource *resource)
{
- auto xsResource = QWaylandXdgSurfacePrivate::Resource::fromResource(resource);
- if (!xsResource)
- return nullptr;
- return static_cast<QWaylandXdgSurfacePrivate *>(xsResource->xdg_surface_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandXdgSurfacePrivate *>(resource))
+ return p->q_func();
+ return nullptr;
}
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
@@ -1182,8 +1195,8 @@ QWaylandSurfaceRole *QWaylandXdgToplevel::role()
*/
QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource)
{
- if (auto *r = QWaylandXdgToplevelPrivate::Resource::fromResource(resource))
- return static_cast<QWaylandXdgToplevelPrivate *>(r->xdg_toplevel_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandXdgToplevelPrivate *>(resource))
+ return p->q_func();
return nullptr;
}
@@ -1869,16 +1882,13 @@ QWaylandXdgPopupPrivate::QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface,
QWaylandXdgPositioner *positioner, const QWaylandResource &resource)
: m_xdgSurface(xdgSurface)
, m_parentXdgSurface(parentXdgSurface)
+ , m_positionerData(positioner->m_data)
{
+ Q_ASSERT(m_positionerData.isComplete());
init(resource.resource());
- m_positionerData = positioner->m_data;
-
- if (!m_positionerData.isComplete())
- qWarning() << "Trying to create xdg popup with incomplete positioner";
QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup);
- //TODO: positioner rect may not extend parent's window geometry, enforce this?
//TODO: Need an API for sending a different initial configure
sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size));
}
@@ -2064,9 +2074,7 @@ void QWaylandXdgPositioner::xdg_positioner_set_offset(QtWaylandServer::xdg_posit
QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource)
{
- if (auto *r = Resource::fromResource(resource))
- return static_cast<QWaylandXdgPositioner *>(r->xdg_positioner_object);
- return nullptr;
+ return QtWayland::fromResource<QWaylandXdgPositioner *>(resource);
}
Qt::Edges QWaylandXdgPositioner::convertToEdges(anchor anchor)
diff --git a/src/compositor/extensions/qwaylandxdgshellintegration.cpp b/src/compositor/extensions/qwaylandxdgshellintegration.cpp
index cc8faf6c7..3de52944b 100644
--- a/src/compositor/extensions/qwaylandxdgshellintegration.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellintegration.cpp
@@ -73,7 +73,7 @@ XdgToplevelIntegration::XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *it
connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){
handlePopupCreated(item, popup);
});
- connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged);
+ connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged);
connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelIntegration::handleToplevelDestroyed);
}
@@ -130,7 +130,7 @@ void XdgToplevelIntegration::handleStartResize(QWaylandSeat *seat, Qt::Edges edg
resizeState.resizeEdges = edges;
resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
resizeState.initialPosition = m_item->moveItem()->position();
- resizeState.initialSurfaceSize = m_item->surface()->size();
+ resizeState.initialSurfaceSize = m_item->surface()->destinationSize();
resizeState.initialized = false;
}
@@ -247,14 +247,14 @@ void XdgToplevelIntegration::handleActivatedChanged()
void XdgToplevelIntegration::handleSurfaceSizeChanged()
{
if (grabberState == GrabberState::Resize) {
- qreal x = resizeState.initialPosition.x();
- qreal y = resizeState.initialPosition.y();
+ qreal dx = 0;
+ qreal dy = 0;
if (resizeState.resizeEdges & Qt::TopEdge)
- y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height();
-
+ dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height();
if (resizeState.resizeEdges & Qt::LeftEdge)
- x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width();
- m_item->moveItem()->setPosition(QPointF(x, y));
+ dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width();
+ QPointF offset = m_item->mapFromSurface({dx, dy});
+ m_item->moveItem()->setPosition(resizeState.initialPosition + offset);
}
}
@@ -285,11 +285,11 @@ void XdgPopupIntegration::handleGeometryChanged()
{
if (m_item->view()->output()) {
const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft();
- const QPoint position = m_popup->unconstrainedPosition() + windowOffset;
+ const QPoint surfacePosition = m_popup->unconstrainedPosition() + windowOffset;
+ const QPoint itemPosition = m_item->mapFromSurface(surfacePosition).toPoint();
//TODO: positioner size or other size...?
- const float scaleFactor = m_item->view()->output()->scaleFactor();
//TODO check positioner constraints etc... sliding, flipping
- m_item->moveItem()->setPosition(position * scaleFactor);
+ m_item->moveItem()->setPosition(itemPosition);
} else {
qWarning() << "XdgPopupIntegration popup item without output" << m_item;
}
diff --git a/src/compositor/extensions/qwaylandxdgshellv5.cpp b/src/compositor/extensions/qwaylandxdgshellv5.cpp
index a6e88aabb..0628f55e7 100644
--- a/src/compositor/extensions/qwaylandxdgshellv5.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv5.cpp
@@ -43,6 +43,7 @@
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
#include "qwaylandxdgshellv5integration_p.h"
#endif
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/QWaylandSurface>
@@ -249,7 +250,7 @@ QRect QWaylandXdgSurfaceV5Private::calculateFallbackWindowGeometry() const
{
// TODO: The unset window geometry should include subsurfaces as well, so this solution
// won't work too well on those kinds of clients.
- return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale());
+ return QRect(QPoint(), m_surface->destinationSize());
}
void QWaylandXdgSurfaceV5Private::updateFallbackWindowGeometry()
@@ -512,15 +513,17 @@ void QWaylandXdgPopupV5Private::xdg_popup_destroy(Resource *resource)
*
* To provide the functionality of the shell extension in a compositor, create
* an instance of the XdgShellV5 component and add it as a child of the
- * compositor: \code
- * import QtWayland.Compositor 1.0
+ * compositor:
+ *
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* XdgShellV5 {
* // ...
* }
* }
- * \endcode
+ * \endqml
*
* \deprecated
*/
@@ -837,7 +840,7 @@ void QWaylandXdgSurfaceV5::initialize(QWaylandXdgShellV5 *xdgShell, QWaylandSurf
d->init(resource.resource());
setExtensionContainer(surface);
d->m_windowGeometry = d->calculateFallbackWindowGeometry();
- connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurfaceV5::handleSurfaceSizeChanged);
+ connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurfaceV5::handleSurfaceSizeChanged);
connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurfaceV5::handleBufferScaleChanged);
emit shellChanged();
emit surfaceChanged();
@@ -1179,10 +1182,9 @@ QWaylandSurfaceRole *QWaylandXdgSurfaceV5::role()
*/
QWaylandXdgSurfaceV5 *QWaylandXdgSurfaceV5::fromResource(wl_resource *resource)
{
- auto xsResource = QWaylandXdgSurfaceV5Private::Resource::fromResource(resource);
- if (!xsResource)
- return nullptr;
- return static_cast<QWaylandXdgSurfaceV5Private *>(xsResource->xdg_surface_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandXdgSurfaceV5Private *>(resource))
+ return p->q_func();
+ return nullptr;
}
QSize QWaylandXdgSurfaceV5::sizeForResize(const QSizeF &size, const QPointF &delta,
@@ -1497,10 +1499,9 @@ QWaylandSurfaceRole *QWaylandXdgPopupV5::role()
QWaylandXdgPopupV5 *QWaylandXdgPopupV5::fromResource(wl_resource *resource)
{
- auto popupResource = QWaylandXdgPopupV5Private::Resource::fromResource(resource);
- if (!popupResource)
- return nullptr;
- return static_cast<QWaylandXdgPopupV5Private *>(popupResource->xdg_popup_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandXdgPopupV5Private *>(resource))
+ return p->q_func();
+ return nullptr;
}
void QWaylandXdgPopupV5::sendPopupDone()
diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp
index ea04a33d2..1d63632a3 100644
--- a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp
@@ -71,7 +71,7 @@ XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item
connect(m_xdgSurface, &QWaylandXdgSurfaceV5::unsetMaximized, this, &XdgShellV5Integration::handleUnsetMaximized);
connect(m_xdgSurface, &QWaylandXdgSurfaceV5::maximizedChanged, this, &XdgShellV5Integration::handleMaximizedChanged);
connect(m_xdgSurface, &QWaylandXdgSurfaceV5::activatedChanged, this, &XdgShellV5Integration::handleActivatedChanged);
- connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgShellV5Integration::handleSurfaceSizeChanged);
+ connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgShellV5Integration::handleSurfaceSizeChanged);
connect(m_xdgSurface->shell(), &QWaylandXdgShellV5::xdgPopupCreated, this, [item](QWaylandXdgPopupV5 *popup){
handlePopupCreated(item, popup);
});
@@ -139,7 +139,7 @@ void XdgShellV5Integration::handleStartResize(QWaylandSeat *seat, QWaylandXdgSur
resizeState.resizeEdges = edges;
resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
resizeState.initialPosition = m_item->moveItem()->position();
- resizeState.initialSurfaceSize = m_item->surface()->size();
+ resizeState.initialSurfaceSize = m_item->surface()->destinationSize();
resizeState.initialized = false;
}
@@ -194,14 +194,14 @@ void XdgShellV5Integration::handleActivatedChanged()
void XdgShellV5Integration::handleSurfaceSizeChanged()
{
if (grabberState == GrabberState::Resize) {
- qreal x = resizeState.initialPosition.x();
- qreal y = resizeState.initialPosition.y();
+ qreal dx = 0;
+ qreal dy = 0;
if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::TopEdge)
- y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height();
-
+ dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height();
if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::LeftEdge)
- x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width();
- m_item->moveItem()->setPosition(QPointF(x, y));
+ dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width();
+ QPointF offset = m_item->mapFromSurface({dx, dy});
+ m_item->moveItem()->setPosition(resizeState.initialPosition + offset);
}
}
@@ -212,10 +212,12 @@ XdgPopupV5Integration::XdgPopupV5Integration(QWaylandQuickShellSurfaceItem *item
, m_xdgShell(QWaylandXdgPopupV5Private::get(m_xdgPopup)->m_xdgShell)
{
item->setSurface(m_xdgPopup->surface());
- if (item->view()->output())
- item->moveItem()->setPosition(QPointF(m_xdgPopup->position() * item->view()->output()->scaleFactor()));
- else
+ if (item->view()->output()) {
+ QPoint position = item->mapFromSurface(m_xdgPopup->position()).toPoint();
+ item->moveItem()->setPosition(position);
+ } else {
qWarning() << "XdgPopupV5Integration popup item without output" << item;
+ }
QWaylandClient *client = m_xdgPopup->surface()->client();
auto shell = m_xdgShell;
diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h
index b2c16c6d9..5d0e1e142 100644
--- a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h
+++ b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h
@@ -91,7 +91,7 @@ private:
struct {
QWaylandSeat *seat = nullptr;
QPointF initialOffset;
- bool initialized;
+ bool initialized = false;
} moveState;
struct {
@@ -101,7 +101,7 @@ private:
QPointF initialMousePos;
QPointF initialPosition;
QSize initialSurfaceSize;
- bool initialized;
+ bool initialized = false;
} resizeState;
struct {
diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp
index 8338fe6e2..f971fe5b4 100644
--- a/src/compositor/extensions/qwaylandxdgshellv6.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp
@@ -40,6 +40,7 @@
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
#include "qwaylandxdgshellv6integration_p.h"
#endif
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/QWaylandSeat>
@@ -158,15 +159,16 @@ void QWaylandXdgShellV6Private::zxdg_shell_v6_pong(Resource *resource, uint32_t
* To provide the functionality of the shell extension in a compositor, create
* an instance of the XdgShellV6 component and add it to the list of extensions
* supported by the compositor:
- * \code
- * import QtWayland.Compositor 1.1
+ *
+ * \qml \QtMinorVersion
+ * import QtWayland.Compositor 1.\1
*
* WaylandCompositor {
* XdgShellV6 {
* // ...
* }
* }
- * \endcode
+ * \endqml
*/
/*!
@@ -316,7 +318,7 @@ QRect QWaylandXdgSurfaceV6Private::calculateFallbackWindowGeometry() const
{
// TODO: The unset window geometry should include subsurfaces as well, so this solution
// won't work too well on those kinds of clients.
- return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale());
+ return QRect(QPoint(), m_surface->destinationSize());
}
void QWaylandXdgSurfaceV6Private::updateFallbackWindowGeometry()
@@ -390,6 +392,7 @@ void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_popup(QtWaylandServer::zxd
"zxdg_surface_v6.get_popup without positioner");
return;
}
+
if (!positioner->m_data.isComplete()) {
QWaylandXdgPositionerV6Data p = positioner->m_data;
wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
@@ -398,6 +401,17 @@ void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_popup(QtWaylandServer::zxd
return;
}
+ QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size());
+ if (!anchorBounds.contains(positioner->m_data.anchorRect)) {
+ // TODO: this is a protocol error and should ideally be handled like this:
+ //wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
+ // "zxdg_positioner_v6 anchor rect extends beyound its parent's window geometry");
+ //return;
+ // However, our own clients currently do this, so we'll settle for a gentle warning instead.
+ qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: zxdg_positioner_v6 anchor"
+ << "rect extends beyond its parent's window geometry";
+ }
+
if (!m_surface->setRole(QWaylandXdgPopupV6::role(), resource->handle, ZXDG_SHELL_V6_ERROR_ROLE))
return;
@@ -515,7 +529,7 @@ void QWaylandXdgSurfaceV6::initialize(QWaylandXdgShellV6 *xdgShell, QWaylandSurf
d->init(resource.resource());
setExtensionContainer(surface);
d->m_windowGeometry = d->calculateFallbackWindowGeometry();
- connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurfaceV6::handleSurfaceSizeChanged);
+ connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurfaceV6::handleSurfaceSizeChanged);
connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurfaceV6::handleBufferScaleChanged);
emit shellChanged();
emit surfaceChanged();
@@ -679,10 +693,9 @@ QByteArray QWaylandXdgSurfaceV6::interfaceName()
*/
QWaylandXdgSurfaceV6 *QWaylandXdgSurfaceV6::fromResource(wl_resource *resource)
{
- auto xsResource = QWaylandXdgSurfaceV6Private::Resource::fromResource(resource);
- if (!xsResource)
- return nullptr;
- return static_cast<QWaylandXdgSurfaceV6Private *>(xsResource->zxdg_surface_v6_object)->q_func();
+ if (auto p = QtWayland::fromResource<QWaylandXdgSurfaceV6Private *>(resource))
+ return p->q_func();
+ return nullptr;
}
#ifdef QT_WAYLAND_COMPOSITOR_QUICK
@@ -1800,16 +1813,13 @@ QWaylandXdgPopupV6Private::QWaylandXdgPopupV6Private(QWaylandXdgSurfaceV6 *xdgSu
QWaylandXdgPositionerV6 *positioner, const QWaylandResource &resource)
: m_xdgSurface(xdgSurface)
, m_parentXdgSurface(parentXdgSurface)
+ , m_positionerData(positioner->m_data)
{
+ Q_ASSERT(m_positionerData.isComplete());
init(resource.resource());
- m_positionerData = positioner->m_data;
-
- if (!m_positionerData.isComplete())
- qWarning() << "Trying to create xdg popup with incomplete positioner";
QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup);
- //TODO: positioner rect may not extend parent's window geometry, enforce this?
//TODO: Need an API for sending a different initial configure
sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size));
}
@@ -1995,9 +2005,7 @@ void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_offset(QtWaylandServer::zxd
QWaylandXdgPositionerV6 *QWaylandXdgPositionerV6::fromResource(wl_resource *resource)
{
- if (auto *r = Resource::fromResource(resource))
- return static_cast<QWaylandXdgPositionerV6 *>(r->zxdg_positioner_v6_object);
- return nullptr;
+ return QtWayland::fromResource<QWaylandXdgPositionerV6 *>(resource);
}
QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp
index 61a9092a3..66dbc6841 100644
--- a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp
+++ b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp
@@ -73,7 +73,7 @@ XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem
connect(m_xdgSurface->shell(), &QWaylandXdgShellV6::popupCreated, this, [item](QWaylandXdgPopupV6 *popup, QWaylandXdgSurfaceV6 *){
handlePopupCreated(item, popup);
});
- connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged);
+ connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged);
connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelV6Integration::handleToplevelDestroyed);
}
@@ -130,7 +130,7 @@ void XdgToplevelV6Integration::handleStartResize(QWaylandSeat *seat, Qt::Edges e
resizeState.resizeEdges = edges;
resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
resizeState.initialPosition = m_item->moveItem()->position();
- resizeState.initialSurfaceSize = m_item->surface()->size();
+ resizeState.initialSurfaceSize = m_item->surface()->destinationSize();
resizeState.initialized = false;
}
@@ -247,14 +247,14 @@ void XdgToplevelV6Integration::handleActivatedChanged()
void XdgToplevelV6Integration::handleSurfaceSizeChanged()
{
if (grabberState == GrabberState::Resize) {
- qreal x = resizeState.initialPosition.x();
- qreal y = resizeState.initialPosition.y();
+ qreal dx = 0;
+ qreal dy = 0;
if (resizeState.resizeEdges & Qt::TopEdge)
- y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height();
-
+ dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height();
if (resizeState.resizeEdges & Qt::LeftEdge)
- x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width();
- m_item->moveItem()->setPosition(QPointF(x, y));
+ dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width();
+ QPointF offset = m_item->mapFromSurface({dx, dy});
+ m_item->moveItem()->setPosition(resizeState.initialPosition + offset);
}
}
@@ -285,11 +285,11 @@ void XdgPopupV6Integration::handleGeometryChanged()
{
if (m_item->view()->output()) {
const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft();
- const QPoint position = m_popup->unconstrainedPosition() + windowOffset;
+ const QPoint surfacePosition = m_popup->unconstrainedPosition() + windowOffset;
+ const QPoint itemPosition = m_item->mapFromSurface(surfacePosition).toPoint();
//TODO: positioner size or other size...?
- const float scaleFactor = m_item->view()->output()->scaleFactor();
//TODO check positioner constraints etc... sliding, flipping
- m_item->moveItem()->setPosition(position * scaleFactor);
+ m_item->moveItem()->setPosition(itemPosition);
} else {
qWarning() << "XdgPopupV6Integration popup item without output" << m_item;
}
diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h
index 5b56af89b..049b901c9 100644
--- a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h
+++ b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h
@@ -96,7 +96,7 @@ private:
struct {
QWaylandSeat *seat = nullptr;
QPointF initialOffset;
- bool initialized;
+ bool initialized = false;
} moveState;
struct {
@@ -106,7 +106,7 @@ private:
QPointF initialMousePos;
QPointF initialPosition;
QSize initialSurfaceSize;
- bool initialized;
+ bool initialized = false;
} resizeState;
struct {
diff --git a/src/compositor/global/global.pri b/src/compositor/global/global.pri
index 29d4f4376..172f916bf 100644
--- a/src/compositor/global/global.pri
+++ b/src/compositor/global/global.pri
@@ -4,6 +4,7 @@ HEADERS += \
global/qtwaylandcompositorglobal.h \
global/qwaylandcompositorextension.h \
global/qwaylandcompositorextension_p.h \
+ global/qwaylandutils_p.h \
global/qwaylandquickextension.h \
SOURCES += \
diff --git a/src/compositor/global/qwaylandcompositorextension.cpp b/src/compositor/global/qwaylandcompositorextension.cpp
index e50df48bd..912985399 100644
--- a/src/compositor/global/qwaylandcompositorextension.cpp
+++ b/src/compositor/global/qwaylandcompositorextension.cpp
@@ -44,7 +44,7 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
diff --git a/src/compositor/global/qwaylandutils_p.h b/src/compositor/global/qwaylandutils_p.h
new file mode 100644
index 000000000..934e27617
--- /dev/null
+++ b/src/compositor/global/qwaylandutils_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDUTILS_P_H
+#define QWAYLANDUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+struct wl_resource;
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWayland {
+
+template<typename return_type>
+return_type fromResource(struct ::wl_resource *resource) {
+ if (auto *r = std::remove_pointer<return_type>::type::Resource::fromResource(resource))
+ return static_cast<return_type>(r->object());
+ return nullptr;
+}
+
+} // namespace QtWayland
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDUTILS_P_H
diff --git a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h
index 13a69fce9..7b458fbc2 100644
--- a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h
+++ b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h
@@ -55,7 +55,7 @@
#include <QtWaylandCompositor/qwaylandsurface.h>
#include <QtWaylandCompositor/qwaylandbufferref.h>
#include <QtCore/QSize>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
diff --git a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp
index 7df9ead3c..cb1ee3da0 100644
--- a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp
+++ b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp
@@ -47,7 +47,7 @@
#include <QtCore/QDebug>
-#include <wayland-server-protocol.h>
+#include <QtWaylandCompositor/private/wayland-wayland-server-protocol.h>
#include "qwaylandsharedmemoryformathelper_p.h"
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
diff --git a/src/compositor/wayland_wrapper/qwlclientbuffer_p.h b/src/compositor/wayland_wrapper/qwlclientbuffer_p.h
index ac8c1ed01..f31ef5d46 100644
--- a/src/compositor/wayland_wrapper/qwlclientbuffer_p.h
+++ b/src/compositor/wayland_wrapper/qwlclientbuffer_p.h
@@ -59,7 +59,7 @@
#include <QtWaylandCompositor/QWaylandSurface>
#include <QtWaylandCompositor/QWaylandBufferRef>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
@@ -110,7 +110,7 @@ protected:
void ref();
void deref();
void sendRelease();
- void setDestroyed();
+ virtual void setDestroyed();
struct ::wl_resource *m_buffer = nullptr;
QRegion m_damage;
diff --git a/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h b/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h
index eca6d4b54..aed4e9185 100644
--- a/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h
+++ b/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h
@@ -109,7 +109,7 @@ private:
QMimeData m_retainedData;
QSocketNotifier *m_retainedReadNotifier = nullptr;
QList<QSocketNotifier *> m_obsoleteRetainedReadNotifiers;
- int m_retainedReadIndex;
+ int m_retainedReadIndex = 0;
QByteArray m_retainedReadBuf;
bool m_compositorOwnsSelection = false;
diff --git a/src/compositor/wayland_wrapper/qwldatasource.cpp b/src/compositor/wayland_wrapper/qwldatasource.cpp
index baa47d6fc..f5f456790 100644
--- a/src/compositor/wayland_wrapper/qwldatasource.cpp
+++ b/src/compositor/wayland_wrapper/qwldatasource.cpp
@@ -41,6 +41,7 @@
#include "qwldataoffer_p.h"
#include "qwldatadevice_p.h"
#include "qwldatadevicemanager_p.h"
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <unistd.h>
#include <QtWaylandCompositor/private/wayland-wayland-server-protocol.h>
@@ -101,7 +102,7 @@ void DataSource::setDevice(DataDevice *device)
DataSource *DataSource::fromResource(struct ::wl_resource *resource)
{
- return static_cast<DataSource *>(Resource::fromResource(resource)->data_source_object);
+ return QtWayland::fromResource<DataSource *>(resource);
}
void DataSource::data_source_offer(Resource *, const QString &mime_type)
diff --git a/src/compositor/wayland_wrapper/qwlregion.cpp b/src/compositor/wayland_wrapper/qwlregion.cpp
index 52c19e946..4383474cb 100644
--- a/src/compositor/wayland_wrapper/qwlregion.cpp
+++ b/src/compositor/wayland_wrapper/qwlregion.cpp
@@ -39,6 +39,8 @@
#include "qwlregion_p.h"
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
+
QT_BEGIN_NAMESPACE
namespace QtWayland {
@@ -54,9 +56,7 @@ Region::~Region()
Region *Region::fromResource(struct ::wl_resource *resource)
{
- if (auto *r = Resource::fromResource(resource))
- return static_cast<Region *>(r->region_object);
- return nullptr;
+ return QtWayland::fromResource<Region *>(resource);
}
void Region::region_destroy_resource(Resource *)
diff --git a/src/compositor/wayland_wrapper/wayland_wrapper.pri b/src/compositor/wayland_wrapper/wayland_wrapper.pri
index 3041d7696..08dd38e82 100644
--- a/src/compositor/wayland_wrapper/wayland_wrapper.pri
+++ b/src/compositor/wayland_wrapper/wayland_wrapper.pri
@@ -1,18 +1,16 @@
CONFIG += wayland-scanner
-WAYLANDSERVERSOURCES_SYSTEM += \
- ../3rdparty/protocol/wayland.xml \
+WAYLANDSERVERSOURCES += \
+ ../3rdparty/protocol/wayland.xml
HEADERS += \
wayland_wrapper/qwlbuffermanager_p.h \
wayland_wrapper/qwlclientbuffer_p.h \
- wayland_wrapper/qwlregion_p.h \
- ../shared/qwaylandxkb_p.h \
+ wayland_wrapper/qwlregion_p.h
SOURCES += \
wayland_wrapper/qwlbuffermanager.cpp \
wayland_wrapper/qwlclientbuffer.cpp \
- wayland_wrapper/qwlregion.cpp \
- ../shared/qwaylandxkb.cpp \
+ wayland_wrapper/qwlregion.cpp
qtConfig(wayland-datadevice) {
HEADERS += \
diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp
index cd0f285cd..59dc76825 100644
--- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp
+++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp
@@ -57,7 +57,7 @@ QWaylandBrcmEglIntegration::QWaylandBrcmEglIntegration()
qDebug() << "Using Brcm-EGL";
}
-void QWaylandBrcmEglIntegration::wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
+void QWaylandBrcmEglIntegration::wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
{
Q_UNUSED(version);
if (interface == "qt_brcm") {
diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h
index 5e8a3bf46..f65a88b1b 100644
--- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h
+++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h
@@ -41,7 +41,8 @@
#define QWAYLANDBRCMEGLINTEGRATION_H
#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h>
-#include <wayland-client.h>
+#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
+#include <wayland-client-core.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -86,12 +87,12 @@ public:
void *nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) override;
private:
- static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version);
+ static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version);
struct wl_display *m_waylandDisplay = nullptr;
struct qt_brcm *m_waylandBrcm = nullptr;
- EGLDisplay m_eglDisplay;
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
};
}
diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp
index 5cd52f676..31adf100b 100644
--- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp
+++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp
@@ -50,7 +50,6 @@
#include <EGL/eglext_brcm.h>
-#include <wayland-client.h>
#include "wayland-brcm-client-protocol.h"
QT_BEGIN_NAMESPACE
diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h
index d27333576..d95ea9410 100644
--- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h
+++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h
@@ -71,7 +71,7 @@ public:
EGLContext eglContext() const { return m_context; }
private:
- EGLDisplay m_eglDisplay;
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
EGLContext m_context;
EGLConfig m_config;
diff --git a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h
index 2c65969f8..64201476d 100644
--- a/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h
+++ b/src/hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h
@@ -101,7 +101,7 @@ private:
PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture;
QWaylandDisplay *m_display = nullptr;
- EGLDisplay m_egl_display;
+ EGLDisplay m_egl_display = EGL_NO_DISPLAY;
bool m_egl_initialized = false;
};
diff --git a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h
index 0bfbe0ea4..588366aa7 100644
--- a/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h
+++ b/src/hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h
@@ -118,7 +118,7 @@ private:
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture;
PFNEGLHYBRISCREATEREMOTEBUFFERPROC m_egl_create_buffer;
QWaylandDisplay *m_display = nullptr;
- EGLDisplay m_egl_display;
+ EGLDisplay m_egl_display = EGL_NO_DISPLAY;
bool m_egl_initialized = false;
};
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp
index 4b3a635c7..3a34d2561 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp
@@ -42,7 +42,7 @@
#include "qwaylandeglwindow.h"
#include "qwaylandglcontext.h"
-#include <wayland-client.h>
+#include <wayland-client-core.h>
#include <QtCore/QDebug>
#include <private/qeglconvenience_p.h>
@@ -65,7 +65,7 @@ static const char *qwaylandegl_threadedgl_blacklist_vendor[] = {
QWaylandEglClientBufferIntegration::QWaylandEglClientBufferIntegration()
{
- qDebug() << "Using Wayland-EGL";
+ qCDebug(lcQpaWayland) << "Using Wayland-EGL";
}
@@ -87,7 +87,7 @@ void QWaylandEglClientBufferIntegration::initialize(QWaylandDisplay *display)
m_eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->wl_display(), nullptr);
} else {
- qWarning("The EGL implementation does not support the Wayland platform");
+ qCWarning(lcQpaWayland) << "The EGL implementation does not support the Wayland platform";
return;
}
} else {
@@ -102,13 +102,13 @@ void QWaylandEglClientBufferIntegration::initialize(QWaylandDisplay *display)
m_display = display;
if (m_eglDisplay == EGL_NO_DISPLAY) {
- qWarning("EGL not available");
+ qCWarning(lcQpaWayland) << "EGL not available";
return;
}
EGLint major,minor;
if (!eglInitialize(m_eglDisplay, &major, &minor)) {
- qWarning("failed to initialize EGL display");
+ qCWarning(lcQpaWayland) << "Failed to initialize EGL display" << hex << eglGetError();
m_eglDisplay = EGL_NO_DISPLAY;
return;
}
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h b/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h
index 233ce78bd..e9998b832 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h
@@ -41,7 +41,7 @@
#define QWAYLANDEGLINCLUDE_H
#include <string.h>
-#include <wayland-client.h>
+#include <wayland-client-core.h>
#include <wayland-egl.h>
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
index 88e893a74..9e6cb876c 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
@@ -62,7 +62,7 @@ public:
void ensureSize() override;
void updateSurface(bool create);
- virtual void setGeometry(const QRect &rect) override;
+ void setGeometry(const QRect &rect) override;
QRect contentsRect() const;
EGLSurface eglSurface() const;
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index 6b9776e9c..bc1f74af9 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -56,6 +56,7 @@
#include <QtGui/QSurfaceFormat>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLFunctions>
+#include <QOpenGLBuffer>
#include <QtCore/qmutex.h>
@@ -138,19 +139,10 @@ public:
qDebug() << "Shader Program link failed.";
qDebug() << m_blitProgram->log();
}
- }
- ~DecorationsBlitter()
- {
- delete m_blitProgram;
- }
- void blit(QWaylandEglWindow *window)
- {
- Q_ASSERT(window->wl_surface::isInitialized());
- QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
- QRect windowRect = window->window()->frameGeometry();
- int scale = window->scale() ;
- glViewport(0, 0, windowRect.width() * scale, windowRect.height() * scale);
+ m_blitProgram->bind();
+ m_blitProgram->enableAttributeArray(0);
+ m_blitProgram->enableAttributeArray(1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
@@ -159,9 +151,8 @@ public:
glDepthMask(GL_FALSE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- m_context->mUseNativeDefaultFbo = true;
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- m_context->mUseNativeDefaultFbo = false;
+ m_buffer.create();
+ m_buffer.bind();
static const GLfloat squareVertices[] = {
-1.f, -1.f,
@@ -169,14 +160,12 @@ public:
-1.f, 1.0f,
1.0f, 1.0f
};
-
static const GLfloat inverseSquareVertices[] = {
-1.f, 1.f,
1.f, 1.f,
-1.f, -1.f,
1.f, -1.f
};
-
static const GLfloat textureVertices[] = {
0.0f, 0.0f,
1.0f, 0.0f,
@@ -184,44 +173,57 @@ public:
1.0f, 1.0f,
};
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- m_blitProgram->bind();
+ m_squareVerticesOffset = 0;
+ m_inverseSquareVerticesOffset = sizeof(squareVertices);
+ m_textureVerticesOffset = sizeof(squareVertices) + sizeof(textureVertices);
- m_blitProgram->enableAttributeArray(0);
- m_blitProgram->enableAttributeArray(1);
- m_blitProgram->setAttributeArray(1, textureVertices, 2);
+ m_buffer.allocate(sizeof(squareVertices) + sizeof(inverseSquareVertices) + sizeof(textureVertices));
+ m_buffer.write(m_squareVerticesOffset, squareVertices, sizeof(squareVertices));
+ m_buffer.write(m_inverseSquareVerticesOffset, inverseSquareVertices, sizeof(inverseSquareVertices));
+ m_buffer.write(m_textureVerticesOffset, textureVertices, sizeof(textureVertices));
+
+ m_blitProgram->setAttributeBuffer(1, GL_FLOAT, m_textureVerticesOffset, 2);
+
+ m_textureWrap = m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+ }
+ ~DecorationsBlitter()
+ {
+ delete m_blitProgram;
+ }
+ void blit(QWaylandEglWindow *window)
+ {
+ Q_ASSERT(window->wl_surface::isInitialized());
+ QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
- glActiveTexture(GL_TEXTURE0);
+ QSize surfaceSize = window->surfaceSize();
+ int scale = window->scale() ;
+ glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale);
//Draw Decoration
- m_blitProgram->setAttributeArray(0, inverseSquareVertices, 2);
+ m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_inverseSquareVerticesOffset, 2);
QImage decorationImage = window->decoration()->contentImage();
cache->bindTexture(m_context->context(), decorationImage);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- if (m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- } else {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_textureWrap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_textureWrap);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//Draw Content
- m_blitProgram->setAttributeArray(0, squareVertices, 2);
+ m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_squareVerticesOffset, 2);
glBindTexture(GL_TEXTURE_2D, window->contentTexture());
QRect r = window->contentsRect();
glViewport(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- //Cleanup
- m_blitProgram->disableAttributeArray(0);
- m_blitProgram->disableAttributeArray(1);
}
QOpenGLShaderProgram *m_blitProgram = nullptr;
QWaylandGLContext *m_context = nullptr;
+ QOpenGLBuffer m_buffer;
+ int m_squareVerticesOffset;
+ int m_inverseSquareVerticesOffset;
+ int m_textureVerticesOffset;
+ int m_textureWrap;
};
@@ -308,6 +310,13 @@ QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *dis
return;
}
+ // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
+ // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
+ QVector<EGLint> eglDecorationsContextAttrs = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
+ m_decorationsContext = eglCreateContext(m_eglDisplay, m_config, m_context, eglDecorationsContextAttrs.constData());
+ if (m_decorationsContext == EGL_NO_CONTEXT)
+ qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
+
EGLint a = EGL_MIN_SWAP_INTERVAL;
EGLint b = EGL_MAX_SWAP_INTERVAL;
if (!eglGetConfigAttrib(m_eglDisplay, m_config, a, &a) ||
@@ -412,12 +421,7 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface)
if (window->isExposed())
window->setCanResize(false);
- // Core profiles mandate the use of VAOs when rendering. We would then need to use one
- // in DecorationsBlitter, but for that we would need a QOpenGLFunctions_3_2_Core instead
- // of the QOpenGLFunctions we use, but that would break when using a lower version context.
- // Instead of going crazy, just disable decorations for core profiles until we use
- // subsurfaces for them.
- if (m_format.profile() != QSurfaceFormat::CoreProfile && !window->decoration())
+ if (m_decorationsContext != EGL_NO_CONTEXT && !window->decoration())
window->createDecoration();
if (eglSurface == EGL_NO_SURFACE) {
@@ -444,102 +448,6 @@ void QWaylandGLContext::doneCurrent()
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
-#define STATE_GUARD_VERTEX_ATTRIB_COUNT 2
-
-class StateGuard
-{
-public:
- StateGuard() {
- QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
-
- glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &m_program);
- glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &m_activeTextureUnit);
- glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &m_texture);
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &m_fbo);
- glGetIntegerv(GL_VIEWPORT, m_viewport);
- glGetIntegerv(GL_DEPTH_WRITEMASK, &m_depthWriteMask);
- glGetIntegerv(GL_COLOR_WRITEMASK, m_colorWriteMask);
- m_blend = glIsEnabled(GL_BLEND);
- m_depth = glIsEnabled(GL_DEPTH_TEST);
- m_cull = glIsEnabled(GL_CULL_FACE);
- m_scissor = glIsEnabled(GL_SCISSOR_TEST);
- for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) {
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, (GLint *) &m_vertexAttribs[i].enabled);
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint *) &m_vertexAttribs[i].arrayBuffer);
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &m_vertexAttribs[i].size);
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &m_vertexAttribs[i].stride);
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *) &m_vertexAttribs[i].type);
- glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, (GLint *) &m_vertexAttribs[i].normalized);
- glFuncs.glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &m_vertexAttribs[i].pointer);
- }
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLint *) &m_minFilter);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLint *) &m_magFilter);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLint *) &m_wrapS);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLint *) &m_wrapT);
- }
-
- ~StateGuard() {
- QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
-
- glFuncs.glUseProgram(m_program);
- glActiveTexture(m_activeTextureUnit);
- glBindTexture(GL_TEXTURE_2D, m_texture);
- glFuncs.glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
- glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]);
- glDepthMask(m_depthWriteMask);
- glColorMask(m_colorWriteMask[0], m_colorWriteMask[1], m_colorWriteMask[2], m_colorWriteMask[3]);
- if (m_blend)
- glEnable(GL_BLEND);
- if (m_depth)
- glEnable(GL_DEPTH_TEST);
- if (m_cull)
- glEnable(GL_CULL_FACE);
- if (m_scissor)
- glEnable(GL_SCISSOR_TEST);
- for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) {
- if (m_vertexAttribs[i].enabled)
- glFuncs.glEnableVertexAttribArray(i);
- GLuint prevBuf;
- glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *) &prevBuf);
- glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_vertexAttribs[i].arrayBuffer);
- glFuncs.glVertexAttribPointer(i, m_vertexAttribs[i].size, m_vertexAttribs[i].type,
- m_vertexAttribs[i].normalized, m_vertexAttribs[i].stride,
- m_vertexAttribs[i].pointer);
- glFuncs.glBindBuffer(GL_ARRAY_BUFFER, prevBuf);
- }
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
- }
-
-private:
- GLuint m_program;
- GLenum m_activeTextureUnit;
- GLuint m_texture;
- GLuint m_fbo;
- GLint m_depthWriteMask;
- GLint m_colorWriteMask[4];
- GLboolean m_blend;
- GLboolean m_depth;
- GLboolean m_cull;
- GLboolean m_scissor;
- GLint m_viewport[4];
- struct VertexAttrib {
- bool enabled;
- GLuint arrayBuffer;
- GLint size;
- GLint stride;
- GLenum type;
- bool normalized;
- void *pointer = nullptr;
- } m_vertexAttribs[STATE_GUARD_VERTEX_ATTRIB_COUNT];
- GLenum m_minFilter;
- GLenum m_magFilter;
- GLenum m_wrapS;
- GLenum m_wrapT;
-};
-
void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
{
QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
@@ -547,15 +455,23 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
EGLSurface eglSurface = window->eglSurface();
if (window->decoration()) {
- makeCurrent(surface);
+ if (m_api != EGL_OPENGL_ES_API)
+ eglBindAPI(EGL_OPENGL_ES_API);
- // Must save & restore all state. Applications are usually not prepared
- // for random context state changes in a swapBuffers() call.
- StateGuard stateGuard;
+ // save the current EGL content and surface to set it again after the blitter is done
+ EGLDisplay currentDisplay = eglGetCurrentDisplay();
+ EGLContext currentContext = eglGetCurrentContext();
+ EGLSurface currentSurfaceDraw = eglGetCurrentSurface(EGL_DRAW);
+ EGLSurface currentSurfaceRead = eglGetCurrentSurface(EGL_READ);
+ eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_decorationsContext);
if (!m_blitter)
m_blitter = new DecorationsBlitter(this);
m_blitter->blit(window);
+
+ if (m_api != EGL_OPENGL_ES_API)
+ eglBindAPI(m_api);
+ eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext);
}
int swapInterval = mSupportNonBlockingSwap ? 0 : m_format.swapInterval();
@@ -573,9 +489,6 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
- if (mUseNativeDefaultFbo)
- return 0;
-
return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
}
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.h b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.h
index 9e876ac17..0e86ca4f5 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.h
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.h
@@ -83,18 +83,16 @@ public:
private:
void updateGLFormat();
- EGLDisplay m_eglDisplay;
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
QWaylandDisplay *m_display = nullptr;
EGLContext m_context;
EGLContext m_shareEGLContext;
+ EGLContext m_decorationsContext;
EGLConfig m_config;
QSurfaceFormat m_format;
DecorationsBlitter *m_blitter = nullptr;
- bool mUseNativeDefaultFbo = false;
uint m_api;
bool mSupportNonBlockingSwap = true;
-
- friend class DecorationsBlitter;
};
}
diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp
index aa5367e02..104a4df91 100644
--- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp
+++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp
@@ -118,7 +118,7 @@ const struct qt_xcomposite_listener QWaylandXCompositeEGLClientBufferIntegration
QWaylandXCompositeEGLClientBufferIntegration::rootInformation
};
-void QWaylandXCompositeEGLClientBufferIntegration::wlDisplayHandleGlobal(void *data, wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
+void QWaylandXCompositeEGLClientBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
{
Q_UNUSED(version);
if (interface == "qt_xcomposite") {
diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h
index ee55d6892..5fe21d7b7 100644
--- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h
+++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h
@@ -41,7 +41,7 @@
#define QWAYLANDXCOMPOSITEEGLCLIENTBUFFERINTEGRATION_H
#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h>
-#include <wayland-client.h>
+#include <wayland-client-core.h>
#include <QtCore/QTextStream>
#include <QtCore/QDataStream>
@@ -64,6 +64,7 @@
struct qt_xcomposite;
struct qt_xcomposite_listener;
+struct wl_registry;
QT_BEGIN_NAMESPACE
@@ -96,11 +97,11 @@ private:
struct qt_xcomposite *mWaylandComposite = nullptr;
Display *mDisplay = nullptr;
- EGLDisplay mEglDisplay;
- int mScreen;
- Window mRootWindow;
+ EGLDisplay mEglDisplay = EGL_NO_DISPLAY;
+ int mScreen = 0;
+ Window mRootWindow = -1;
- static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id,
+ static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id,
const QString &interface, uint32_t version);
static const struct ::qt_xcomposite_listener xcomposite_listener;
diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp
index 090cfb8a0..8be47fa2e 100644
--- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp
+++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp
@@ -109,7 +109,7 @@ const struct qt_xcomposite_listener QWaylandXCompositeGLXIntegration::xcomposite
QWaylandXCompositeGLXIntegration::rootInformation
};
-void QWaylandXCompositeGLXIntegration::wlDisplayHandleGlobal(void *data, wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
+void QWaylandXCompositeGLXIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
{
Q_UNUSED(version);
if (interface == "qt_xcomposite") {
diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h
index 26f2bad6f..809690816 100644
--- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h
+++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h
@@ -41,7 +41,7 @@
#define QWAYLANDXCOMPOSITEGLXINTEGRATION_H
#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h>
-#include <wayland-client.h>
+#include <wayland-client-core.h>
#include <QtCore/QTextStream>
#include <QtCore/QDataStream>
@@ -59,6 +59,7 @@
struct qt_xcomposite;
struct qt_xcomposite_listener;
+struct wl_registry;
QT_BEGIN_NAMESPACE
@@ -93,7 +94,7 @@ private:
int mScreen = 0;
Window mRootWindow = 0;
- static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id,
+ static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id,
const QString &interface, uint32_t version);
static const struct qt_xcomposite_listener xcomposite_listener;
diff --git a/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp b/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp
index 5f1818cdf..33d9a6038 100644
--- a/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp
+++ b/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp
@@ -39,7 +39,6 @@
#include "qwaylandxcompositebuffer.h"
-#include <wayland-client.h>
#include "wayland-xcomposite-client-protocol.h"
QT_BEGIN_NAMESPACE
diff --git a/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri b/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri
index b18aa2d50..d2b129d03 100644
--- a/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri
+++ b/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri
@@ -1,7 +1,7 @@
INCLUDEPATH += $$PWD
QMAKE_USE += xcomposite x11
-CONFIG += wayland-scanner
+CONFIG += wayland-scanner-client-wayland-protocol-include
WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/xcomposite.xml
HEADERS += \
diff --git a/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h b/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h
index 3028fbed5..83545de2e 100644
--- a/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h
+++ b/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h
@@ -41,6 +41,7 @@
#define BRCMBUFFER_H
#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
#include <QtCore/QSize>
#include <QtCore/QVector>
@@ -62,7 +63,7 @@ public:
QSize size() { return m_size; }
- static BrcmBuffer *fromResource(struct ::wl_resource *resource) { return static_cast<BrcmBuffer*>(Resource::fromResource(resource)->buffer_object); }
+ static BrcmBuffer *fromResource(struct ::wl_resource *resource) { return QtWayland::fromResource<BrcmBuffer *>(resource); }
protected:
void buffer_destroy_resource(Resource *resource) override;
diff --git a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h
index ff329c1a1..214a03fda 100644
--- a/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h
+++ b/src/hardwareintegration/compositor/drm-egl-server/drmeglserverbufferintegration.h
@@ -116,7 +116,7 @@ public:
inline void glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image);
private:
- EGLDisplay m_egl_display;
+ EGLDisplay m_egl_display = EGL_NO_DISPLAY;
PFNEGLCREATEDRMIMAGEMESAPROC m_egl_create_drm_image;
PFNEGLEXPORTDRMIMAGEMESAPROC m_egl_export_drm_image;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture_2d;
diff --git a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp
index 5a42c00dc..af9059603 100644
--- a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp
+++ b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp
@@ -42,7 +42,7 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLTexture>
#include <hybris/eglplatformcommon/hybris_nativebufferext.h>
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format)
diff --git a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h
index f4b297785..a5ca424b1 100644
--- a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h
+++ b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.h
@@ -121,7 +121,7 @@ public:
inline void eglHybrisSerializeNativeBuffer(EGLClientBuffer buffer, int *ints, int *fds);
private:
- EGLDisplay m_egl_display;
+ EGLDisplay m_egl_display = EGL_NO_DISPLAY;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture_2d;
PFNEGLHYBRISCREATENATIVEBUFFERPROC m_egl_create_buffer;
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri
new file mode 100644
index 000000000..a7630040e
--- /dev/null
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri
@@ -0,0 +1,16 @@
+INCLUDEPATH += $$PWD
+
+QMAKE_USE_PRIVATE += egl wayland-server
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += $$PWD/../../../3rdparty/protocol/linux-dmabuf-unstable-v1.xml
+
+QT += egl_support-private
+
+SOURCES += \
+ $$PWD/linuxdmabufclientbufferintegration.cpp \
+ $$PWD/linuxdmabuf.cpp
+
+HEADERS += \
+ $$PWD/linuxdmabufclientbufferintegration.h \
+ $$PWD/linuxdmabuf.h
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp
new file mode 100644
index 000000000..2ba0462c7
--- /dev/null
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "linuxdmabuf.h"
+#include "linuxdmabufclientbufferintegration.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <drm_fourcc.h>
+#include <drm_mode.h>
+#include <unistd.h>
+
+QT_BEGIN_NAMESPACE
+
+LinuxDmabuf::LinuxDmabuf(wl_display *display, LinuxDmabufClientBufferIntegration *clientBufferIntegration)
+ : zwp_linux_dmabuf_v1(display, 3 /*version*/)
+ , m_clientBufferIntegration(clientBufferIntegration)
+{
+}
+
+void LinuxDmabuf::setSupportedModifiers(const QHash<uint32_t, QVector<uint64_t>> &modifiers)
+{
+ Q_ASSERT(resourceMap().isEmpty());
+ m_modifiers = modifiers;
+}
+
+void LinuxDmabuf::zwp_linux_dmabuf_v1_bind_resource(Resource *resource)
+{
+ for (auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) {
+ auto format = it.key();
+ auto modifiers = it.value();
+ // send DRM_FORMAT_MOD_INVALID when no modifiers are supported for a format
+ if (modifiers.isEmpty())
+ modifiers << DRM_FORMAT_MOD_INVALID;
+ for (const auto &modifier : qAsConst(modifiers)) {
+ if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
+ const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
+ const uint32_t modifier_hi = modifier >> 32;
+ send_modifier(resource->handle, format, modifier_hi, modifier_lo);
+ } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
+ send_format(resource->handle, format);
+ }
+ }
+ }
+}
+
+void LinuxDmabuf::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
+{
+ wl_resource *r = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface,
+ wl_resource_get_version(resource->handle), params_id);
+ new LinuxDmabufParams(m_clientBufferIntegration, r); // deleted by the client, or when it disconnects
+}
+
+LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource)
+ : zwp_linux_buffer_params_v1(resource)
+ , m_clientBufferIntegration(clientBufferIntegration)
+{
+}
+
+LinuxDmabufParams::~LinuxDmabufParams()
+{
+ for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
+ if (it.value().fd != -1)
+ close(it.value().fd);
+ it.value().fd = -1;
+ }
+}
+
+bool LinuxDmabufParams::handleCreateParams(Resource *resource, int width, int height, uint format, uint flags)
+{
+ if (m_used) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "Params already used");
+ return false;
+ }
+
+ if (width <= 0 || height <= 0) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
+ "Invalid dimensions in create request");
+ return false;
+ }
+
+ if (m_planes.isEmpty()) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "Cannot create a buffer with no planes");
+ return false;
+ }
+
+ // check for holes in plane sequence
+ auto planeIds = m_planes.keys();
+ std::sort(planeIds.begin(), planeIds.end());
+ for (int i = 0; i < planeIds.count(); ++i) {
+ if (uint(i) != planeIds[i]) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "No dmabuf parameters provided for plane %i", i);
+ return false;
+ }
+ }
+
+ // check for overflows
+ for (auto it = m_planes.constBegin(); it != m_planes.constEnd(); ++it) {
+ const auto planeId = it.key();
+ const auto plane = it.value();
+ if (static_cast<int64_t>(plane.offset) + plane.stride > UINT32_MAX) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "Size overflow for plane %i",
+ planeId);
+ return false;
+ }
+ if (planeId == 0 && static_cast<int64_t>(plane.offset) + plane.stride * static_cast<int64_t>(height) > UINT32_MAX) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "Size overflow for plane %i",
+ planeId);
+ return false;
+ }
+
+ // do not report an error as it might be caused by the kernel not supporting seeking on dmabuf
+ off_t size = lseek(plane.fd, 0, SEEK_END);
+ if (size == -1) {
+ qCDebug(qLcWaylandCompositorHardwareIntegration) << "Seeking is not supported";
+ continue;
+ }
+
+ if (static_cast<int64_t>(plane.offset) >= size) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "Invalid offset %i for plane %i",
+ plane.offset, planeId);
+ return false;
+ }
+
+ if (static_cast<int64_t>(plane.offset) + static_cast<int64_t>(plane.stride) > size) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "Invalid stride %i for plane %i",
+ plane.stride, planeId);
+ return false;
+ }
+
+ // only valid for first plane as other planes might be sub-sampled
+ if (planeId == 0 && plane.offset + static_cast<int64_t>(plane.stride) * height > size) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "Invalid buffer stride or height for plane %i", planeId);
+ return false;
+ }
+ }
+
+ m_size = QSize(width, height);
+ m_drmFormat = format;
+ m_flags = flags;
+ m_used = true;
+
+ return true;
+}
+
+void LinuxDmabufParams::zwp_linux_buffer_params_v1_destroy(Resource *resource)
+{
+ wl_resource_destroy(resource->handle);
+}
+
+void LinuxDmabufParams::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+void LinuxDmabufParams::zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo)
+{
+ const uint64_t modifiers = (static_cast<uint64_t>(modifier_hi) << 32) | modifier_lo;
+ if (plane_idx >= LinuxDmabufWlBuffer::MaxDmabufPlanes) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+ "Plane index %i is out of bounds", plane_idx);
+ }
+
+ if (m_planes.contains(plane_idx)) {
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
+ "Plane already set");
+ }
+
+ Plane plane;
+ plane.fd = fd;
+ plane.modifiers = modifiers;
+ plane.offset = offset;
+ plane.stride = stride;
+ m_planes.insert(plane_idx, plane);
+}
+
+void LinuxDmabufParams::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
+{
+ if (!handleCreateParams(resource, width, height, format, flags))
+ return;
+
+ auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration);
+ buffer->m_size = m_size;
+ buffer->m_flags = m_flags;
+ buffer->m_drmFormat = m_drmFormat;
+ buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
+ for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
+ buffer->m_planes[it.key()] = it.value();
+ it.value().fd = -1; // ownership is moved
+ }
+
+ if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
+ send_failed(resource->handle);
+ } else {
+ send_created(resource->handle, buffer->resource()->handle);
+ }
+}
+
+void LinuxDmabufParams::zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags)
+{
+ if (!handleCreateParams(resource, width, height, format, flags))
+ return;
+
+ auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration, buffer_id);
+ buffer->m_size = m_size;
+ buffer->m_flags = m_flags;
+ buffer->m_drmFormat = m_drmFormat;
+ buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence
+ for (auto it = m_planes.begin(); it != m_planes.end(); ++it) {
+ buffer->m_planes[it.key()] = it.value();
+ it.value().fd = -1; // ownership is moved
+ }
+
+ if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) {
+ // for the 'create_immed' request, the implementation can decide
+ // how to handle the failure by an unknown cause; we decide
+ // to raise a fatal error at the client
+ wl_resource_post_error(resource->handle,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+ "Import of the provided DMA buffer failed");
+ }
+ // note: create signal shall not be sent for the 'create_immed' request
+}
+
+LinuxDmabufWlBuffer::LinuxDmabufWlBuffer(::wl_client *client, LinuxDmabufClientBufferIntegration *clientBufferIntegration, uint id)
+ : wl_buffer(client, id, 1 /*version*/)
+ , m_clientBufferIntegration(clientBufferIntegration)
+{
+}
+
+LinuxDmabufWlBuffer::~LinuxDmabufWlBuffer()
+{
+ m_clientBufferIntegration->removeBuffer(resource()->handle);
+ buffer_destroy(resource());
+}
+
+void LinuxDmabufWlBuffer::buffer_destroy(Resource *resource)
+{
+ Q_UNUSED(resource);
+ for (uint32_t i = 0; i < m_planesNumber; ++i) {
+ if (m_textures[i] != nullptr) {
+ m_clientBufferIntegration->deleteGLTextureWhenPossible(m_textures[i]);
+ m_textures[i] = nullptr;
+ }
+ if (m_eglImages[i] != EGL_NO_IMAGE_KHR) {
+ m_clientBufferIntegration->deleteImage(m_eglImages[i]);
+ m_eglImages[i] = EGL_NO_IMAGE_KHR;
+ }
+ if (m_planes[i].fd != -1)
+ close(m_planes[i].fd);
+ m_planes[i].fd = -1;
+ }
+ m_planesNumber = 0;
+}
+
+void LinuxDmabufWlBuffer::initImage(uint32_t plane, EGLImageKHR image)
+{
+ Q_ASSERT(plane < m_planesNumber);
+ Q_ASSERT(m_eglImages.at(plane) == EGL_NO_IMAGE_KHR);
+ m_eglImages[plane] = image;
+}
+
+void LinuxDmabufWlBuffer::initTexture(uint32_t plane, QOpenGLTexture *texture)
+{
+ Q_ASSERT(plane < m_planesNumber);
+ Q_ASSERT(m_textures.at(plane) == nullptr);
+ m_textures[plane] = texture;
+}
+
+void LinuxDmabufWlBuffer::buffer_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
new file mode 100644
index 000000000..2abc2ce6b
--- /dev/null
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LINUXDMABUF_H
+#define LINUXDMABUF_H
+
+#include "qwayland-server-linux-dmabuf-unstable-v1.h"
+
+#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QSize>
+#include <QtCore/QTextStream>
+#include <QtGui/QOpenGLTexture>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+// compatibility with libdrm <= 2.4.74
+#ifndef DRM_FORMAT_RESERVED
+#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1)
+#endif
+#ifndef DRM_FORMAT_MOD_VENDOR_NONE
+#define DRM_FORMAT_MOD_VENDOR_NONE 0
+#endif
+#ifndef DRM_FORMAT_MOD_LINEAR
+#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
+#endif
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandCompositor;
+class QWaylandResource;
+class LinuxDmabufParams;
+class LinuxDmabufClientBufferIntegration;
+
+struct Plane {
+ int fd = -1;
+ uint32_t offset = 0;
+ uint32_t stride = 0;
+ uint64_t modifiers = 0;
+};
+
+class LinuxDmabuf : public QtWaylandServer::zwp_linux_dmabuf_v1
+{
+public:
+ explicit LinuxDmabuf(wl_display *display, LinuxDmabufClientBufferIntegration *clientBufferIntegration);
+
+ void setSupportedModifiers(const QHash<uint32_t, QVector<uint64_t>> &modifiers);
+
+protected:
+ void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override;
+ void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override;
+
+private:
+ QHash<uint32_t, QVector<uint64_t>> m_modifiers; // key=DRM format, value=supported DRM modifiers for format
+ LinuxDmabufClientBufferIntegration *m_clientBufferIntegration;
+};
+
+class LinuxDmabufParams : public QtWaylandServer::zwp_linux_buffer_params_v1
+{
+public:
+ explicit LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource);
+ ~LinuxDmabufParams() override;
+
+private:
+ bool handleCreateParams(Resource *resource, int width, int height, uint format, uint flags);
+ uint m_drmFormat = 0;
+ uint m_flags = 0;
+ QSize m_size;
+ bool m_used = false;
+ QMap<uint, Plane> m_planes;
+ LinuxDmabufClientBufferIntegration *m_clientBufferIntegration;
+
+protected:
+ void zwp_linux_buffer_params_v1_destroy(Resource *resource) override;
+ void zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) override;
+ void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
+ void zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
+ void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override;
+
+ friend class LinuxDmabufClientBufferIntegrationPrivate;
+};
+
+class LinuxDmabufWlBuffer : public QtWaylandServer::wl_buffer
+{
+public:
+ explicit LinuxDmabufWlBuffer(::wl_client *client, LinuxDmabufClientBufferIntegration *clientBufferIntegration, uint id = 0);
+ ~LinuxDmabufWlBuffer() override;
+
+ void initImage(uint32_t plane, EGLImageKHR image);
+ void initTexture(uint32_t plane, QOpenGLTexture *texture);
+ inline QSize size() const { return m_size; }
+ inline uint32_t flags() const { return m_flags; }
+ inline uint32_t drmFormat() const { return m_drmFormat; }
+ inline Plane& plane(uint index) { return m_planes.at(index); }
+ inline uint32_t planesNumber() const { return m_planesNumber; }
+ inline EGLImageKHR image(uint32_t plane) { return m_eglImages.at(plane); }
+ inline QOpenGLTexture *texture(uint32_t plane) const { return m_textures.at(plane); }
+ void buffer_destroy_resource(Resource *resource) override;
+
+ static const uint32_t MaxDmabufPlanes = 4;
+
+private:
+ QSize m_size;
+ uint32_t m_flags = 0;
+ uint32_t m_drmFormat = EGL_TEXTURE_RGBA;
+ std::array<Plane, MaxDmabufPlanes> m_planes;
+ uint32_t m_planesNumber = 1;
+ LinuxDmabufClientBufferIntegration *m_clientBufferIntegration = nullptr;
+ std::array<EGLImageKHR, MaxDmabufPlanes> m_eglImages = { {EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR} };
+ std::array<QOpenGLTexture *, MaxDmabufPlanes> m_textures = { {nullptr, nullptr, nullptr, nullptr} };
+ void freeResources();
+ void buffer_destroy(Resource *resource) override;
+
+ friend class LinuxDmabufParams;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXDMABUF_H
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
new file mode 100644
index 000000000..a85f24542
--- /dev/null
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "linuxdmabufclientbufferintegration.h"
+#include "linuxdmabuf.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <unistd.h>
+#include <drm_fourcc.h>
+
+QT_BEGIN_NAMESPACE
+
+static QWaylandBufferRef::BufferFormatEgl formatFromDrmFormat(EGLint format) {
+ switch (format) {
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_XBGR1555:
+ case DRM_FORMAT_RGBX5551:
+ case DRM_FORMAT_BGRX5551:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ return QWaylandBufferRef::BufferFormatEgl_RGB;
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_RGBA5551:
+ case DRM_FORMAT_BGRA5551:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ return QWaylandBufferRef::BufferFormatEgl_RGBA;
+ case DRM_FORMAT_YUYV:
+ return QWaylandBufferRef::BufferFormatEgl_Y_XUXV;
+ default:
+ qCDebug(qLcWaylandCompositorHardwareIntegration) << "Buffer format" << hex << format << "not supported";
+ return QWaylandBufferRef::BufferFormatEgl_Null;
+ }
+}
+
+static QOpenGLTexture::TextureFormat openGLFormatFromBufferFormat(QWaylandBufferRef::BufferFormatEgl format) {
+ switch (format) {
+ case QWaylandBufferRef::BufferFormatEgl_RGB:
+ return QOpenGLTexture::RGBFormat;
+ case QWaylandBufferRef::BufferFormatEgl_RGBA:
+ return QOpenGLTexture::RGBAFormat;
+ default:
+ return QOpenGLTexture::NoFormat;
+ }
+}
+
+bool LinuxDmabufClientBufferIntegration::initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer)
+{
+ bool success = true;
+
+ // Resolving GL functions may need a context current, so do it only here.
+ if (!gl_egl_image_target_texture_2d)
+ gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+
+ if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
+ success = false;
+ }
+
+ for (uint32_t i = 0; i < dmabufBuffer->planesNumber(); ++i) {
+ QVarLengthArray<EGLint, 17> attribs;
+ switch (i) {
+ case 0:
+ attribs = {
+ EGL_WIDTH, dmabufBuffer->size().width(),
+ EGL_HEIGHT, dmabufBuffer->size().height(),
+ EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()),
+ EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(i).fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset),
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride),
+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff),
+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32),
+ EGL_NONE
+ };
+ break;
+ case 1:
+ attribs = {
+ EGL_WIDTH, dmabufBuffer->size().width(),
+ EGL_HEIGHT, dmabufBuffer->size().height(),
+ EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()),
+ EGL_DMA_BUF_PLANE1_FD_EXT, dmabufBuffer->plane(i).fd,
+ EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset),
+ EGL_DMA_BUF_PLANE1_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride),
+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff),
+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32),
+ EGL_NONE
+ };
+ break;
+ case 2:
+ attribs = {
+ EGL_WIDTH, dmabufBuffer->size().width(),
+ EGL_HEIGHT, dmabufBuffer->size().height(),
+ EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()),
+ EGL_DMA_BUF_PLANE2_FD_EXT, dmabufBuffer->plane(i).fd,
+ EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset),
+ EGL_DMA_BUF_PLANE2_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride),
+ EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff),
+ EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32),
+ EGL_NONE
+ };
+ break;
+ case 3:
+ attribs = {
+ EGL_WIDTH, dmabufBuffer->size().width(),
+ EGL_HEIGHT, dmabufBuffer->size().height(),
+ EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()),
+ EGL_DMA_BUF_PLANE3_FD_EXT, dmabufBuffer->plane(i).fd,
+ EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset),
+ EGL_DMA_BUF_PLANE3_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride),
+ EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff),
+ EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32),
+ EGL_NONE
+ };
+ break;
+ default:
+ return false;
+ }
+
+ // note: EGLImageKHR does NOT take ownership of the file descriptors
+ EGLImageKHR image = egl_create_image(m_eglDisplay,
+ EGL_NO_CONTEXT,
+ EGL_LINUX_DMA_BUF_EXT,
+ (EGLClientBuffer) nullptr,
+ attribs.constData());
+
+ if (image == EGL_NO_IMAGE_KHR) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i;
+ success = false;
+ }
+
+ dmabufBuffer->initImage(i, image);
+ }
+ return success;
+}
+
+bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer)
+{
+ bool success = true;
+
+ const YuvFormatConversion conversion = m_yuvFormats.value(dmabufBuffer->drmFormat());
+ if (conversion.inputPlanes != dmabufBuffer->planesNumber()) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer for this format must provide" << conversion.inputPlanes
+ << "planes but only" << dmabufBuffer->planesNumber() << "received";
+ return false;
+ }
+
+ // Resolving GL functions may need a context current, so do it only here.
+ if (!gl_egl_image_target_texture_2d)
+ gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+
+
+ if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported.";
+ success = false;
+ }
+
+ for (uint32_t i = 0; i < conversion.outputPlanes; ++i) {
+ const YuvPlaneConversion plane = conversion.plane[i];
+
+ QVarLengthArray<EGLint, 17> attribs = {
+ EGL_WIDTH, dmabufBuffer->size().width() / plane.widthDivisor,
+ EGL_HEIGHT, dmabufBuffer->size().height() / plane.heightDivisor,
+ EGL_LINUX_DRM_FOURCC_EXT, plane.format,
+ EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(plane.planeIndex).fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).offset),
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).stride),
+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers & 0xffffffff),
+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers >> 32),
+ EGL_NONE
+ };
+
+ // note: EGLImageKHR does NOT take ownership of the file descriptors
+ EGLImageKHR image = egl_create_image(m_eglDisplay,
+ EGL_NO_CONTEXT,
+ EGL_LINUX_DMA_BUF_EXT,
+ (EGLClientBuffer) nullptr,
+ attribs.constData());
+
+ if (image == EGL_NO_IMAGE_KHR) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i;
+ success = false;
+ }
+
+ dmabufBuffer->initImage(i, image);
+ }
+ return success;
+}
+
+LinuxDmabufClientBufferIntegration::LinuxDmabufClientBufferIntegration()
+{
+ m_yuvFormats.insert(DRM_FORMAT_YUYV,
+ YuvFormatConversion {
+ .inputPlanes = 1,
+ .outputPlanes = 2,
+ {{
+ .format = DRM_FORMAT_GR88,
+ .widthDivisor = 1,
+ .heightDivisor = 1,
+ .planeIndex = 0
+ }, {
+ .format = DRM_FORMAT_ARGB8888,
+ .widthDivisor = 2,
+ .heightDivisor = 1,
+ .planeIndex = 0
+ }}
+ });
+}
+
+LinuxDmabufClientBufferIntegration::~LinuxDmabufClientBufferIntegration()
+{
+ m_importedBuffers.clear();
+}
+
+void LinuxDmabufClientBufferIntegration::initializeHardware(struct ::wl_display *display)
+{
+ m_linuxDmabuf.reset(new LinuxDmabuf(display, this));
+
+ const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty() && qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").toInt() != 0;
+
+ // initialize hardware extensions
+ egl_query_dmabuf_modifiers_ext = reinterpret_cast<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(eglGetProcAddress("eglQueryDmaBufModifiersEXT"));
+ egl_query_dmabuf_formats_ext = reinterpret_cast<PFNEGLQUERYDMABUFFORMATSEXTPROC>(eglGetProcAddress("eglQueryDmaBufFormatsEXT"));
+ if (!egl_query_dmabuf_modifiers_ext || !egl_query_dmabuf_formats_ext) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglQueryDmaBufModifiersEXT and eglQueryDmaBufFormatsEXT.";
+ return;
+ }
+
+ egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
+ egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
+ if ((!egl_bind_wayland_display || !egl_unbind_wayland_display) && !ignoreBindDisplay) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.";
+ return;
+ }
+
+ egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+ egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+ if (!egl_create_image || !egl_destroy_image) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR.";
+ return;
+ }
+
+ // initialize EGL display
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ if (!nativeInterface) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. No native platform interface available.";
+ return;
+ }
+
+ m_eglDisplay = nativeInterface->nativeResourceForIntegration("EglDisplay");
+ if (!m_eglDisplay) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not get EglDisplay for window.";
+ return;
+ }
+
+ const char *extensionString = eglQueryString(m_eglDisplay, EGL_EXTENSIONS);
+ if (!extensionString || !strstr(extensionString, "EGL_EXT_image_dma_buf_import")) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. There is no EGL_EXT_image_dma_buf_import extension.";
+ return;
+ }
+ if (strstr(extensionString, "EGL_EXT_image_dma_buf_import_modifiers"))
+ m_supportsDmabufModifiers = true;
+
+ if (egl_bind_wayland_display && egl_unbind_wayland_display) {
+ m_displayBound = egl_bind_wayland_display(m_eglDisplay, display);
+ if (!m_displayBound) {
+ if (ignoreBindDisplay) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not bind Wayland display. Ignoring.";
+ } else {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not bind Wayland display.";
+ return;
+ }
+ }
+ }
+
+ // request and sent formats/modifiers only after egl_display is bound
+ QHash<uint32_t, QVector<uint64_t>> modifiers;
+ for (const auto &format : supportedDrmFormats()) {
+ modifiers[format] = supportedDrmModifiers(format);
+ }
+ m_linuxDmabuf->setSupportedModifiers(modifiers);
+}
+
+QVector<uint32_t> LinuxDmabufClientBufferIntegration::supportedDrmFormats()
+{
+ if (!egl_query_dmabuf_formats_ext)
+ return QVector<uint32_t>();
+
+ // request total number of formats
+ EGLint count = 0;
+ EGLBoolean success = egl_query_dmabuf_formats_ext(m_eglDisplay, 0, nullptr, &count);
+
+ if (success && count > 0) {
+ QVector<uint32_t> drmFormats(count);
+ if (egl_query_dmabuf_formats_ext(m_eglDisplay, count, (EGLint *) drmFormats.data(), &count))
+ return drmFormats;
+ }
+
+ return QVector<uint32_t>();
+}
+
+QVector<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32_t format)
+{
+ if (!egl_query_dmabuf_modifiers_ext)
+ return QVector<uint64_t>();
+
+ // request total number of formats
+ EGLint count = 0;
+ EGLBoolean success = egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, 0, nullptr, nullptr, &count);
+
+ if (success && count > 0) {
+ QVector<uint64_t> modifiers(count);
+ if (egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, count, modifiers.data(), nullptr, &count)) {
+ return modifiers;
+ }
+ }
+
+ return QVector<uint64_t>();
+}
+
+void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures()
+{
+ Q_ASSERT(QOpenGLContext::currentContext());
+ qDeleteAll(m_orphanedTextures);
+ m_orphanedTextures.clear();
+}
+
+void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image)
+{
+ egl_destroy_image(m_eglDisplay, image);
+}
+
+QtWayland::ClientBuffer *LinuxDmabufClientBufferIntegration::createBufferFor(wl_resource *resource)
+{
+ // fallback for shared memory buffers
+ if (wl_shm_buffer_get(resource))
+ return nullptr;
+
+ auto it = m_importedBuffers.find(resource);
+ if (it != m_importedBuffers.end()) {
+ m_importedBuffers.value(resource);
+ return new LinuxDmabufClientBuffer(this, it.value()->resource()->handle, m_importedBuffers.value(resource));
+ }
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not create client buffer for dmabuf buffer";
+ return nullptr;
+}
+
+bool LinuxDmabufClientBufferIntegration::importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer)
+{
+ if (m_importedBuffers.contains(resource)) {
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "buffer has already been added";
+ return false;
+ }
+ m_importedBuffers[resource] = linuxDmabufBuffer;
+ if (m_yuvFormats.contains(linuxDmabufBuffer->drmFormat()))
+ return initYuvTexture(linuxDmabufBuffer);
+ else
+ return initSimpleTexture(linuxDmabufBuffer);
+}
+
+void LinuxDmabufClientBufferIntegration::removeBuffer(wl_resource *resource)
+{
+ m_importedBuffers.remove(resource);
+}
+
+LinuxDmabufClientBuffer::LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration *integration,
+ wl_resource *bufferResource,
+ LinuxDmabufWlBuffer *dmabufBuffer)
+ : ClientBuffer(bufferResource)
+ , m_integration(integration)
+{
+ d = dmabufBuffer;
+}
+
+QOpenGLTexture *LinuxDmabufClientBuffer::toOpenGlTexture(int plane)
+{
+ // At this point we should have a valid OpenGL context, so it's safe to destroy textures
+ m_integration->deleteOrphanedTextures();
+
+ if (!m_buffer)
+ return nullptr;
+
+ QOpenGLTexture *texture = d->texture(plane);
+
+ const auto target = static_cast<QOpenGLTexture::Target>(GL_TEXTURE_2D);
+
+ if (!texture) {
+ texture = new QOpenGLTexture(target);
+ texture->setFormat(openGLFormatFromBufferFormat(formatFromDrmFormat(d->drmFormat())));
+ texture->setSize(d->size().width(), d->size().height());
+ texture->create();
+ d->initTexture(plane, texture);
+ }
+
+ if (m_textureDirty) {
+ texture->bind();
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ m_integration->gl_egl_image_target_texture_2d(target, d->image(plane));
+ }
+ return texture;
+}
+
+void LinuxDmabufClientBuffer::setDestroyed()
+{
+ m_integration->removeBuffer(m_buffer);
+ ClientBuffer::setDestroyed();
+}
+
+LinuxDmabufClientBuffer::~LinuxDmabufClientBuffer()
+{
+ // resources are deleted by buffer_destroy_resource
+ m_buffer = nullptr;
+ d = nullptr;
+}
+
+QWaylandBufferRef::BufferFormatEgl LinuxDmabufClientBuffer::bufferFormatEgl() const
+{
+ return formatFromDrmFormat(d->drmFormat());
+}
+
+QSize LinuxDmabufClientBuffer::size() const
+{
+ return d->size();
+}
+
+QWaylandSurface::Origin LinuxDmabufClientBuffer::origin() const
+{
+ return (d->flags() & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) ? QWaylandSurface::OriginBottomLeft : QWaylandSurface::OriginTopLeft;
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h
new file mode 100644
index 000000000..914c1e7a6
--- /dev/null
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LINUXDMABUFCLIENTBUFFERINTEGRATION_H
+#define LINUXDMABUFCLIENTBUFFERINTEGRATION_H
+
+#include "linuxdmabuf.h"
+
+#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
+#include <QtWaylandCompositor/private/qwlclientbuffer_p.h>
+#include <QtWaylandCompositor/private/qwayland-server-wayland.h>
+
+#include <drm_fourcc.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFFORMATSEXTPROC) (EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFMODIFIERSEXTPROC) (EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
+
+class LinuxDmabufClientBufferIntegrationPrivate;
+class LinuxDmabufParams;
+class LinuxDmabufClientBuffer;
+
+// buffer conversion definitions to import YUV buffers
+struct YuvPlaneConversion {
+ EGLint format = DRM_FORMAT_YUYV;
+ EGLint widthDivisor = 1;
+ EGLint heightDivisor = 1;
+ EGLint planeIndex = 0;
+};
+struct YuvFormatConversion {
+ uint32_t inputPlanes = 1;
+ uint32_t outputPlanes = 1;
+ struct YuvPlaneConversion plane[LinuxDmabufWlBuffer::MaxDmabufPlanes];
+};
+
+class LinuxDmabufClientBufferIntegration : public QtWayland::ClientBufferIntegration
+{
+public:
+ LinuxDmabufClientBufferIntegration();
+ ~LinuxDmabufClientBufferIntegration() override;
+
+ void initializeHardware(struct ::wl_display *display) override;
+ QtWayland::ClientBuffer *createBufferFor(wl_resource *resource) override;
+ bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer);
+ void removeBuffer(wl_resource *resource);
+ void deleteOrphanedTextures();
+ void deleteImage(EGLImageKHR image);
+ void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { m_orphanedTextures << texture; }
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr;
+
+private:
+ Q_DISABLE_COPY(LinuxDmabufClientBufferIntegration)
+
+ PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
+ PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
+ PFNEGLCREATEIMAGEKHRPROC egl_create_image = nullptr;
+ PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image = nullptr;
+ PFNEGLQUERYDMABUFMODIFIERSEXTPROC egl_query_dmabuf_modifiers_ext = nullptr;
+ PFNEGLQUERYDMABUFFORMATSEXTPROC egl_query_dmabuf_formats_ext = nullptr;
+
+ bool initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer);
+ bool initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer);
+ QVector<uint32_t> supportedDrmFormats();
+ QVector<uint64_t> supportedDrmModifiers(uint32_t format);
+
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
+ bool m_displayBound = false;
+ QVector<QOpenGLTexture *> m_orphanedTextures;
+ QHash<EGLint, YuvFormatConversion> m_yuvFormats;
+ bool m_supportsDmabufModifiers = false;
+ QHash<struct ::wl_resource *, LinuxDmabufWlBuffer *> m_importedBuffers;
+ QScopedPointer<LinuxDmabuf> m_linuxDmabuf;
+};
+
+class LinuxDmabufClientBuffer : public QtWayland::ClientBuffer
+{
+public:
+ ~LinuxDmabufClientBuffer() override;
+
+ QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const override;
+ QSize size() const override;
+ QWaylandSurface::Origin origin() const override;
+ QOpenGLTexture *toOpenGlTexture(int plane) override;
+
+protected:
+ void setDestroyed() override;
+
+private:
+ friend class LinuxDmabufClientBufferIntegration;
+ friend class LinuxDmabufClientBufferIntegrationPrivate;
+
+ LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration* integration, wl_resource *bufferResource, LinuxDmabufWlBuffer *dmabufBuffer);
+
+ LinuxDmabufWlBuffer *d = nullptr;
+ LinuxDmabufClientBufferIntegration *m_integration = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXDMABUFCLIENTBUFFERINTEGRATION_H
diff --git a/src/hardwareintegration/compositor/xcomposite-egl/xcompositeeglintegration.h b/src/hardwareintegration/compositor/xcomposite-egl/xcompositeeglintegration.h
index aa6c5b87a..514ae09cf 100644
--- a/src/hardwareintegration/compositor/xcomposite-egl/xcompositeeglintegration.h
+++ b/src/hardwareintegration/compositor/xcomposite-egl/xcompositeeglintegration.h
@@ -62,7 +62,7 @@ public:
private:
Display *mDisplay = nullptr;
- EGLDisplay mEglDisplay;
+ EGLDisplay mEglDisplay = EGL_NO_DISPLAY;
};
class XCompositeEglClientBuffer : public QtWayland::ClientBuffer
diff --git a/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h b/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h
index b3f1cf3a9..73a66d3af 100644
--- a/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h
+++ b/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h
@@ -41,6 +41,9 @@
#define XCOMPOSITEBUFFER_H
#include <qwayland-server-wayland.h>
+
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
+
#include <QtWaylandCompositor/QWaylandSurface>
#include <QtWaylandCompositor/QWaylandCompositor>
@@ -68,7 +71,7 @@ public:
QSize size() const { return mSize; }
- static XCompositeBuffer *fromResource(struct ::wl_resource *resource) { return static_cast<XCompositeBuffer*>(Resource::fromResource(resource)->buffer_object); }
+ static XCompositeBuffer *fromResource(struct ::wl_resource *resource) { return QtWayland::fromResource<XCompositeBuffer *>(resource); }
protected:
void buffer_destroy_resource(Resource *) override;
diff --git a/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h b/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h
index bb43ed1af..28168994c 100644
--- a/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h
+++ b/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h
@@ -45,7 +45,7 @@
#include "xlibinclude.h"
#include "qwayland-server-xcomposite.h"
-#include <wayland-server.h>
+#include <wayland-server-core.h>
QT_BEGIN_NAMESPACE
diff --git a/src/imports/compositor/compositor.pro b/src/imports/compositor/compositor.pro
index 50b26d4d0..f5c7567a0 100644
--- a/src/imports/compositor/compositor.pro
+++ b/src/imports/compositor/compositor.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qwaylandcompositorplugin
TARGETPATH = QtWayland/Compositor
-IMPORT_VERSION = 1.3
+IMPORT_VERSION = 1.$$QT_MINOR_VERSION
HEADERS += \
qwaylandmousetracker_p.h
@@ -43,4 +43,5 @@ CONFIG(debug, debug|release): QML_FILES += $$COMPOSITOR_QML_FILES
QT += quick-private qml-private gui-private core-private waylandcompositor waylandcompositor-private
+QMAKE_QMLPLUGINDUMP_FLAGS = -defaultplatform
load(qml_plugin)
diff --git a/src/imports/compositor/plugins.qmltypes b/src/imports/compositor/plugins.qmltypes
index a0ff2eb76..07a306e57 100644
--- a/src/imports/compositor/plugins.qmltypes
+++ b/src/imports/compositor/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtWayland.Compositor 1.3'
+// 'qmlplugindump -nonrelocatable -defaultplatform QtWayland.Compositor 1.13'
Module {
dependencies: ["QtQuick 2.0", "QtQuick.Window 2.11"]
@@ -280,8 +280,11 @@ Module {
name: "QWaylandQuickItem"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtWayland.Compositor/WaylandQuickItem 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtWayland.Compositor/WaylandQuickItem 1.0",
+ "QtWayland.Compositor/WaylandQuickItem 1.13"
+ ]
+ exportMetaObjectRevisions: [0, 13]
Property { name: "compositor"; type: "QWaylandCompositor"; isReadonly: true; isPointer: true }
Property { name: "surface"; type: "QWaylandSurface"; isPointer: true }
Property { name: "paintEnabled"; type: "bool" }
@@ -326,6 +329,12 @@ Module {
Parameter { name: "point"; type: "QPointF" }
}
Method {
+ name: "mapFromSurface"
+ revision: 13
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
name: "inputMethodQuery"
type: "QVariant"
Parameter { name: "query"; type: "Qt::InputMethodQuery" }
@@ -357,8 +366,11 @@ Module {
name: "QWaylandQuickSurface"
defaultProperty: "data"
prototype: "QWaylandSurface"
- exports: ["QtWayland.Compositor/WaylandSurface 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtWayland.Compositor/WaylandSurface 1.0",
+ "QtWayland.Compositor/WaylandSurface 1.13"
+ ]
+ exportMetaObjectRevisions: [0, 13]
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "useTextureAlpha"; type: "bool" }
Property { name: "clientRenderingEnabled"; type: "bool" }
@@ -474,6 +486,9 @@ Module {
}
}
Property { name: "client"; type: "QWaylandClient"; isReadonly: true; isPointer: true }
+ Property { name: "sourceGeometry"; revision: 13; type: "QRectF"; isReadonly: true }
+ Property { name: "destinationSize"; revision: 13; type: "QSize"; isReadonly: true }
+ Property { name: "bufferSize"; revision: 13; type: "QSize"; isReadonly: true }
Property { name: "size"; type: "QSize"; isReadonly: true }
Property { name: "bufferScale"; type: "int"; isReadonly: true }
Property { name: "contentOrientation"; type: "Qt::ScreenOrientation"; isReadonly: true }
@@ -493,6 +508,9 @@ Module {
name: "childAdded"
Parameter { name: "child"; type: "QWaylandSurface"; isPointer: true }
}
+ Signal { name: "sourceGeometryChanged"; revision: 13 }
+ Signal { name: "destinationSizeChanged"; revision: 13 }
+ Signal { name: "bufferSizeChanged"; revision: 13 }
Signal {
name: "offsetForNextFrame"
Parameter { name: "offset"; type: "QPoint" }
@@ -541,6 +559,15 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
}
+ Component { name: "QWaylandWlScaler"; prototype: "QWaylandCompositorExtension" }
+ Component {
+ name: "QWaylandWlScalerQuickExtension"
+ defaultProperty: "data"
+ prototype: "QWaylandWlScaler"
+ exports: ["QtWayland.Compositor/WlScaler 1.13"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ }
Component {
name: "QWaylandWlShell"
prototype: "QWaylandShell"
@@ -1262,6 +1289,12 @@ Module {
Parameter { name: "point"; type: "QPointF" }
}
Method {
+ name: "mapFromSurface"
+ revision: 13
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
name: "inputMethodQuery"
type: "QVariant"
Parameter { name: "query"; type: "Qt::InputMethodQuery" }
diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
index 1c576b6a1..c7553d932 100644
--- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp
+++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
@@ -59,6 +59,7 @@
#include <QtWaylandCompositor/QWaylandResource>
#include <QtWaylandCompositor/QWaylandQtWindowManager>
+#include <QtWaylandCompositor/QWaylandWlScaler>
#include <QtWaylandCompositor/QWaylandWlShell>
#include <QtWaylandCompositor/QWaylandTextInputManager>
#include <QtWaylandCompositor/QWaylandXdgShellV5>
@@ -76,6 +77,7 @@ QT_BEGIN_NAMESPACE
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CONTAINER_CLASS(QWaylandQuickCompositor)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandQtWindowManager)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandIviApplication)
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandWlScaler)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandWlShell)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV5)
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV6)
@@ -128,12 +130,18 @@ public:
static void defineModule(const char *uri)
{
+ // This is needed so to guarantee that the import is available with the current
+ // Qt minor version even if no new types have been added since the last release.
+ qmlRegisterModule(uri, 1, QT_VERSION_MINOR);
+
qmlRegisterType<QWaylandQuickCompositorQuickExtensionContainer>(uri, 1, 0, "WaylandCompositor");
qmlRegisterType<QWaylandQuickItem>(uri, 1, 0, "WaylandQuickItem");
+ qmlRegisterType<QWaylandQuickItem, 13>(uri, 1, 13, "WaylandQuickItem");
qmlRegisterType<QWaylandQuickHardwareLayer>(uri, 1, 2, "WaylandHardwareLayer");
qmlRegisterType<QWaylandMouseTracker>(uri, 1, 0, "WaylandMouseTracker");
qmlRegisterType<QWaylandQuickOutput>(uri, 1, 0, "WaylandOutput");
qmlRegisterType<QWaylandQuickSurface>(uri, 1, 0, "WaylandSurface");
+ qmlRegisterType<QWaylandQuickSurface, 13>(uri, 1, 13, "WaylandSurface");
qmlRegisterType<QWaylandKeymap>(uri, 1, 0, "WaylandKeymap");
qmlRegisterUncreatableType<QWaylandCompositorExtension>(uri, 1, 0, "WaylandExtension", QObject::tr("Cannot create instance of WaylandExtension"));
@@ -173,6 +181,8 @@ public:
qmlRegisterUncreatableType<QWaylandXdgPopup>(uri, 1, 3, "XdgPopup", QObject::tr("Cannot create instance of XdgShellPopup"));
qmlRegisterType<QWaylandXdgDecorationManagerV1QuickExtension>(uri, 1, 3, "XdgDecorationManagerV1");
+
+ qmlRegisterType<QWaylandWlScalerQuickExtension>(uri, 1, 13, "WlScaler");
}
};
//![class decl]
diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp
index 3fa723446..e8e35775e 100644
--- a/src/plugins/decorations/bradient/main.cpp
+++ b/src/plugins/decorations/bradient/main.cpp
@@ -109,19 +109,22 @@ QWaylandBradientDecoration::QWaylandBradientDecoration()
QRectF QWaylandBradientDecoration::closeButtonRect() const
{
- return QRectF(window()->frameGeometry().width() - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN,
+ const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ return QRectF(windowRight - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::maximizeButtonRect() const
{
- return QRectF(window()->frameGeometry().width() - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN,
+ const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ return QRectF(windowRight - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::minimizeButtonRect() const
{
- return QRectF(window()->frameGeometry().width() - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN,
+ const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ return QRectF(windowRight - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
@@ -133,13 +136,13 @@ QMargins QWaylandBradientDecoration::margins() const
void QWaylandBradientDecoration::paint(QPaintDevice *device)
{
bool active = window()->handle()->isActive();
- QRect surfaceRect(QPoint(), window()->frameGeometry().size());
+ QRect wg = waylandWindow()->windowContentGeometry();
QRect clips[] =
{
- QRect(0, 0, surfaceRect.width(), margins().top()),
- QRect(0, surfaceRect.height() - margins().bottom(), surfaceRect.width(), margins().bottom()),
- QRect(0, margins().top(), margins().left(), surfaceRect.height() - margins().top() - margins().bottom()),
- QRect(surfaceRect.width() - margins().right(), margins().top(), margins().left(), surfaceRect.height() - margins().top() - margins().bottom())
+ QRect(wg.left(), wg.top(), wg.width(), margins().top()),
+ QRect(wg.left(), (wg.bottom() + 1) - margins().bottom(), wg.width(), margins().bottom()),
+ QRect(wg.left(), margins().top(), margins().left(), wg.height() - margins().top() - margins().bottom()),
+ QRect((wg.right() + 1) - margins().right(), wg.top() + margins().top(), margins().right(), wg.height() - margins().top() - margins().bottom())
};
QRect top = clips[0];
@@ -149,7 +152,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
// Title bar
QPainterPath roundedRect;
- roundedRect.addRoundedRect(surfaceRect, 3, 3);
+ roundedRect.addRoundedRect(wg, 3, 3);
for (int i = 0; i < 4; ++i) {
p.save();
p.setClipRect(clips[i]);
@@ -264,13 +267,14 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c
Q_UNUSED(global);
// Figure out what area mouse is in
- if (local.y() <= margins().top()) {
+ QRect wg = waylandWindow()->windowContentGeometry();
+ if (local.y() <= wg.top() + margins().top()) {
processMouseTop(inputDevice,local,b,mods);
- } else if (local.y() > window()->height() + margins().top()) {
+ } else if (local.y() > wg.bottom() - margins().bottom()) {
processMouseBottom(inputDevice,local,b,mods);
- } else if (local.x() <= margins().left()) {
+ } else if (local.x() <= wg.left() + margins().left()) {
processMouseLeft(inputDevice,local,b,mods);
- } else if (local.x() > window()->width() + margins().left()) {
+ } else if (local.x() > wg.right() - margins().right()) {
processMouseRight(inputDevice,local,b,mods);
} else {
#if QT_CONFIG(cursor)
@@ -308,31 +312,34 @@ bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, c
void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
{
+ QRect wg = waylandWindow()->windowContentGeometry();
Q_UNUSED(mods);
- if (local.y() <= margins().bottom()) {
+ if (local.y() <= wg.top() + margins().bottom()) {
if (local.x() <= margins().left()) {
//top left bit
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
#endif
- startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP_LEFT,b);
- } else if (local.x() > window()->width() + margins().left()) {
+ startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b);
+ } else if (local.x() > wg.right() - margins().right()) {
//top right bit
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
#endif
- startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP_RIGHT,b);
+ startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b);
} else {
//top resize bit
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
#endif
- startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP,b);
+ startResize(inputDevice, Qt::TopEdge, b);
}
- } else if (local.x() <= margins().left()) {
+ } else if (local.x() <= wg.left() + margins().left()) {
processMouseLeft(inputDevice, local, b, mods);
- } else if (local.x() > window()->width() + margins().left()) {
+ } else if (local.x() > wg.right() - margins().right()) {
processMouseRight(inputDevice, local, b, mods);
+ } else if (isRightClicked(b)) {
+ showWindowMenu(inputDevice);
} else if (closeButtonRect().contains(local)) {
if (clickButton(b, Close))
QWindowSystemInterface::handleCloseEvent(window());
@@ -358,19 +365,19 @@ void QWaylandBradientDecoration::processMouseBottom(QWaylandInputDevice *inputDe
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
#endif
- startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT,b);
+ startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b);
} else if (local.x() > window()->width() + margins().left()) {
//bottom right bit
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
#endif
- startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT,b);
+ startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b);
} else {
//bottom bit
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
#endif
- startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_BOTTOM,b);
+ startResize(inputDevice, Qt::BottomEdge, b);
}
}
@@ -381,7 +388,7 @@ void QWaylandBradientDecoration::processMouseLeft(QWaylandInputDevice *inputDevi
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
#endif
- startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_LEFT,b);
+ startResize(inputDevice, Qt::LeftEdge, b);
}
void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
@@ -391,7 +398,7 @@ void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDev
#if QT_CONFIG(cursor)
waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
#endif
- startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_RIGHT,b);
+ startResize(inputDevice, Qt::RightEdge, b);
}
class QWaylandBradientDecorationPlugin : public QWaylandDecorationPlugin
diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro
index 66272e830..59ea91414 100644
--- a/src/plugins/hardwareintegration/compositor/compositor.pro
+++ b/src/plugins/hardwareintegration/compositor/compositor.pro
@@ -1,6 +1,8 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += waylandcompositor-private
+qtConfig(wayland-dmabuf-client-buffer): \
+ SUBDIRS += linux-dmabuf-unstable-v1
qtConfig(wayland-egl): \
SUBDIRS += wayland-egl
qtConfig(wayland-brcm): \
diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json
new file mode 100644
index 000000000..1fab86fef
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "linux-dmabuf-unstable-v1" ]
+}
diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro
new file mode 100644
index 000000000..bc4311423
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro
@@ -0,0 +1,12 @@
+QT = waylandcompositor waylandcompositor-private core-private gui-private
+
+OTHER_FILES += linux-dmabuf.json
+
+SOURCES += \
+ main.cpp \
+
+include(../../../../hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri)
+
+PLUGIN_TYPE = wayland-graphics-integration-server
+PLUGIN_CLASS_NAME = QWaylandDmabufClientBufferIntegrationPlugin
+load(qt_plugin)
diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp
new file mode 100644
index 000000000..d16268a2c
--- /dev/null
+++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWaylandCompositor/private/qwlclientbufferintegrationfactory_p.h>
+#include <QtWaylandCompositor/private/qwlclientbufferintegrationplugin_p.h>
+#include "linuxdmabufclientbufferintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandDmabufClientBufferIntegrationPlugin : public QtWayland::ClientBufferIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtWaylandClientBufferIntegrationFactoryInterface_iid FILE "linux-dmabuf-unstable-v1.json")
+public:
+ QtWayland::ClientBufferIntegration *create(const QString& key, const QStringList& paramList) override;
+};
+
+QtWayland::ClientBufferIntegration *QWaylandDmabufClientBufferIntegrationPlugin::create(const QString& key, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ Q_UNUSED(key);
+ return new LinuxDmabufClientBufferIntegration();
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/hardwareintegration/hardwareintegration.pro b/src/plugins/hardwareintegration/hardwareintegration.pro
index 59d867f9f..cb7a4b263 100644
--- a/src/plugins/hardwareintegration/hardwareintegration.pro
+++ b/src/plugins/hardwareintegration/hardwareintegration.pro
@@ -1,4 +1,4 @@
TEMPLATE=subdirs
SUBDIRS += client
-SUBDIRS += compositor
+qtHaveModule(waylandcompositor): SUBDIRS += compositor
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json
new file mode 100644
index 000000000..f32637bcc
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "fullscreen-shell-v1" ]
+}
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro
new file mode 100644
index 000000000..a522f87a8
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro
@@ -0,0 +1,23 @@
+QT += gui-private waylandclient-private
+CONFIG += wayland-scanner
+
+QMAKE_USE += wayland-client
+
+WAYLANDCLIENTSOURCES += \
+ ../../../3rdparty/protocol/fullscreen-shell-unstable-v1.xml
+
+HEADERS += \
+ qwaylandfullscreenshellv1integration.h \
+ qwaylandfullscreenshellv1surface.h
+
+SOURCES += \
+ main.cpp \
+ qwaylandfullscreenshellv1integration.cpp \
+ qwaylandfullscreenshellv1surface.cpp
+
+OTHER_FILES += \
+ fullscreen-shell-v1.json
+
+PLUGIN_TYPE = wayland-shell-integration
+PLUGIN_CLASS_NAME = QWaylandFullScreenShellV1IntegrationPlugin
+load(qt_plugin)
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp
new file mode 100644
index 000000000..dfcd997da
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWaylandClient/private/qwaylandshellintegrationplugin_p.h>
+
+#include "qwaylandfullscreenshellv1integration.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandFullScreenShellV1IntegrationPlugin : public QWaylandShellIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "fullscreen-shell-v1.json")
+public:
+ QWaylandShellIntegration *create(const QString &key, const QStringList &paramList) override;
+};
+
+QWaylandShellIntegration *QWaylandFullScreenShellV1IntegrationPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+
+ if (key == QLatin1String("fullscreen-shell-v1"))
+ return new QWaylandFullScreenShellV1Integration();
+ return nullptr;
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp
new file mode 100644
index 000000000..0cd2cb1e8
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandfullscreenshellv1integration.h"
+#include "qwaylandfullscreenshellv1surface.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+bool QWaylandFullScreenShellV1Integration::initialize(QWaylandDisplay *display)
+{
+ for (const QWaylandDisplay::RegistryGlobal &global : display->globals()) {
+ if (global.interface == QLatin1String("zwp_fullscreen_shell_v1") && !m_shell) {
+ m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(display->wl_registry(), global.id, global.version));
+ break;
+ }
+ }
+
+ if (!m_shell) {
+ qCDebug(lcQpaWayland) << "Couldn't find global zwp_fullscreen_shell_v1 for fullscreen-shell";
+ return false;
+ }
+
+ return QWaylandShellIntegration::initialize(display);
+}
+
+QWaylandShellSurface *QWaylandFullScreenShellV1Integration::createShellSurface(QWaylandWindow *window)
+{
+ return new QWaylandFullScreenShellV1Surface(m_shell.data(), window);
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h
new file mode 100644
index 000000000..131f9e720
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDFULLSCREENSHELLV1INTEGRATION_H
+#define QWAYLANDFULLSCREENSHELLV1INTEGRATION_H
+
+#include <wayland-client.h>
+#include <QtWaylandClient/private/qwayland-wayland.h>
+#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
+
+#include "qwayland-fullscreen-shell-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Integration : public QWaylandShellIntegration
+{
+public:
+ bool initialize(QWaylandDisplay *display) override;
+ QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
+
+private:
+ QScopedPointer<QtWayland::zwp_fullscreen_shell_v1> m_shell;
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDFULLSCREENSHELLV1INTEGRATION_H
diff --git a/src/shared/qwaylandxkb_p.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp
index 4820d94be..9a829f6e9 100644
--- a/src/shared/qwaylandxkb_p.h
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Jolla Ltd
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
@@ -38,36 +37,25 @@
**
****************************************************************************/
-#ifndef QWAYLANDXKB_H
-#define QWAYLANDXKB_H
+#include <QtWaylandClient/private/qwaylandscreen_p.h>
-#include <QtGui/private/qtguiglobal_p.h>
-#include <Qt>
-#include <QEvent>
-
-#if QT_CONFIG(xkbcommon)
-#include <xkbcommon/xkbcommon.h>
-#else
-typedef quint32 xkb_keysym_t;
-struct xkb_state;
-#endif
-
-#include <utility>
+#include "qwaylandfullscreenshellv1surface.h"
QT_BEGIN_NAMESPACE
-class QKeyEvent;
+namespace QtWaylandClient {
-class QWaylandXkb
+QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zwp_fullscreen_shell_v1 *shell, QWaylandWindow *window)
+ : QWaylandShellSurface(window)
+ , m_shell(shell)
+ , m_window(window)
{
-public:
- static std::pair<int, QString> keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers);
- static Qt::KeyboardModifiers modifiers(struct xkb_state *state);
+ auto screen = static_cast<QWaylandScreen *>(m_window->screen());
+ m_shell->present_surface(m_window->object(),
+ QtWayland::zwp_fullscreen_shell_v1::present_method_default,
+ screen->output());
+}
- static QEvent::Type toQtEventType(uint32_t state);
- static QVector<xkb_keysym_t> toKeysym(QKeyEvent *event);
-};
+} // namespace QtWaylandClient
QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h
new file mode 100644
index 000000000..718e1e861
--- /dev/null
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDFULLSCREENSHELLV1SURFACE_H
+#define QWAYLANDFULLSCREENSHELLV1SURFACE_H
+
+#include <QtWaylandClient/qtwaylandclientglobal.h>
+#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
+#include <QtWaylandClient/private/qwaylandwindow_p.h>
+
+#include "qwayland-fullscreen-shell-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Surface : public QWaylandShellSurface
+{
+public:
+ QWaylandFullScreenShellV1Surface(QtWayland::zwp_fullscreen_shell_v1 *shell, QWaylandWindow *window);
+
+private:
+ QtWayland::zwp_fullscreen_shell_v1 *m_shell = nullptr;
+ QWaylandWindow *m_window = nullptr;
+};
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDFULLSCREENSHELLV1SURFACE_H
diff --git a/src/plugins/shellintegration/shellintegration.pro b/src/plugins/shellintegration/shellintegration.pro
index 48b6efa36..39c57940a 100644
--- a/src/plugins/shellintegration/shellintegration.pro
+++ b/src/plugins/shellintegration/shellintegration.pro
@@ -1,9 +1,9 @@
TEMPLATE = subdirs
+QT_FOR_CONFIG += waylandclient-private
-SUBDIRS += \
- ivi-shell \
- xdg-shell \
- xdg-shell-v5 \
- xdg-shell-v6 \
- wl-shell \
-
+qtConfig(wayland-client-fullscreen-shell-v1): SUBDIRS += fullscreen-shell-v1
+qtConfig(wayland-client-ivi-shell): SUBDIRS += ivi-shell
+qtConfig(wayland-client-wl-shell): SUBDIRS += wl-shell
+qtConfig(wayland-client-xdg-shell): SUBDIRS += xdg-shell
+qtConfig(wayland-client-xdg-shell-v5): SUBDIRS += xdg-shell-v5
+qtConfig(wayland-client-xdg-shell-v6): SUBDIRS += xdg-shell-v6
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
index 80a7507d4..3d76cc310 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
@@ -51,7 +51,6 @@
// We mean it.
//
-#include <wayland-client.h>
#include <private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
index 88a63655c..4506c312a 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp
@@ -76,11 +76,10 @@ QWaylandWlShellSurface::~QWaylandWlShellSurface()
delete m_extendedWindow;
}
-void QWaylandWlShellSurface::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges)
+void QWaylandWlShellSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
{
- resize(inputDevice->wl_seat(),
- inputDevice->serial(),
- edges);
+ enum resize resizeEdges = convertToResizeEdges(edges);
+ resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges);
}
bool QWaylandWlShellSurface::move(QWaylandInputDevice *inputDevice)
@@ -193,6 +192,14 @@ void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states)
m_pending.states = states & ~Qt::WindowMinimized;
}
+enum QWaylandWlShellSurface::resize QWaylandWlShellSurface::convertToResizeEdges(Qt::Edges edges)
+{
+ return static_cast<enum resize>(
+ ((edges & Qt::TopEdge) ? resize_top : 0)
+ | ((edges & Qt::BottomEdge) ? resize_bottom : 0)
+ | ((edges & Qt::LeftEdge) ? resize_left : 0)
+ | ((edges & Qt::RightEdge) ? resize_right : 0));
+}
void QWaylandWlShellSurface::setTopLevel()
{
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h
index 57e06525a..324c10aac 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h
@@ -53,8 +53,6 @@
#include <QtCore/QSize>
-#include <wayland-client.h>
-
#include <QtWaylandClient/qtwaylandclientglobal.h>
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
@@ -78,7 +76,7 @@ public:
~QWaylandWlShellSurface() override;
using QtWayland::wl_shell_surface::resize;
- void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override;
+ void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override;
using QtWayland::wl_shell_surface::move;
bool move(QWaylandInputDevice *inputDevice) override;
@@ -99,6 +97,7 @@ protected:
void requestWindowStates(Qt::WindowStates states) override;
private:
+ static enum resize convertToResizeEdges(Qt::Edges edges);
void setTopLevel();
void updateTransientParent(QWindow *parent);
void setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial);
diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/.gitignore b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/.gitignore
new file mode 100644
index 000000000..6a2d3c207
--- /dev/null
+++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/.gitignore
@@ -0,0 +1,3 @@
+!qwayland-*.cpp
+!qwayland-*.h
+!wayland-*-protocol.c
diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp
index 0115eb1da..51979acf7 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp
@@ -25,6 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#include "qwayland-xdg-shell-unstable-v5_p.h"
+#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
QT_BEGIN_NAMESPACE
QT_WARNING_PUSH
diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h
index 3d8a6c13d..8fb1ea7b8 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h
@@ -31,6 +31,8 @@
#include <QByteArray>
#include <QString>
+struct wl_registry;
+
QT_BEGIN_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h
index 46644610b..8877e8830 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h
@@ -7,7 +7,7 @@
#include <stdint.h>
#include <stddef.h>
-#include "wayland-client.h"
+#include "wayland-client-core.h"
#ifdef __cplusplus
extern "C" {
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
index ff8e5639f..7494f6a67 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
@@ -53,8 +53,6 @@
#include "qwayland-xdg-shell-unstable-v5_p.h"
-#include <wayland-client.h>
-
#include <QtWaylandClient/qtwaylandclientglobal.h>
#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h
index 67e5d32a7..2b0a59f17 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h
@@ -56,8 +56,6 @@
#include <QtCore/QSize>
#include <QtCore/QVector>
-#include <wayland-client.h>
-
#include <QtWaylandClient/qtwaylandclientglobal.h>
#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
index 61a865cb2..e9f64e2e6 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp
@@ -73,18 +73,19 @@ QWaylandXdgSurfaceV5::~QWaylandXdgSurfaceV5()
delete m_extendedWindow;
}
-void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges)
+QtWayland::xdg_surface_v5::resize_edge QWaylandXdgSurfaceV5::convertToResizeEdges(Qt::Edges edges)
{
- // May need some conversion if types get incompatibles, ATM they're identical
- enum resize_edge const * const arg = reinterpret_cast<enum resize_edge const *>(&edges);
- resize(inputDevice, *arg);
+ return static_cast<enum resize_edge>(
+ ((edges & Qt::TopEdge) ? resize_edge_top : 0)
+ | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0)
+ | ((edges & Qt::LeftEdge) ? resize_edge_left : 0)
+ | ((edges & Qt::RightEdge) ? resize_edge_right : 0));
}
-void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum resize_edge edges)
+void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
{
- resize(inputDevice->wl_seat(),
- inputDevice->serial(),
- edges);
+ resize_edge resizeEdges = convertToResizeEdges(edges);
+ resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges);
}
bool QWaylandXdgSurfaceV5::move(QWaylandInputDevice *inputDevice)
@@ -94,6 +95,13 @@ bool QWaylandXdgSurfaceV5::move(QWaylandInputDevice *inputDevice)
return true;
}
+bool QWaylandXdgSurfaceV5::showWindowMenu(QWaylandInputDevice *seat)
+{
+ QPoint position = seat->pointerSurfacePosition().toPoint();
+ show_window_menu(seat->wl_seat(), seat->serial(), position.x(), position.y());
+ return true;
+}
+
void QWaylandXdgSurfaceV5::updateTransientParent(QWaylandWindow *parent)
{
if (!parent)
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h
index b938047ec..feebee7f4 100644
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h
@@ -59,8 +59,6 @@
#include <QtCore/QSize>
#include <QtCore/QMargins>
-#include <wayland-client.h>
-
QT_BEGIN_NAMESPACE
class QWindow;
@@ -81,12 +79,12 @@ public:
~QWaylandXdgSurfaceV5() override;
using QtWayland::xdg_surface_v5::resize;
- void resize(QWaylandInputDevice *inputDevice, enum resize_edge edges);
-
- void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override;
+ static resize_edge convertToResizeEdges(Qt::Edges edges);
+ void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override;
using QtWayland::xdg_surface_v5::move;
bool move(QWaylandInputDevice *inputDevice) override;
+ bool showWindowMenu(QWaylandInputDevice *seat) override;
void setTitle(const QString &title) override;
void setAppId(const QString &appId) override;
diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
index 2f729d971..980e4a601 100644
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
@@ -46,6 +46,8 @@
#include <QtWaylandClient/private/qwaylandscreen_p.h>
#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
+#include <QtGui/private/qwindow_p.h>
+
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -92,8 +94,8 @@ void QWaylandXdgSurfaceV6::Toplevel::applyConfigure()
m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size);
}
- QSize windowGeometrySize = m_xdgSurface->m_window->window()->frameGeometry().size();
- m_xdgSurface->set_window_geometry(0, 0, windowGeometrySize.width(), windowGeometrySize.height());
+ m_xdgSurface->setSizeHints();
+
m_applied = m_pending;
qCDebug(lcQpaWayland) << "Applied pending zxdg_toplevel_v6 configure event:" << m_applied.size << m_applied.states;
}
@@ -157,6 +159,15 @@ void QWaylandXdgSurfaceV6::Toplevel::requestWindowStates(Qt::WindowStates states
}
}
+QtWayland::zxdg_toplevel_v6::resize_edge QWaylandXdgSurfaceV6::Toplevel::convertToResizeEdges(Qt::Edges edges)
+{
+ return static_cast<enum resize_edge>(
+ ((edges & Qt::TopEdge) ? resize_edge_top : 0)
+ | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0)
+ | ((edges & Qt::LeftEdge) ? resize_edge_left : 0)
+ | ((edges & Qt::RightEdge) ? resize_edge_right : 0));
+}
+
QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parent,
QtWayland::zxdg_positioner_v6 *positioner)
: zxdg_popup_v6(xdgSurface->get_popup(parent->object(), positioner->object()))
@@ -226,23 +237,27 @@ QWaylandXdgSurfaceV6::~QWaylandXdgSurfaceV6()
destroy();
}
-void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, zxdg_toplevel_v6_resize_edge edges)
+void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
{
Q_ASSERT(m_toplevel && m_toplevel->isInitialized());
- m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), edges);
+ auto resizeEdges = Toplevel::convertToResizeEdges(edges);
+ m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges);
}
-void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges)
+bool QWaylandXdgSurfaceV6::move(QWaylandInputDevice *inputDevice)
{
- auto xdgEdges = reinterpret_cast<enum zxdg_toplevel_v6_resize_edge const *>(&edges);
- resize(inputDevice, *xdgEdges);
+ if (m_toplevel && m_toplevel->isInitialized()) {
+ m_toplevel->move(inputDevice->wl_seat(), inputDevice->serial());
+ return true;
+ }
+ return false;
}
-
-bool QWaylandXdgSurfaceV6::move(QWaylandInputDevice *inputDevice)
+bool QWaylandXdgSurfaceV6::showWindowMenu(QWaylandInputDevice *seat)
{
if (m_toplevel && m_toplevel->isInitialized()) {
- m_toplevel->move(inputDevice->wl_seat(), inputDevice->serial());
+ QPoint position = seat->pointerSurfacePosition().toPoint();
+ m_toplevel->show_window_menu(seat->wl_seat(), seat->serial(), position.x(), position.y());
return true;
}
return false;
@@ -292,6 +307,36 @@ bool QWaylandXdgSurfaceV6::wantsDecorations() const
return m_toplevel && !(m_toplevel->m_pending.states & Qt::WindowFullScreen);
}
+void QWaylandXdgSurfaceV6::propagateSizeHints()
+{
+ setSizeHints();
+
+ if (m_toplevel && m_window)
+ m_window->commit();
+}
+
+void QWaylandXdgSurfaceV6::setWindowGeometry(const QRect &rect)
+{
+ set_window_geometry(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void QWaylandXdgSurfaceV6::setSizeHints()
+{
+ if (m_toplevel && m_window) {
+ const int minWidth = qMax(0, m_window->windowMinimumSize().width());
+ const int minHeight = qMax(0, m_window->windowMinimumSize().height());
+ m_toplevel->set_min_size(minWidth, minHeight);
+
+ int maxWidth = qMax(0, m_window->windowMaximumSize().width());
+ if (maxWidth == QWINDOWSIZE_MAX)
+ maxWidth = 0;
+ int maxHeight = qMax(0, m_window->windowMaximumSize().height());
+ if (maxHeight == QWINDOWSIZE_MAX)
+ maxHeight = 0;
+ m_toplevel->set_max_size(maxWidth, maxHeight);
+ }
+}
+
void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states)
{
if (m_toplevel)
diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h
index 659aad047..f77a4d4ba 100644
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h
@@ -60,8 +60,6 @@
#include <QtCore/QSize>
#include <QtGui/QRegion>
-#include <wayland-client.h>
-
QT_BEGIN_NAMESPACE
class QWindow;
@@ -79,9 +77,9 @@ public:
QWaylandXdgSurfaceV6(QWaylandXdgShellV6 *shell, ::zxdg_surface_v6 *surface, QWaylandWindow *window);
~QWaylandXdgSurfaceV6() override;
- void resize(QWaylandInputDevice *inputDevice, enum zxdg_toplevel_v6_resize_edge edges);
- void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override;
+ void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override;
bool move(QWaylandInputDevice *inputDevice) override;
+ bool showWindowMenu(QWaylandInputDevice *seat) override;
void setTitle(const QString &title) override;
void setAppId(const QString &appId) override;
@@ -90,6 +88,10 @@ public:
bool handlesActiveState() const { return m_toplevel; }
void applyConfigure() override;
bool wantsDecorations() const override;
+ void propagateSizeHints() override;
+ void setWindowGeometry(const QRect &rect) override;
+
+ void setSizeHints();
protected:
void requestWindowStates(Qt::WindowStates states) override;
@@ -108,6 +110,9 @@ private:
void zxdg_toplevel_v6_close() override;
void requestWindowStates(Qt::WindowStates states);
+
+ static resize_edge convertToResizeEdges(Qt::Edges edges);
+
struct {
QSize size = {0, 0};
Qt::WindowStates states = Qt::WindowNoState;
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index d2452aa48..385651bbe 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
@@ -46,6 +46,8 @@
#include <QtWaylandClient/private/qwaylandscreen_p.h>
#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
+#include <QtGui/private/qwindow_p.h>
+
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@@ -102,8 +104,8 @@ void QWaylandXdgSurface::Toplevel::applyConfigure()
m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size);
}
- QSize windowGeometrySize = m_xdgSurface->m_window->window()->frameGeometry().size();
- m_xdgSurface->set_window_geometry(0, 0, windowGeometrySize.width(), windowGeometrySize.height());
+ m_xdgSurface->setSizeHints();
+
m_applied = m_pending;
qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states;
}
@@ -185,6 +187,15 @@ void QWaylandXdgSurface::Toplevel::requestWindowStates(Qt::WindowStates states)
}
}
+QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResizeEdges(Qt::Edges edges)
+{
+ return static_cast<enum resize_edge>(
+ ((edges & Qt::TopEdge) ? resize_edge_top : 0)
+ | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0)
+ | ((edges & Qt::LeftEdge) ? resize_edge_left : 0)
+ | ((edges & Qt::RightEdge) ? resize_edge_right : 0));
+}
+
QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent,
QtWayland::xdg_positioner *positioner)
: xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object()))
@@ -254,23 +265,27 @@ QWaylandXdgSurface::~QWaylandXdgSurface()
destroy();
}
-void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, xdg_toplevel_resize_edge edges)
+void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
{
Q_ASSERT(m_toplevel && m_toplevel->isInitialized());
- m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), edges);
+ auto resizeEdges = Toplevel::convertToResizeEdges(edges);
+ m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges);
}
-void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges)
+bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice)
{
- auto xdgEdges = reinterpret_cast<enum xdg_toplevel_resize_edge const *>(&edges);
- resize(inputDevice, *xdgEdges);
+ if (m_toplevel && m_toplevel->isInitialized()) {
+ m_toplevel->move(inputDevice->wl_seat(), inputDevice->serial());
+ return true;
+ }
+ return false;
}
-
-bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice)
+bool QWaylandXdgSurface::showWindowMenu(QWaylandInputDevice *seat)
{
if (m_toplevel && m_toplevel->isInitialized()) {
- m_toplevel->move(inputDevice->wl_seat(), inputDevice->serial());
+ QPoint position = seat->pointerSurfacePosition().toPoint();
+ m_toplevel->show_window_menu(seat->wl_seat(), seat->serial(), position.x(), position.y());
return true;
}
return false;
@@ -326,6 +341,36 @@ bool QWaylandXdgSurface::wantsDecorations() const
return m_toplevel && m_toplevel->wantsDecorations();
}
+void QWaylandXdgSurface::propagateSizeHints()
+{
+ setSizeHints();
+
+ if (m_toplevel && m_window)
+ m_window->commit();
+}
+
+void QWaylandXdgSurface::setWindowGeometry(const QRect &rect)
+{
+ set_window_geometry(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void QWaylandXdgSurface::setSizeHints()
+{
+ if (m_toplevel && m_window) {
+ const int minWidth = qMax(0, m_window->windowMinimumSize().width());
+ const int minHeight = qMax(0, m_window->windowMinimumSize().height());
+ m_toplevel->set_min_size(minWidth, minHeight);
+
+ int maxWidth = qMax(0, m_window->windowMaximumSize().width());
+ if (maxWidth == QWINDOWSIZE_MAX)
+ maxWidth = 0;
+ int maxHeight = qMax(0, m_window->windowMaximumSize().height());
+ if (maxHeight == QWINDOWSIZE_MAX)
+ maxHeight = 0;
+ m_toplevel->set_max_size(maxWidth, maxHeight);
+ }
+}
+
void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
{
if (m_toplevel)
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
index 741b83cfd..8f8682a47 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
@@ -62,8 +62,6 @@
#include <QtCore/QSize>
#include <QtGui/QRegion>
-#include <wayland-client.h>
-
QT_BEGIN_NAMESPACE
class QWindow;
@@ -82,9 +80,9 @@ public:
QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *surface, QWaylandWindow *window);
~QWaylandXdgSurface() override;
- void resize(QWaylandInputDevice *inputDevice, enum xdg_toplevel_resize_edge edges);
- void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override;
+ void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override;
bool move(QWaylandInputDevice *inputDevice) override;
+ bool showWindowMenu(QWaylandInputDevice *seat) override;
void setTitle(const QString &title) override;
void setAppId(const QString &appId) override;
void setWindowFlags(Qt::WindowFlags flags) override;
@@ -94,6 +92,10 @@ public:
bool handlesActiveState() const { return m_toplevel; }
void applyConfigure() override;
bool wantsDecorations() const override;
+ void propagateSizeHints() override;
+ void setWindowGeometry(const QRect &rect) override;
+
+ void setSizeHints();
protected:
void requestWindowStates(Qt::WindowStates states) override;
@@ -114,6 +116,9 @@ private:
void requestWindowFlags(Qt::WindowFlags flags);
void requestWindowStates(Qt::WindowStates states);
+
+ static resize_edge convertToResizeEdges(Qt::Edges edges);
+
struct {
QSize size = {0, 0};
Qt::WindowStates states = Qt::WindowNoState;
diff --git a/src/qtwaylandscanner/qtwaylandscanner.cpp b/src/qtwaylandscanner/qtwaylandscanner.cpp
index 6f4a33b5f..56045e880 100644
--- a/src/qtwaylandscanner/qtwaylandscanner.cpp
+++ b/src/qtwaylandscanner/qtwaylandscanner.cpp
@@ -441,7 +441,7 @@ bool Scanner::process()
printf("#ifndef %s\n", inclusionGuard.constData());
printf("#define %s\n", inclusionGuard.constData());
printf("\n");
- printf("#include \"wayland-server.h\"\n");
+ printf("#include \"wayland-server-core.h\"\n");
if (m_headerPath.isEmpty())
printf("#include \"wayland-%s-server-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData());
else
@@ -505,6 +505,7 @@ bool Scanner::process()
printf(" virtual ~Resource() {}\n");
printf("\n");
printf(" %s *%s_object;\n", interfaceName, interfaceNameStripped);
+ printf(" %s *object() { return %s_object; } \n", interfaceName, interfaceNameStripped);
printf(" struct ::wl_resource *handle;\n");
printf("\n");
printf(" struct ::wl_client *client() const { return wl_resource_get_client(handle); }\n");
@@ -769,6 +770,7 @@ bool Scanner::process()
printf(" void %s::destroy_func(struct ::wl_resource *client_resource)\n", interfaceName);
printf(" {\n");
printf(" Resource *resource = Resource::fromResource(client_resource);\n");
+ printf(" Q_ASSERT(resource);\n");
printf(" %s *that = resource->%s_object;\n", interfaceName, interfaceNameStripped);
printf(" that->m_resource_map.remove(resource->client(), resource);\n");
printf(" that->%s_destroy_resource(resource);\n", interfaceNameStripped);
@@ -939,6 +941,8 @@ bool Scanner::process()
printf("#include <QByteArray>\n");
printf("#include <QString>\n");
printf("\n");
+ printf("struct wl_registry;\n");
+ printf("\n");
printf("QT_BEGIN_NAMESPACE\n");
printf("QT_WARNING_PUSH\n");
printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n");
@@ -1056,6 +1060,22 @@ bool Scanner::process()
printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n");
printf("\n");
printf("namespace QtWayland {\n");
+ printf("\n");
+
+ // wl_registry_bind is part of the protocol, so we can't use that... instead we use core
+ // libwayland API to do the same thing a wayland-scanner generated wl_registry_bind would.
+ printf("static inline void *wlRegistryBind(struct ::wl_registry *registry, uint32_t name, const struct ::wl_interface *interface, uint32_t version)\n");
+ printf("{\n");
+ printf(" const uint32_t bindOpCode = 0;\n");
+ printf("#if (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR > 10) || WAYLAND_VERSION_MAJOR > 1\n");
+ printf(" return (void *) wl_proxy_marshal_constructor_versioned((struct wl_proxy *) registry,\n");
+ printf(" bindOpCode, interface, version, name, interface->name, version, nullptr);\n");
+ printf("#else\n");
+ printf(" return (void *) wl_proxy_marshal_constructor((struct wl_proxy *) registry,\n");
+ printf(" bindOpCode, interface, name, interface->name, version, nullptr);\n");
+ printf("#endif\n");
+ printf("}\n");
+ printf("\n");
for (int j = 0; j < interfaces.size(); ++j) {
const WaylandInterface &interface = interfaces.at(j);
@@ -1096,7 +1116,7 @@ bool Scanner::process()
printf(" void %s::init(struct ::wl_registry *registry, int id, int version)\n", interfaceName);
printf(" {\n");
- printf(" m_%s = static_cast<struct ::%s *>(wl_registry_bind(registry, id, &%s_interface, version));\n", interfaceName, interfaceName, interfaceName);
+ printf(" m_%s = static_cast<struct ::%s *>(wlRegistryBind(registry, id, &%s_interface, version));\n", interfaceName, interfaceName, interfaceName);
if (hasEvents)
printf(" init_listener();\n");
printf(" }\n");
diff --git a/src/shared/qwaylandxkb.cpp b/src/shared/qwaylandxkb.cpp
deleted file mode 100644
index 3cfc4b074..000000000
--- a/src/shared/qwaylandxkb.cpp
+++ /dev/null
@@ -1,395 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Jolla Ltd
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qwaylandxkb_p.h"
-
-#include <QKeyEvent>
-#include <QString>
-
-#if QT_CONFIG(xkbcommon)
-#include <xkbcommon/xkbcommon-keysyms.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(xkbcommon)
-static const uint32_t KeyTbl[] = {
- XKB_KEY_Escape, Qt::Key_Escape,
- XKB_KEY_Tab, Qt::Key_Tab,
- XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab,
- XKB_KEY_BackSpace, Qt::Key_Backspace,
- XKB_KEY_Return, Qt::Key_Return,
- XKB_KEY_Insert, Qt::Key_Insert,
- XKB_KEY_Delete, Qt::Key_Delete,
- XKB_KEY_Clear, Qt::Key_Delete,
- XKB_KEY_Pause, Qt::Key_Pause,
- XKB_KEY_Print, Qt::Key_Print,
-
- XKB_KEY_Home, Qt::Key_Home,
- XKB_KEY_End, Qt::Key_End,
- XKB_KEY_Left, Qt::Key_Left,
- XKB_KEY_Up, Qt::Key_Up,
- XKB_KEY_Right, Qt::Key_Right,
- XKB_KEY_Down, Qt::Key_Down,
- XKB_KEY_Prior, Qt::Key_PageUp,
- XKB_KEY_Next, Qt::Key_PageDown,
-
- XKB_KEY_Shift_L, Qt::Key_Shift,
- XKB_KEY_Shift_R, Qt::Key_Shift,
- XKB_KEY_Shift_Lock, Qt::Key_Shift,
- XKB_KEY_Control_L, Qt::Key_Control,
- XKB_KEY_Control_R, Qt::Key_Control,
- XKB_KEY_Meta_L, Qt::Key_Meta,
- XKB_KEY_Meta_R, Qt::Key_Meta,
- XKB_KEY_Alt_L, Qt::Key_Alt,
- XKB_KEY_Alt_R, Qt::Key_Alt,
- XKB_KEY_Caps_Lock, Qt::Key_CapsLock,
- XKB_KEY_Num_Lock, Qt::Key_NumLock,
- XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock,
- XKB_KEY_Super_L, Qt::Key_Super_L,
- XKB_KEY_Super_R, Qt::Key_Super_R,
- XKB_KEY_Menu, Qt::Key_Menu,
- XKB_KEY_Hyper_L, Qt::Key_Hyper_L,
- XKB_KEY_Hyper_R, Qt::Key_Hyper_R,
- XKB_KEY_Help, Qt::Key_Help,
-
- XKB_KEY_KP_Space, Qt::Key_Space,
- XKB_KEY_KP_Tab, Qt::Key_Tab,
- XKB_KEY_KP_Enter, Qt::Key_Enter,
- XKB_KEY_KP_Home, Qt::Key_Home,
- XKB_KEY_KP_Left, Qt::Key_Left,
- XKB_KEY_KP_Up, Qt::Key_Up,
- XKB_KEY_KP_Right, Qt::Key_Right,
- XKB_KEY_KP_Down, Qt::Key_Down,
- XKB_KEY_KP_Prior, Qt::Key_PageUp,
- XKB_KEY_KP_Next, Qt::Key_PageDown,
- XKB_KEY_KP_End, Qt::Key_End,
- XKB_KEY_KP_Begin, Qt::Key_Clear,
- XKB_KEY_KP_Insert, Qt::Key_Insert,
- XKB_KEY_KP_Delete, Qt::Key_Delete,
- XKB_KEY_KP_Equal, Qt::Key_Equal,
- XKB_KEY_KP_Multiply, Qt::Key_Asterisk,
- XKB_KEY_KP_Add, Qt::Key_Plus,
- XKB_KEY_KP_Separator, Qt::Key_Comma,
- XKB_KEY_KP_Subtract, Qt::Key_Minus,
- XKB_KEY_KP_Decimal, Qt::Key_Period,
- XKB_KEY_KP_Divide, Qt::Key_Slash,
-
- XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr,
- XKB_KEY_Multi_key, Qt::Key_Multi_key,
- XKB_KEY_Codeinput, Qt::Key_Codeinput,
- XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate,
- XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
- XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
-
- XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
- XKB_KEY_script_switch, Qt::Key_Mode_switch,
-
- XKB_KEY_XF86Back, Qt::Key_Back,
- XKB_KEY_XF86Forward, Qt::Key_Forward,
- XKB_KEY_XF86Stop, Qt::Key_Stop,
- XKB_KEY_XF86Refresh, Qt::Key_Refresh,
- XKB_KEY_XF86Favorites, Qt::Key_Favorites,
- XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia,
- XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl,
- XKB_KEY_XF86HomePage, Qt::Key_HomePage,
- XKB_KEY_XF86Search, Qt::Key_Search,
- XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown,
- XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute,
- XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
- XKB_KEY_XF86AudioPlay, Qt::Key_MediaTogglePlayPause,
- XKB_KEY_XF86AudioStop, Qt::Key_MediaStop,
- XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious,
- XKB_KEY_XF86AudioNext, Qt::Key_MediaNext,
- XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord,
- XKB_KEY_XF86AudioPause, Qt::Key_MediaPause,
- XKB_KEY_XF86Mail, Qt::Key_LaunchMail,
- XKB_KEY_XF86Calculator, Qt::Key_Calculator,
- XKB_KEY_XF86Memo, Qt::Key_Memo,
- XKB_KEY_XF86ToDoList, Qt::Key_ToDoList,
- XKB_KEY_XF86Calendar, Qt::Key_Calendar,
- XKB_KEY_XF86PowerDown, Qt::Key_PowerDown,
- XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust,
- XKB_KEY_XF86Standby, Qt::Key_Standby,
- XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp,
- XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown,
- XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff,
- XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp,
- XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown,
- XKB_KEY_XF86PowerOff, Qt::Key_PowerOff,
- XKB_KEY_XF86WakeUp, Qt::Key_WakeUp,
- XKB_KEY_XF86Eject, Qt::Key_Eject,
- XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver,
- XKB_KEY_XF86WWW, Qt::Key_WWW,
- XKB_KEY_XF86Sleep, Qt::Key_Sleep,
- XKB_KEY_XF86LightBulb, Qt::Key_LightBulb,
- XKB_KEY_XF86Shop, Qt::Key_Shop,
- XKB_KEY_XF86History, Qt::Key_History,
- XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite,
- XKB_KEY_XF86HotLinks, Qt::Key_HotLinks,
- XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust,
- XKB_KEY_XF86Finance, Qt::Key_Finance,
- XKB_KEY_XF86Community, Qt::Key_Community,
- XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind,
- XKB_KEY_XF86BackForward, Qt::Key_BackForward,
- XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft,
- XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight,
- XKB_KEY_XF86Book, Qt::Key_Book,
- XKB_KEY_XF86CD, Qt::Key_CD,
- XKB_KEY_XF86Calculater, Qt::Key_Calculator,
- XKB_KEY_XF86Clear, Qt::Key_Clear,
- XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab,
- XKB_KEY_XF86Close, Qt::Key_Close,
- XKB_KEY_XF86Copy, Qt::Key_Copy,
- XKB_KEY_XF86Cut, Qt::Key_Cut,
- XKB_KEY_XF86Display, Qt::Key_Display,
- XKB_KEY_XF86DOS, Qt::Key_DOS,
- XKB_KEY_XF86Documents, Qt::Key_Documents,
- XKB_KEY_XF86Excel, Qt::Key_Excel,
- XKB_KEY_XF86Explorer, Qt::Key_Explorer,
- XKB_KEY_XF86Game, Qt::Key_Game,
- XKB_KEY_XF86Go, Qt::Key_Go,
- XKB_KEY_XF86iTouch, Qt::Key_iTouch,
- XKB_KEY_XF86LogOff, Qt::Key_LogOff,
- XKB_KEY_XF86Market, Qt::Key_Market,
- XKB_KEY_XF86Meeting, Qt::Key_Meeting,
- XKB_KEY_XF86MenuKB, Qt::Key_MenuKB,
- XKB_KEY_XF86MenuPB, Qt::Key_MenuPB,
- XKB_KEY_XF86MySites, Qt::Key_MySites,
- XKB_KEY_XF86New, Qt::Key_New,
- XKB_KEY_XF86News, Qt::Key_News,
- XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome,
- XKB_KEY_XF86Open, Qt::Key_Open,
- XKB_KEY_XF86Option, Qt::Key_Option,
- XKB_KEY_XF86Paste, Qt::Key_Paste,
- XKB_KEY_XF86Phone, Qt::Key_Phone,
- XKB_KEY_XF86Reply, Qt::Key_Reply,
- XKB_KEY_XF86Reload, Qt::Key_Reload,
- XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows,
- XKB_KEY_XF86RotationPB, Qt::Key_RotationPB,
- XKB_KEY_XF86RotationKB, Qt::Key_RotationKB,
- XKB_KEY_XF86Save, Qt::Key_Save,
- XKB_KEY_XF86Send, Qt::Key_Send,
- XKB_KEY_XF86Spell, Qt::Key_Spell,
- XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen,
- XKB_KEY_XF86Support, Qt::Key_Support,
- XKB_KEY_XF86TaskPane, Qt::Key_TaskPane,
- XKB_KEY_XF86Terminal, Qt::Key_Terminal,
- XKB_KEY_XF86Tools, Qt::Key_Tools,
- XKB_KEY_XF86Travel, Qt::Key_Travel,
- XKB_KEY_XF86Video, Qt::Key_Video,
- XKB_KEY_XF86Word, Qt::Key_Word,
- XKB_KEY_XF86Xfer, Qt::Key_Xfer,
- XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn,
- XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut,
- XKB_KEY_XF86Away, Qt::Key_Away,
- XKB_KEY_XF86Messenger, Qt::Key_Messenger,
- XKB_KEY_XF86WebCam, Qt::Key_WebCam,
- XKB_KEY_XF86MailForward, Qt::Key_MailForward,
- XKB_KEY_XF86Pictures, Qt::Key_Pictures,
- XKB_KEY_XF86Music, Qt::Key_Music,
- XKB_KEY_XF86Battery, Qt::Key_Battery,
- XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
- XKB_KEY_XF86WLAN, Qt::Key_WLAN,
- XKB_KEY_XF86UWB, Qt::Key_UWB,
- XKB_KEY_XF86AudioForward, Qt::Key_AudioForward,
- XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat,
- XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay,
- XKB_KEY_XF86Subtitle, Qt::Key_Subtitle,
- XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack,
- XKB_KEY_XF86Time, Qt::Key_Time,
- XKB_KEY_XF86Select, Qt::Key_Select,
- XKB_KEY_XF86View, Qt::Key_View,
- XKB_KEY_XF86TopMenu, Qt::Key_TopMenu,
- XKB_KEY_XF86Red, Qt::Key_Red,
- XKB_KEY_XF86Green, Qt::Key_Green,
- XKB_KEY_XF86Yellow, Qt::Key_Yellow,
- XKB_KEY_XF86Blue, Qt::Key_Blue,
- XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
- XKB_KEY_XF86Suspend, Qt::Key_Suspend,
- XKB_KEY_XF86Hibernate, Qt::Key_Hibernate,
- XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle,
- XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn,
- XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff,
- XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute,
- XKB_KEY_XF86Launch0, Qt::Key_Launch0,
- XKB_KEY_XF86Launch1, Qt::Key_Launch1,
- XKB_KEY_XF86Launch2, Qt::Key_Launch2,
- XKB_KEY_XF86Launch3, Qt::Key_Launch3,
- XKB_KEY_XF86Launch4, Qt::Key_Launch4,
- XKB_KEY_XF86Launch5, Qt::Key_Launch5,
- XKB_KEY_XF86Launch6, Qt::Key_Launch6,
- XKB_KEY_XF86Launch7, Qt::Key_Launch7,
- XKB_KEY_XF86Launch8, Qt::Key_Launch8,
- XKB_KEY_XF86Launch9, Qt::Key_Launch9,
- XKB_KEY_XF86LaunchA, Qt::Key_LaunchA,
- XKB_KEY_XF86LaunchB, Qt::Key_LaunchB,
- XKB_KEY_XF86LaunchC, Qt::Key_LaunchC,
- XKB_KEY_XF86LaunchD, Qt::Key_LaunchD,
- XKB_KEY_XF86LaunchE, Qt::Key_LaunchE,
- XKB_KEY_XF86LaunchF, Qt::Key_LaunchF,
-
- 0, 0
-};
-
-static int lookupKeysym(xkb_keysym_t key)
-{
- int code = 0;
- int i = 0;
- while (KeyTbl[i]) {
- if (key == KeyTbl[i]) {
- code = (int)KeyTbl[i+1];
- break;
- }
- i += 2;
- }
-
- return code;
-}
-
-static xkb_keysym_t toKeysymFromTable(uint32_t key)
-{
- for (int i = 0; KeyTbl[i]; i += 2) {
- if (key == KeyTbl[i + 1])
- return KeyTbl[i];
- }
-
- return 0;
-}
-#endif
-
-std::pair<int, QString> QWaylandXkb::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers)
-{
-#if QT_CONFIG(xkbcommon)
- QString text;
- uint utf32 = xkb_keysym_to_utf32(keysym);
- if (utf32)
- text = QString::fromUcs4(&utf32, 1);
-
- int code = 0;
-
- if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
- code = Qt::Key_F1 + (int(keysym) - XKB_KEY_F1);
- } else if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) {
- if (keysym >= XKB_KEY_KP_0) {
- // numeric keypad keys
- code = Qt::Key_0 + ((int)keysym - XKB_KEY_KP_0);
- } else {
- code = lookupKeysym(keysym);
- }
- modifiers |= Qt::KeypadModifier;
- } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
- && text.unicode()->unicode() != 0x7f
- && !(keysym >= XKB_KEY_dead_grave && keysym <= XKB_KEY_dead_currency)) {
- code = text.unicode()->toUpper().unicode();
- } else {
- // any other keys
- code = lookupKeysym(keysym);
- }
-
- // Map control + letter to proper text
- if (utf32 >= 'A' && utf32 <= '~' && (modifiers & Qt::ControlModifier)) {
- utf32 &= ~0x60;
- text = QString::fromUcs4(&utf32, 1);
- }
-
- return { code, text };
-#else
- Q_UNUSED(modifiers)
- return { keysym, "" };
-#endif
-}
-
-Qt::KeyboardModifiers QWaylandXkb::modifiers(struct xkb_state *state)
-{
-#if QT_CONFIG(xkbcommon)
- Qt::KeyboardModifiers modifiers = Qt::NoModifier;
-
- xkb_state_component cstate = static_cast<xkb_state_component>(XKB_STATE_DEPRESSED | XKB_STATE_LATCHED | XKB_STATE_LOCKED);
-
- if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, cstate))
- modifiers |= Qt::ShiftModifier;
- if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, cstate))
- modifiers |= Qt::ControlModifier;
- if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, cstate))
- modifiers |= Qt::AltModifier;
- if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, cstate))
- modifiers |= Qt::MetaModifier;
-
- return modifiers;
-#else
- Q_UNUSED(state)
- return Qt::NoModifier;
-#endif
-}
-
-QEvent::Type QWaylandXkb::toQtEventType(uint32_t state)
-{
- return state != 0 ? QEvent::KeyPress : QEvent::KeyRelease;
-}
-
-QVector<xkb_keysym_t> QWaylandXkb::toKeysym(QKeyEvent *event)
-{
-#if QT_CONFIG(xkbcommon)
- QVector<xkb_keysym_t> keysyms;
- if (event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) {
- keysyms.append(XKB_KEY_F1 + (event->key() - Qt::Key_F1));
- } else if (event->modifiers() & Qt::KeypadModifier) {
- if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9)
- keysyms.append(XKB_KEY_KP_0 + (event->key() - Qt::Key_0));
- else
- keysyms.append(toKeysymFromTable(event->key()));
- } else if (!event->text().isEmpty()) {
- // From libxkbcommon keysym-utf.c:
- // "We allow to represent any UCS character in the range U-00000000 to
- // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff."
- foreach (uint utf32, event->text().toUcs4()) {
- keysyms.append(utf32 | 0x01000000);
- }
- } else {
- keysyms.append(toKeysymFromTable(event->key()));
- }
- return keysyms;
-#else
- return QVector<xkb_keysym_t>() << event->nativeScanCode();
-#endif
-}
-
-QT_END_NAMESPACE
diff --git a/src/src.pro b/src/src.pro
index 4ecbc71b9..031ff885c 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -13,6 +13,14 @@ qtConfig(wayland-client) {
sub_client.target = sub-client
SUBDIRS += sub_client
+ sub_plugins.subdir = plugins
+ sub_plugins.depends += sub-qtwaylandscanner sub-client
+ qtConfig(wayland-server) {
+ sub_plugins.depends += sub-compositor
+ }
+ sub_plugins.target = sub-plugins
+ SUBDIRS += sub_plugins
+
qtConfig(wayland-server) {
sub_compositor.subdir = compositor
sub_compositor.depends = sub-qtwaylandscanner
@@ -23,11 +31,5 @@ qtConfig(wayland-client) {
sub_imports.depends += sub-compositor
sub_imports.target = sub-imports
SUBDIRS += sub_imports
-
- sub_plugins.subdir = plugins
- sub_plugins.depends = sub-qtwaylandscanner sub-client sub-compositor
- sub_plugins.target = sub-plugins
- SUBDIRS += sub_plugins
}
}
-
diff --git a/sync.profile b/sync.profile
index 756674cda..a06caa377 100644
--- a/sync.profile
+++ b/sync.profile
@@ -58,9 +58,11 @@
"^qwayland-server-ivi-application.h",
"^qwayland-server-qt-windowmanager.h",
"^qwayland-server-qt-key-unstable-v1.h",
+ "^qwayland-server-scaler.h",
"^qwayland-server-server-buffer-extension.h",
"^qwayland-server-text-input-unstable-v2.h",
"^qwayland-server-touch-extension.h",
+ "^qwayland-server-viewporter.h",
"^qwayland-server-xdg-decoration-unstable-v1.h",
"^qwayland-server-xdg-shell-unstable-v5.h",
"^qwayland-server-xdg-shell-unstable-v6.h",
@@ -69,8 +71,10 @@
"^wayland-ivi-application-server-protocol.h",
"^wayland-qt-windowmanager-server-protocol.h",
"^wayland-qt-key-unstable-v1-server-protocol.h",
+ "^wayland-scaler-server-protocol.h",
"^wayland-server-buffer-extension-server-protocol.h",
"^wayland-text-input-unstable-v2-server-protocol.h",
+ "^wayland-viewporter-server-protocol.h",
"^wayland-touch-extension-server-protocol.h",
"^wayland-wayland-server-protocol.h",
"^wayland-xdg-decoration-unstable-v1-server-protocol.h",
@@ -78,5 +82,9 @@
"^wayland-xdg-shell-unstable-v5-server-protocol.h",
"^wayland-xdg-shell-unstable-v6-server-protocol.h",
],
+ "$basedir/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1" => [
+ "^qwayland-server-linux-dmabuf-unstable-v1.h",
+ "^wayland-linux-dmabuf-unstable-v1-server-protocol.h",
+ ],
);
@private_headers = ( qr/^qwayland-.*\.h/, qr/^wayland-.*-protocol\.h/ );
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 79ad29bd5..4ae3b4f65 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -2,6 +2,10 @@ TEMPLATE=subdirs
QT_FOR_CONFIG += waylandclient-private
qtConfig(wayland-client): \
- SUBDIRS += client cmake
+ SUBDIRS += client
+
+qtConfig(wayland-client):qtHaveModule(waylandcompositor): \
+ SUBDIRS += cmake
+
qtHaveModule(waylandcompositor): \
SUBDIRS += compositor
diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro
index 9fd8fc3f9..051cb4e3d 100644
--- a/tests/auto/client/client.pro
+++ b/tests/auto/client/client.pro
@@ -2,6 +2,15 @@ TEMPLATE=subdirs
SUBDIRS += \
client \
+ datadevicev1 \
+ fullscreenshellv1 \
iviapplication \
- xdgshellv6 \
- wl_connect
+ output \
+ seatv4 \
+ surface \
+ wl_connect \
+ xdgoutput \
+ xdgshell \
+ xdgshellv6
+
+qtConfig(im): SUBDIRS += inputcontext
diff --git a/tests/auto/client/client/client.pro b/tests/auto/client/client/client.pro
index f4ced252c..7c3a934d0 100644
--- a/tests/auto/client/client/client.pro
+++ b/tests/auto/client/client/client.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client
SOURCES += tst_client.cpp
diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp
index 470db25a4..08120c8c2 100644
--- a/tests/auto/client/client/tst_client.cpp
+++ b/tests/auto/client/client/tst_client.cpp
@@ -167,11 +167,6 @@ public slots:
}
private slots:
- void primaryScreen();
- void screens();
- void addScreenWithGeometryChange();
- void windowScreens();
- void removePrimaryScreen();
void createDestroyWindow();
void activeWindowFollowsKeyboardFocus();
void events();
@@ -188,117 +183,6 @@ private:
MockCompositor *compositor = nullptr;
};
-void tst_WaylandClient::primaryScreen()
-{
- compositor->setOutputMode(screenSize);
- QTRY_COMPARE(QGuiApplication::primaryScreen()->size(), screenSize);
-}
-
-void tst_WaylandClient::screens()
-{
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
- compositor->sendAddOutput();
- QTRY_COMPARE(QGuiApplication::screens().size(), 2);
- QSharedPointer<MockOutput> secondOutput;
- QTRY_VERIFY(secondOutput = compositor->output(1));
- compositor->sendRemoveOutput(secondOutput);
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
-}
-
-//QTBUG-62044
-void tst_WaylandClient::addScreenWithGeometryChange()
-{
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
- const QRect oldGeometry = QGuiApplication::primaryScreen()->geometry();
- compositor->sendAddOutput();
-
- // Move the primary screen to the right
- const QRect newGeometry(QPoint(screenSize.width(), 0), screenSize);
- Q_ASSERT(oldGeometry != newGeometry);
- compositor->sendOutputGeometry(compositor->output(0), newGeometry);
-
- QTRY_COMPARE(QGuiApplication::screens().size(), 2);
- QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), newGeometry);
-
- compositor->sendRemoveOutput(compositor->output(1));
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
-
- // Move the screen back
- compositor->sendOutputGeometry(compositor->output(0), oldGeometry);
- QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), oldGeometry);
-}
-
-void tst_WaylandClient::windowScreens()
-{
- QSharedPointer<MockOutput> firstOutput;
- QTRY_VERIFY(firstOutput = compositor->output());
-
- TestWindow window;
- window.show();
-
- QSharedPointer<MockSurface> surface;
- QTRY_VERIFY(surface = compositor->surface());
- compositor->sendShellSurfaceConfigure(surface);
-
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
- QScreen *primaryScreen = QGuiApplication::screens().first();
- QCOMPARE(window.screen(), primaryScreen);
-
- compositor->sendAddOutput();
-
- QTRY_COMPARE(QGuiApplication::screens().size(), 2);
- QScreen *secondaryScreen = QGuiApplication::screens().at(1);
- QVERIFY(secondaryScreen);
-
- window.setScreen(secondaryScreen);
- QCOMPARE(window.screen(), secondaryScreen);
-
- QSharedPointer<MockOutput> secondOutput;
- QTRY_VERIFY(secondOutput = compositor->output(1));
- compositor->sendSurfaceEnter(surface, firstOutput);
-
- compositor->sendSurfaceEnter(surface, secondOutput);
- QTRY_COMPARE(window.screen(), primaryScreen);
-
- compositor->sendSurfaceLeave(surface, firstOutput);
- QTRY_COMPARE(window.screen(), secondaryScreen);
-
- compositor->sendRemoveOutput(secondOutput);
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
- QCOMPARE(window.screen(), primaryScreen);
-}
-
-void tst_WaylandClient::removePrimaryScreen()
-{
- QSharedPointer<MockOutput> firstOutput;
- QTRY_VERIFY(firstOutput = compositor->output());
-
- TestWindow window;
- window.show();
-
- QSharedPointer<MockSurface> surface;
- QTRY_VERIFY(surface = compositor->surface());
- compositor->sendShellSurfaceConfigure(surface);
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
- QScreen *primaryScreen = QGuiApplication::screens().first();
- QCOMPARE(window.screen(), primaryScreen);
-
- compositor->sendAddOutput();
-
- QTRY_COMPARE(QGuiApplication::screens().size(), 2);
- QTRY_COMPARE(QGuiApplication::primaryScreen()->virtualSiblings().size(), 2);
- QScreen *secondaryScreen = QGuiApplication::screens().at(1);
- QVERIFY(secondaryScreen);
-
- compositor->sendRemoveOutput(firstOutput);
- QTRY_COMPARE(QGuiApplication::screens().size(), 1);
-
- compositor->sendMousePress(surface, window.frameOffset() + QPoint(10, 10));
- QTRY_COMPARE(window.mousePressEventCount, 1);
- compositor->sendMouseRelease(surface);
- QTRY_COMPARE(window.mouseReleaseEventCount, 1);
-}
-
void tst_WaylandClient::createDestroyWindow()
{
TestWindow window;
diff --git a/tests/auto/client/datadevicev1/datadevicev1.pro b/tests/auto/client/datadevicev1/datadevicev1.pro
new file mode 100644
index 000000000..b3c687c4d
--- /dev/null
+++ b/tests/auto/client/datadevicev1/datadevicev1.pro
@@ -0,0 +1,4 @@
+include (../shared/shared.pri)
+
+TARGET = tst_datadevicev1
+SOURCES += tst_datadevicev1.cpp
diff --git a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
new file mode 100644
index 000000000..fe68d520d
--- /dev/null
+++ b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+//TODO: move?
+#include <QtGui/QClipboard>
+
+using namespace MockCompositor;
+
+constexpr int dataDeviceVersion = 1;
+
+class DataDeviceCompositor : public DefaultCompositor {
+public:
+ explicit DataDeviceCompositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+ add<DataDeviceManager>(dataDeviceVersion);
+ });
+ }
+ DataDevice *dataDevice() { return get<DataDeviceManager>()->deviceFor(get<Seat>()); }
+};
+
+class tst_datadevicev1 : public QObject, private DataDeviceCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void initTestCase();
+ void pasteAscii();
+ void pasteUtf8();
+ void destroysPreviousSelection();
+ void destroysSelectionWithSurface();
+};
+
+void tst_datadevicev1::initTestCase()
+{
+ QCOMPOSITOR_TRY_VERIFY(pointer());
+ QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty());
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4);
+
+ QCOMPOSITOR_TRY_VERIFY(keyboard());
+
+ QCOMPOSITOR_TRY_VERIFY(dataDevice());
+ QCOMPOSITOR_TRY_VERIFY(dataDevice()->resourceMap().contains(client()));
+ QCOMPOSITOR_TRY_COMPARE(dataDevice()->resourceMap().value(client())->version(), dataDeviceVersion);
+}
+
+void tst_datadevicev1::pasteAscii()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *) override { m_text = QGuiApplication::clipboard()->text(); }
+ QString m_text;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ exec([&] {
+ auto *client = xdgSurface()->resource()->client();
+ auto *offer = dataDevice()->sendDataOffer(client, {"text/plain"});
+ connect(offer, &DataOffer::receive, [](QString mimeType, int fd) {
+ QFile file;
+ file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ QCOMPARE(mimeType, "text/plain");
+ file.write(QByteArray("normal ascii"));
+ file.close();
+ });
+ dataDevice()->sendSelection(offer);
+
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client, BTN_LEFT, 1);
+ pointer()->sendButton(client, BTN_LEFT, 0);
+ });
+ QTRY_COMPARE(window.m_text, "normal ascii");
+}
+
+void tst_datadevicev1::pasteUtf8()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *) override { m_text = QGuiApplication::clipboard()->text(); }
+ QString m_text;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ exec([&] {
+ auto *client = xdgSurface()->resource()->client();
+ auto *offer = dataDevice()->sendDataOffer(client, {"text/plain", "text/plain;charset=utf-8"});
+ connect(offer, &DataOffer::receive, [](QString mimeType, int fd) {
+ QFile file;
+ file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ QCOMPARE(mimeType, "text/plain;charset=utf-8");
+ file.write(QByteArray("face with tears of joy: 😂"));
+ file.close();
+ });
+ dataDevice()->sendSelection(offer);
+
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client, BTN_LEFT, 1);
+ pointer()->sendButton(client, BTN_LEFT, 0);
+ });
+ QTRY_COMPARE(window.m_text, "face with tears of joy: 😂");
+}
+
+void tst_datadevicev1::destroysPreviousSelection()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ // When the client receives a selection event, it is required to destroy the previous offer
+ exec([&] {
+ QCOMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
+ auto *offer = dataDevice()->sendDataOffer(client(), {"text/plain"});
+ dataDevice()->sendSelection(offer);
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+ QCOMPARE(dataDevice()->m_sentSelectionOffers.size(), 1);
+ });
+
+ exec([&] {
+ auto *offer = dataDevice()->sendDataOffer(client(), {"text/plain"});
+ dataDevice()->sendSelection(offer);
+ QCOMPARE(dataDevice()->m_sentSelectionOffers.size(), 2);
+ });
+
+ // Verify the first offer gets destroyed
+ QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 1);
+
+ exec([&] {
+ auto *offer = dataDevice()->sendDataOffer(client(), {"text/plain"});
+ dataDevice()->sendSelection(offer);
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendLeave(surface);
+ });
+
+ // Clients are required to destroy their offer when losing keyboard focus
+ QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
+}
+
+void tst_datadevicev1::destroysSelectionWithSurface()
+{
+ auto *window = new QRasterWindow;
+ window->resize(64, 64);
+ window->show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ // When the client receives a selection event, it is required to destroy the previous offer
+ exec([&] {
+ QCOMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
+ auto *offer = dataDevice()->sendDataOffer(client(), {"text/plain"});
+ dataDevice()->sendSelection(offer);
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
+ QCOMPARE(dataDevice()->m_sentSelectionOffers.size(), 1);
+ });
+
+ // Ping to make sure we receive the wl_keyboard enter and leave events, before destroying the
+ // surface. Otherwise, the client will receive enter and leave events with a destroyed (null)
+ // surface, which is not what we are trying to test for here.
+ xdgPingAndWaitForPong();
+ window->destroy();
+
+ QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_datadevicev1)
+#include "tst_datadevicev1.moc"
diff --git a/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro
new file mode 100644
index 000000000..49d19d5c3
--- /dev/null
+++ b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro
@@ -0,0 +1,4 @@
+include (../shared_old/shared_old.pri)
+
+TARGET = tst_client_fullscreenshell1
+SOURCES += tst_fullscreenshellv1.cpp
diff --git a/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp b/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp
new file mode 100644
index 000000000..f93d9fbc5
--- /dev/null
+++ b/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <QWindow>
+
+#include <QtTest/QtTest>
+
+static const QSize screenSize(1600, 1200);
+
+class TestWindow : public QWindow
+{
+public:
+ TestWindow()
+ {
+ setSurfaceType(QSurface::RasterSurface);
+ setGeometry(0, 0, 800, 600);
+ create();
+ }
+};
+
+class tst_WaylandClientFullScreenShellV1 : public QObject
+{
+ Q_OBJECT
+public:
+ tst_WaylandClientFullScreenShellV1(MockCompositor *c)
+ : m_compositor(c)
+ {
+ QSocketNotifier *notifier = new QSocketNotifier(m_compositor->waylandFileDescriptor(), QSocketNotifier::Read, this);
+ connect(notifier, &QSocketNotifier::activated, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents);
+ // connect to the event dispatcher to make sure to flush out the outgoing message queue
+ connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents);
+ connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents);
+ }
+
+public slots:
+ void processWaylandEvents()
+ {
+ m_compositor->processWaylandEvents();
+ }
+
+ void cleanup()
+ {
+ // make sure the surfaces from the last test are properly cleaned up
+ // and don't show up as false positives in the next test
+ QTRY_VERIFY(!m_compositor->fullScreenShellV1Surface());
+ }
+
+private slots:
+ void createDestroyWindow();
+
+private:
+ MockCompositor *m_compositor = nullptr;
+};
+
+void tst_WaylandClientFullScreenShellV1::createDestroyWindow()
+{
+ TestWindow window;
+ window.show();
+
+ QTRY_VERIFY(m_compositor->fullScreenShellV1Surface());
+
+ window.destroy();
+ QTRY_VERIFY(!m_compositor->fullScreenShellV1Surface());
+}
+
+int main(int argc, char **argv)
+{
+ setenv("XDG_RUNTIME_DIR", ".", 1);
+ setenv("QT_QPA_PLATFORM", "wayland", 1); // force QGuiApplication to use wayland plugin
+ setenv("QT_WAYLAND_SHELL_INTEGRATION", "fullscreen-shell-v1", 1);
+ setenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1", 1); // window decorations don't make much sense here
+
+ MockCompositor compositor;
+ compositor.setOutputMode(screenSize);
+
+ QGuiApplication app(argc, argv);
+ compositor.applicationInitialized();
+
+ tst_WaylandClientFullScreenShellV1 tc(&compositor);
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include <tst_fullscreenshellv1.moc>
diff --git a/tests/auto/client/inputcontext/inputcontext.pro b/tests/auto/client/inputcontext/inputcontext.pro
new file mode 100644
index 000000000..4419b3e77
--- /dev/null
+++ b/tests/auto/client/inputcontext/inputcontext.pro
@@ -0,0 +1,6 @@
+include (../shared/shared.pri)
+
+QT += waylandcompositor
+
+TARGET = tst_inputcontext
+SOURCES += tst_inputcontext.cpp
diff --git a/tests/auto/client/inputcontext/tst_inputcontext.cpp b/tests/auto/client/inputcontext/tst_inputcontext.cpp
new file mode 100644
index 000000000..b1a5a7f17
--- /dev/null
+++ b/tests/auto/client/inputcontext/tst_inputcontext.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include "textinput.h"
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatforminputcontext.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
+
+#include <QtTest/QtTest>
+
+using namespace MockCompositor;
+
+class tst_inputcontext : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void selectingInputContext_data();
+ void selectingInputContext();
+ void inputContextReconfigurationWhenTogglingTextInputExtension();
+
+private:
+ QByteArray inputContextName() const;
+ void ensureTextInputPresentOnCompositor();
+ void ensureTextInputNotPresentOnCompositor();
+
+ QByteArray mComposeModule = QByteArray("QComposeInputContext"); // default input context
+ QByteArray mIbusModule = QByteArray("QIBusPlatformInputContext");
+ QByteArray mWaylandModule = QByteArray("QtWaylandClient::QWaylandInputContext");
+
+ TextInputManager *mTextInputManager = nullptr;
+};
+
+void tst_inputcontext::initTestCase()
+{
+ // Verify that plugins are present and valid
+ QPlatformInputContext *context = QPlatformInputContextFactory::create(QStringLiteral("compose"));
+ QVERIFY(context && context->isValid());
+
+ context = QPlatformInputContextFactory::create(QStringLiteral("ibus"));
+ // The ibus plugin depends on properly configured system services, if plugin is not valid
+ // verify that wayland qpa plugin properly fallbacks to default input context.
+ if (!context || !context->isValid())
+ mIbusModule = mComposeModule;
+}
+
+QByteArray tst_inputcontext::inputContextName() const
+{
+ QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ if (platformIntegration->inputContext())
+ return platformIntegration->inputContext()->metaObject()->className();
+
+ return QByteArray("");
+}
+
+void tst_inputcontext::ensureTextInputPresentOnCompositor()
+{
+ exec([&] {
+ QVector<TextInputManager *> extensions = getAll<TextInputManager>();
+ if (extensions.length() > 1)
+ QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned");
+ if (extensions.length() == 0)
+ add<TextInputManager>();
+ });
+}
+
+void tst_inputcontext::ensureTextInputNotPresentOnCompositor()
+{
+ exec([&] {
+ QVector<TextInputManager *> extensions = getAll<TextInputManager>();
+ if (extensions.length() > 1)
+ QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned");
+ if (extensions.length() == 1)
+ remove(extensions.first());
+ });
+}
+
+void tst_inputcontext::selectingInputContext_data()
+{
+ QTest::addColumn<QByteArray>("requestedModule");
+ QTest::addColumn<QByteArray>("expectedModule");
+
+ // Test compositor without Text Input extension
+ QTest::newRow("ibus") << QByteArray("ibus") << mIbusModule;
+ QTest::newRow("compose") << QByteArray("compose") << mComposeModule;
+ QTest::newRow("empty") << QByteArray("") << mComposeModule;
+ QTest::newRow("null") << QByteArray() << mComposeModule;
+ QTest::newRow("fake") << QByteArray("fake") << mComposeModule;
+
+ // Test compositor with Text Input extension
+ QTest::newRow("ibus:text-input") << QByteArray("ibus") << mIbusModule;
+ QTest::newRow("compose:text-input") << QByteArray("compose") << mComposeModule;
+ QTest::newRow("empty:text-input") << QByteArray("") << mComposeModule;
+ QTest::newRow("null:text-input") << QByteArray() << mWaylandModule;
+ QTest::newRow("fake:text-input") << QByteArray("fake") << mComposeModule;
+}
+
+void tst_inputcontext::selectingInputContext()
+{
+ QFETCH(QByteArray, requestedModule);
+ QFETCH(QByteArray, expectedModule);
+
+ if (requestedModule.isNull())
+ qunsetenv("QT_IM_MODULE");
+ else
+ qputenv("QT_IM_MODULE", requestedModule);
+
+ const bool withTextInputAtCompositorSide = QByteArray(QTest::currentDataTag()).endsWith(":text-input");
+
+ if (withTextInputAtCompositorSide)
+ ensureTextInputPresentOnCompositor();
+ else
+ ensureTextInputNotPresentOnCompositor();
+
+ int argc = 0;
+ QGuiApplication app(argc, nullptr); // loads the platform plugin
+
+ QCOMPARE(inputContextName(), expectedModule);
+}
+
+void tst_inputcontext::inputContextReconfigurationWhenTogglingTextInputExtension()
+{
+ qunsetenv("QT_IM_MODULE");
+
+ ensureTextInputPresentOnCompositor();
+ int argc = 0;
+ QGuiApplication app(argc, nullptr); // loads the platform plugin
+ QCOMPARE(inputContextName(), mWaylandModule);
+
+ // remove text input extension after the platform plugin has been loaded
+ ensureTextInputNotPresentOnCompositor();
+ // QTRY_* because we need to spin the event loop for wayland QPA plugin
+ // to handle registry_global_remove()
+ QTRY_COMPARE(inputContextName(), mComposeModule);
+
+ // add text input extension after the platform plugin has been loaded
+ ensureTextInputPresentOnCompositor();
+ // QTRY_* because we need to spin the event loop for wayland QPA plugin
+ // to handle registry_global()
+ QTRY_COMPARE(inputContextName(), mWaylandModule);
+}
+
+int main(int argc, char *argv[])
+{
+ qputenv("XDG_RUNTIME_DIR", ".");
+ qputenv("QT_QPA_PLATFORM", "wayland");
+
+ tst_inputcontext tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_inputcontext.moc"
diff --git a/tests/auto/client/iviapplication/iviapplication.pro b/tests/auto/client/iviapplication/iviapplication.pro
index 326921373..f2d596e6c 100644
--- a/tests/auto/client/iviapplication/iviapplication.pro
+++ b/tests/auto/client/iviapplication/iviapplication.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client_iviapplication
SOURCES += tst_iviapplication.cpp
diff --git a/tests/auto/client/output/output.pro b/tests/auto/client/output/output.pro
new file mode 100644
index 000000000..d1dc672f0
--- /dev/null
+++ b/tests/auto/client/output/output.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_output
+SOURCES += tst_output.cpp
+
diff --git a/tests/auto/client/output/tst_output.cpp b/tests/auto/client/output/tst_output.cpp
new file mode 100644
index 000000000..2d2c8efd6
--- /dev/null
+++ b/tests/auto/client/output/tst_output.cpp
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QScreen>
+#include <QtGui/QRasterWindow>
+
+using namespace MockCompositor;
+
+class tst_output : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void initTestCase()
+ {
+ m_config.autoConfigure = true;
+ m_config.autoEnter = false;
+ }
+ void cleanup()
+ {
+ QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // Only the default output should be left
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+ }
+ void primaryScreen();
+ void secondaryHiDpiScreen();
+ void addScreenWithGeometryChange();
+ void windowScreens();
+ void removePrimaryScreen();
+ void screenOrder();
+};
+
+void tst_output::primaryScreen()
+{
+ // Verify that the client has bound to the output global
+ QCOMPOSITOR_TRY_COMPARE(output()->resourceMap().size(), 1);
+ QTRY_VERIFY(QGuiApplication::primaryScreen());
+ QScreen *screen = QGuiApplication::primaryScreen();
+ QCOMPARE(screen->manufacturer(), "Make");
+ QCOMPARE(screen->model(), "Model");
+ QCOMPARE(screen->size(), QSize(1920, 1080));
+ QCOMPARE(screen->refreshRate(), 60);
+ QCOMPARE(qRound(screen->physicalDotsPerInch()), 96 / screen->devicePixelRatio());
+ QCOMPARE(screen->devicePixelRatio(), 1);
+ QCOMPARE(screen->logicalDotsPerInch(), 96);
+}
+
+void tst_output::secondaryHiDpiScreen()
+{
+ exec([=] {
+ OutputData d;
+ d.position = {1920, 0}; // in global compositor space (not pixels)
+ d.mode.resolution = {800, 640};
+ d.physicalSize = d.mode.physicalSizeForDpi(200);
+ d.scale = 2;
+ add<Output>(d);
+ });
+
+ // Verify that the client has bound to the output global
+ QCOMPOSITOR_TRY_VERIFY(output(1) && output(1)->resourceMap().size() == 1);
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
+ QScreen *screen = QGuiApplication::screens()[1];
+ QCOMPARE(screen->refreshRate(), 60);
+ QCOMPARE(screen->devicePixelRatio(), 2);
+ QCOMPARE(screen->logicalDotsPerInch(), 96);
+
+ // Dots currently means device pixels, not actual pixels (see QTBUG-62649)
+ QCOMPARE(qRound(screen->physicalDotsPerInch() * screen->devicePixelRatio()), 200);
+
+ // Size is in logical pixel coordinates
+ QCOMPARE(screen->size(), QSize(800, 640) / 2);
+ QCOMPARE(screen->geometry(), QRect(QPoint(1920, 0), QSize(400, 320)));
+ QCOMPARE(screen->virtualGeometry(), QRect(QPoint(0, 0), QSize(1920 + 800 / 2, 1080)));
+
+ exec([=] { remove(output(1)); });
+}
+
+// QTBUG-62044
+void tst_output::addScreenWithGeometryChange()
+{
+ const QPoint initialPosition = exec([=] { return output(0)->m_data.position; });
+
+ exec([=] {
+ auto *oldOutput = output(0);
+ auto *newOutput = add<Output>();
+ newOutput->m_data.mode.resolution = {1280, 720};
+ // Move the primary output to the right
+ QPoint newPosition(newOutput->m_data.mode.resolution.width(), 0);
+ Q_ASSERT(newPosition != initialPosition);
+ oldOutput->m_data.position = newPosition;
+ oldOutput->sendGeometry();
+ oldOutput->sendDone();
+ });
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
+ QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), QRect(QPoint(1280, 0), QSize(1920, 1080)));
+
+ // Remove the extra output and move the old one back
+ exec([=] {
+ remove(output(1));
+ output()->m_data.position = initialPosition;
+ output()->sendGeometry();
+ output()->sendDone();
+ });
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+ QTRY_COMPARE(QGuiApplication::primaryScreen()->geometry(), QRect(QPoint(0, 0), QSize(1920, 1080)));
+}
+
+void tst_output::windowScreens()
+{
+ QRasterWindow window;
+ window.resize(400, 320);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+ QScreen *primaryScreen = QGuiApplication::screens().first();
+ QCOMPARE(window.screen(), primaryScreen);
+
+ exec([=] { add<Output>(); });
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
+ QScreen *secondaryScreen = QGuiApplication::screens().at(1);
+ QVERIFY(secondaryScreen);
+
+ window.setScreen(secondaryScreen);
+ QCOMPARE(window.screen(), secondaryScreen);
+
+ exec([=] {
+ xdgToplevel()->surface()->sendEnter(output(0));
+ xdgToplevel()->surface()->sendEnter(output(1));
+ });
+
+ QTRY_COMPARE(window.screen(), primaryScreen);
+
+ exec([=] {
+ xdgToplevel()->surface()->sendLeave(output(0));
+ });
+ QTRY_COMPARE(window.screen(), secondaryScreen);
+
+ exec([=] {
+ remove(output(1));
+ });
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+ QCOMPARE(window.screen(), primaryScreen);
+}
+
+void tst_output::removePrimaryScreen()
+{
+ QRasterWindow window;
+ window.resize(400, 320);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+ QScreen *primaryScreen = QGuiApplication::screens().first();
+ QCOMPARE(window.screen(), primaryScreen);
+
+ // Add a clone of the primary output
+ exec([&] { add<Output>(output()->m_data); });
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
+ QTRY_COMPARE(QGuiApplication::primaryScreen()->virtualSiblings().size(), 2);
+ QScreen *secondaryScreen = QGuiApplication::screens().at(1);
+ QVERIFY(secondaryScreen);
+
+ exec([&] { remove(output()); });
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
+
+ exec([&] {
+ auto *surface = xdgToplevel()->surface();
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client(), BTN_LEFT, 1);
+ pointer()->sendButton(client(), BTN_LEFT, 0);
+ });
+
+ // Wait to make sure mouse events dont't cause a crash now that the screen has changed
+ xdgPingAndWaitForPong();
+}
+
+// QTBUG-72828
+void tst_output::screenOrder()
+{
+ exec([=] {
+ add<Output>()->m_data.model = "Screen 1";
+ add<Output>()->m_data.model = "Screen 2";
+ });
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 3);
+ const auto screens = QGuiApplication::screens();
+
+ QCOMPARE(screens[1]->model(), "Screen 1");
+ QCOMPARE(screens[2]->model(), "Screen 2");
+
+ exec([=] {
+ remove(output(2));
+ remove(output(1));
+ });
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_output)
+#include "tst_output.moc"
diff --git a/tests/auto/client/seatv4/seatv4.pro b/tests/auto/client/seatv4/seatv4.pro
new file mode 100644
index 000000000..7a86cbf03
--- /dev/null
+++ b/tests/auto/client/seatv4/seatv4.pro
@@ -0,0 +1,9 @@
+include (../shared/shared.pri)
+
+qtConfig(cursor) {
+ QMAKE_USE += wayland-cursor
+ QT += gui-private
+}
+
+TARGET = tst_seatv4
+SOURCES += tst_seatv4.cpp
diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp
new file mode 100644
index 000000000..7dc2e727a
--- /dev/null
+++ b/tests/auto/client/seatv4/tst_seatv4.cpp
@@ -0,0 +1,560 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+#if QT_CONFIG(cursor)
+#include <wayland-cursor.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtWaylandClient/private/qwaylandintegration_p.h>
+#endif
+
+using namespace MockCompositor;
+
+// wl_seat version 5 was introduced in wayland 1.10, and although that's pretty old,
+// there are still compositors that have yet to update their implementation to support
+// the new version (most importantly our own QtWaylandCompositor).
+// As long as that's the case, this test makes sure input events still works on version 4.
+class SeatV4Compositor : public DefaultCompositor {
+public:
+ explicit SeatV4Compositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+
+ removeAll<Seat>();
+
+ uint capabilities = Seat::capability_pointer | Seat::capability_keyboard;
+ int version = 4;
+ add<Seat>(capabilities, version);
+ });
+ }
+};
+
+class tst_seatv4 : public QObject, private SeatV4Compositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup();
+ void bindsToSeat();
+ void keyboardKeyPress();
+#if QT_CONFIG(cursor)
+ void createsPointer();
+ void setsCursorOnEnter();
+ void usesEnterSerial();
+ void focusDestruction();
+ void mousePress();
+ void simpleAxis_data();
+ void simpleAxis();
+ void invalidPointerEvents();
+ void scaledCursor();
+ void unscaledFallbackCursor();
+ void bitmapCursor();
+ void hidpiBitmapCursor();
+ void hidpiBitmapCursorNonInt();
+#endif
+};
+
+void tst_seatv4::cleanup()
+{
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+ QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // No extra outputs left
+}
+
+void tst_seatv4::bindsToSeat()
+{
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1);
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 4);
+}
+
+void tst_seatv4::keyboardKeyPress()
+{
+ class Window : public QRasterWindow {
+ public:
+ void keyPressEvent(QKeyEvent *) override { m_pressed = true; }
+ bool m_pressed = false;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ uint keyCode = 80; // arbitrarily chosen
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ keyboard()->sendEnter(surface);
+ keyboard()->sendKey(client(), keyCode, Keyboard::key_state_pressed);
+ keyboard()->sendKey(client(), keyCode, Keyboard::key_state_released);
+ });
+ QTRY_VERIFY(window.m_pressed);
+}
+
+#if QT_CONFIG(cursor)
+
+void tst_seatv4::createsPointer()
+{
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4);
+}
+
+void tst_seatv4::setsCursorOnEnter()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+}
+
+void tst_seatv4::usesEnterSerial()
+{
+ QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor);
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ uint enterSerial = exec([=] {
+ return pointer()->sendEnter(xdgSurface()->m_surface, {32, 32});
+ });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+
+ QTRY_COMPARE(setCursorSpy.count(), 1);
+ QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial);
+}
+
+void tst_seatv4::focusDestruction()
+{
+ QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor);
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ // Setting a cursor now is not allowed since there has been no enter event
+ QCOMPARE(setCursorSpy.count(), 0);
+
+ uint enterSerial = exec([=] {
+ return pointer()->sendEnter(xdgSurface()->m_surface, {32, 32});
+ });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QTRY_COMPARE(setCursorSpy.count(), 1);
+ QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial);
+
+ // Destroy the focus
+ window.close();
+
+ QRasterWindow window2;
+ window2.resize(64, 64);
+ window2.show();
+ window2.setCursor(Qt::WaitCursor);
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ // Setting a cursor now is not allowed since there has been no enter event
+ xdgPingAndWaitForPong();
+ QCOMPARE(setCursorSpy.count(), 0);
+}
+
+void tst_seatv4::mousePress()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *) override { m_pressed = true; }
+ bool m_pressed = false;
+ };
+
+ Window window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([&] {
+ auto *surface = xdgSurface()->m_surface;
+ pointer()->sendEnter(surface, {32, 32});
+ pointer()->sendButton(client(), BTN_LEFT, 1);
+ pointer()->sendButton(client(), BTN_LEFT, 0);
+ });
+ QTRY_VERIFY(window.m_pressed);
+}
+
+void tst_seatv4::simpleAxis_data()
+{
+ QTest::addColumn<uint>("axis");
+ QTest::addColumn<qreal>("value");
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<QPoint>("angleDelta");
+
+ // Directions in regular windows/linux terms (no "natural" scrolling)
+ QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12};
+ QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12};
+ QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0};
+ QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0};
+ QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120};
+}
+
+void tst_seatv4::simpleAxis()
+{
+ QFETCH(uint, axis);
+ QFETCH(qreal, value);
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(QPoint, angleDelta);
+
+ class WheelWindow : QRasterWindow {
+ public:
+ explicit WheelWindow()
+ {
+ resize(64, 64);
+ show();
+ }
+ void wheelEvent(QWheelEvent *event) override
+ {
+ QRasterWindow::wheelEvent(event);
+ // Angle delta should always be provided (says docs)
+ QVERIFY(!event->angleDelta().isNull());
+
+ // There are now scroll phases on Wayland prior to v5
+ QCOMPARE(event->phase(), Qt::NoScrollPhase);
+
+ // Pixel delta should only be set if we know it's a high-res input device (which we don't)
+ QCOMPARE(event->pixelDelta(), QPoint(0, 0));
+
+ // The axis vector of the event is already in surface space, so there is now way to tell
+ // whether it is inverted or not.
+ QCOMPARE(event->inverted(), false);
+
+ // We didn't press any buttons
+ QCOMPARE(event->buttons(), Qt::NoButton);
+
+ if (event->orientation() == Qt::Horizontal)
+ QCOMPARE(event->delta(), event->angleDelta().x());
+ else
+ QCOMPARE(event->delta(), event->angleDelta().y());
+
+ // There has been no information about what created the event.
+ // Documentation says not synthesized is appropriate in such cases
+ QCOMPARE(event->source(), Qt::MouseEventNotSynthesized);
+
+ m_events.append(Event(event->pixelDelta(), event->angleDelta(), event->orientation()));
+ }
+ struct Event // Because I didn't find a convenient way to copy it entirely
+ {
+ // TODO: Constructors can be removed when we start supporting brace-initializers
+ Event() = default;
+ Event(const QPoint &pixelDelta, const QPoint &angleDelta, Qt::Orientation orientation)
+ : pixelDelta(pixelDelta), angleDelta(angleDelta), orientation(orientation)
+ {}
+ const QPoint pixelDelta;
+ const QPoint angleDelta; // eights of a degree, positive is upwards, left
+ const Qt::Orientation orientation{};
+ };
+ QVector<Event> m_events;
+ };
+
+ WheelWindow window;
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ Surface *surface = xdgSurface()->m_surface;
+ pointer()->sendEnter(surface, {32, 32});
+ wl_client *client = surface->resource()->client();
+ // Length of vector in surface-local space. i.e. positive is downwards
+ pointer()->sendAxis(
+ client,
+ Pointer::axis(axis),
+ value // Length of vector in surface-local space. i.e. positive is downwards
+ );
+ });
+
+ QTRY_COMPARE(window.m_events.size(), 1);
+ auto event = window.m_events.takeFirst();
+ QCOMPARE(event.angleDelta, angleDelta);
+ QCOMPARE(event.orientation, orientation);
+}
+
+void tst_seatv4::invalidPointerEvents()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *p = pointer();
+ auto *c = client();
+ // Purposefully send events without a wl_pointer.enter
+ p->sendMotion(c, {32, 32});
+ p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed);
+ p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0);
+ });
+
+ // Make sure we get here without crashing
+ xdgPingAndWaitForPong();
+}
+
+static bool supportsCursorSize(uint size, wl_shm *shm)
+{
+ auto *theme = wl_cursor_theme_load(qgetenv("XCURSOR_THEME"), size, shm);
+ if (!theme)
+ return false;
+
+ constexpr std::array<const char *, 4> names{"left_ptr", "default", "left_arrow", "top_left_arrow"};
+ for (const char *name : names) {
+ if (auto *cursor = wl_cursor_theme_get_cursor(theme, name)) {
+ auto *image = cursor->images[0];
+ return image->width == image->height && image->width == size;
+ }
+ }
+ return false;
+}
+
+static bool supportsCursorSizes(const QVector<uint> &sizes)
+{
+ auto *waylandIntegration = static_cast<QtWaylandClient::QWaylandIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ wl_shm *shm = waylandIntegration->display()->shm()->object();
+ return std::all_of(sizes.begin(), sizes.end(), [=](uint size) {
+ return supportsCursorSize(size, shm);
+ });
+}
+
+static uint defaultCursorSize() {
+ const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
+ return xCursorSize > 0 ? uint(xCursorSize) : 32;
+}
+
+void tst_seatv4::scaledCursor()
+{
+ const uint defaultSize = defaultCursorSize();
+ if (!supportsCursorSizes({defaultSize, defaultSize * 2}))
+ QSKIP("Cursor themes with default size and 2x default size not found.");
+
+ // Add a highdpi output
+ exec([&] {
+ OutputData d;
+ d.scale = 2;
+ d.position = {1920, 0};
+ add<Output>(d);
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.bufferScale, 1);
+ QSize unscaledPixelSize = exec([=] {
+ return cursorSurface()->m_committed.buffer->size();
+ });
+
+ exec([=] {
+ auto *surface = cursorSurface();
+ surface->sendEnter(getAll<Output>()[1]);
+ surface->sendLeave(getAll<Output>()[0]);
+ });
+
+ QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.buffer->size(), unscaledPixelSize * 2);
+
+ // Remove the extra output to clean up for the next test
+ exec([&] { remove(output(1)); });
+}
+
+void tst_seatv4::unscaledFallbackCursor()
+{
+ const uint defaultSize = defaultCursorSize();
+ if (!supportsCursorSizes({defaultSize}))
+ QSKIP("Default cursor size not supported");
+
+ const int screens = 4; // with scales 1, 2, 4, 8
+
+ exec([=] {
+ for (int i = 1; i < screens; ++i) {
+ OutputData d;
+ d.scale = int(qPow(2, i));
+ d.position = {1920 * i, 0};
+ add<Output>(d);
+ }
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.bufferScale, 1);
+ QSize unscaledPixelSize = exec([=] {
+ return cursorSurface()->m_committed.buffer->size();
+ });
+
+ QCOMPARE(unscaledPixelSize.width(), int(defaultSize));
+ QCOMPARE(unscaledPixelSize.height(), int(defaultSize));
+
+ for (int i = 1; i < screens; ++i) {
+ exec([=] {
+ auto *surface = cursorSurface();
+ surface->sendEnter(getAll<Output>()[i]);
+ surface->sendLeave(getAll<Output>()[i-1]);
+ });
+
+ xdgPingAndWaitForPong(); // Give the client a chance to mess up
+
+ // Surface size (buffer size / scale) should stay constant
+ QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.buffer->size() / cursorSurface()->m_committed.bufferScale, unscaledPixelSize);
+ }
+
+ // Remove the extra outputs to clean up for the next test
+ exec([&] { while (auto *o = output(1)) remove(o); });
+}
+
+void tst_seatv4::bitmapCursor()
+{
+ // Add a highdpi output
+ exec([&] {
+ OutputData d;
+ d.scale = 2;
+ d.position = {1920, 0};
+ add<Output>(d);
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+
+ QPixmap pixmap(24, 24);
+ pixmap.setDevicePixelRatio(1);
+ QPoint hotspot(12, 12); // In device pixel coordinates
+ QCursor cursor(pixmap, hotspot.x(), hotspot.y());
+ window.setCursor(cursor);
+
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(24, 24));
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 1);
+ QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
+
+ exec([=] {
+ auto *surface = cursorSurface();
+ surface->sendEnter(getAll<Output>()[1]);
+ surface->sendLeave(getAll<Output>()[0]);
+ });
+
+ xdgPingAndWaitForPong();
+
+ // Everything should remain the same, the cursor still has dpr 1
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 1);
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(24, 24));
+ QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
+
+ // Remove the extra output to clean up for the next test
+ exec([&] { remove(getAll<Output>()[1]); });
+}
+
+void tst_seatv4::hidpiBitmapCursor()
+{
+ // Add a highdpi output
+ exec([&] {
+ OutputData d;
+ d.scale = 2;
+ d.position = {1920, 0};
+ add<Output>(d);
+ });
+
+ QRasterWindow window;
+ window.resize(64, 64);
+
+ QPixmap pixmap(48, 48);
+ pixmap.setDevicePixelRatio(2);
+ QPoint hotspot(12, 12); // In device pixel coordinates
+ QCursor cursor(pixmap, hotspot.x(), hotspot.y());
+ window.setCursor(cursor);
+
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(48, 48));
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2);
+ QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
+
+ exec([=] {
+ auto *surface = cursorSurface();
+ surface->sendEnter(getAll<Output>()[1]);
+ surface->sendLeave(getAll<Output>()[0]);
+ });
+
+ xdgPingAndWaitForPong();
+
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2);
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(48, 48));
+ QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
+
+ // Remove the extra output to clean up for the next test
+ exec([&] { remove(getAll<Output>()[1]); });
+}
+
+void tst_seatv4::hidpiBitmapCursorNonInt()
+{
+ QRasterWindow window;
+ window.resize(64, 64);
+
+ QPixmap pixmap(100, 100);
+ pixmap.setDevicePixelRatio(2.5); // dpr width is now 100 / 2.5 = 40
+ QPoint hotspot(20, 20); // In device pixel coordinates (middle of buffer)
+ QCursor cursor(pixmap, hotspot.x(), hotspot.y());
+ window.setCursor(cursor);
+
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
+
+ exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface());
+ QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer);
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(100, 100));
+ QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2);
+ // Verify that the hotspot was scaled correctly
+ // Surface size is now 100 / 2 = 50, so the middle should be at 25 in surface coordinates
+ QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(25, 25));
+}
+
+#endif // QT_CONFIG(cursor)
+
+QCOMPOSITOR_TEST_MAIN(tst_seatv4)
+#include "tst_seatv4.moc"
diff --git a/tests/auto/client/shared/corecompositor.cpp b/tests/auto/client/shared/corecompositor.cpp
new file mode 100644
index 000000000..7edb1c2d4
--- /dev/null
+++ b/tests/auto/client/shared/corecompositor.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "corecompositor.h"
+
+namespace MockCompositor {
+
+CoreCompositor::CoreCompositor()
+ : m_display(wl_display_create())
+ , m_socketName(wl_display_add_socket_auto(m_display))
+ , m_eventLoop(wl_display_get_event_loop(m_display))
+
+ // Start dispatching
+ , m_dispatchThread([this](){
+ while (m_running) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ dispatch();
+ }
+ })
+{
+ m_timer.start();
+ Q_ASSERT(isClean());
+}
+
+CoreCompositor::~CoreCompositor()
+{
+ m_running = false;
+ m_dispatchThread.join();
+ wl_display_destroy(m_display);
+}
+
+bool CoreCompositor::isClean()
+{
+ Lock lock(this);
+ for (auto *global : qAsConst(m_globals)) {
+ if (!global->isClean())
+ return false;
+ }
+ return true;
+}
+
+QString CoreCompositor::dirtyMessage()
+{
+ Lock lock(this);
+ QStringList messages;
+ for (auto *global : qAsConst(m_globals)) {
+ if (!global->isClean())
+ messages << (global->metaObject()->className() % QLatin1String(": ") % global->dirtyMessage());
+ }
+ return messages.join(", ");
+}
+
+void CoreCompositor::dispatch()
+{
+ Lock lock(this);
+ wl_display_flush_clients(m_display);
+ constexpr int timeout = 0; // immediate return
+ wl_event_loop_dispatch(m_eventLoop, timeout);
+}
+
+/*!
+ * \brief Adds a new global interface for the compositor
+ *
+ * Takes ownership of \a global
+ */
+void CoreCompositor::add(Global *global)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ m_globals.append(global);
+}
+
+void CoreCompositor::remove(Global *global)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ m_globals.removeAll(global);
+ delete global;
+}
+
+uint CoreCompositor::nextSerial()
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ return wl_display_next_serial(m_display);
+}
+
+uint CoreCompositor::currentTimeMilliseconds()
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ return uint(m_timer.elapsed());
+}
+
+wl_client *CoreCompositor::client(int index)
+{
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ wl_list *clients = wl_display_get_client_list(m_display);
+ wl_client *client = nullptr;
+ int i = 0;
+ wl_client_for_each(client, clients) {
+ if (i++ == index)
+ return client;
+ }
+ return nullptr;
+}
+
+void CoreCompositor::warnIfNotLockedByThread(const char *caller)
+{
+ if (!m_lock || !m_lock->isOwnedByCurrentThread()) {
+ qWarning() << caller << "called without locking the compositor to the current thread."
+ << "This means the compositor can start dispatching at any moment,"
+ << "potentially leading to threading issues."
+ << "Unless you know what you are doing you should probably fix the test"
+ << "by locking the compositor before accessing it (see mutex()).";
+ }
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/corecompositor.h b/tests/auto/client/shared/corecompositor.h
new file mode 100644
index 000000000..875b7d050
--- /dev/null
+++ b/tests/auto/client/shared/corecompositor.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_CORECOMPOSITOR_H
+#define MOCKCOMPOSITOR_CORECOMPOSITOR_H
+
+#include <QtTest/QtTest>
+
+#include <wayland-server-core.h>
+
+struct wl_resource;
+
+namespace MockCompositor {
+
+class Global : public QObject
+{
+ Q_OBJECT
+public:
+ virtual bool isClean() { return true; }
+ virtual QString dirtyMessage() { return isClean() ? "clean" : "dirty"; }
+};
+
+class CoreCompositor
+{
+public:
+ explicit CoreCompositor();
+ ~CoreCompositor();
+ bool isClean();
+ QString dirtyMessage();
+ void dispatch();
+
+ template<typename function_type, typename... arg_types>
+ auto exec(function_type func, arg_types&&... args) -> decltype(func())
+ {
+ Lock lock(this);
+ return func(std::forward<arg_types>(args)...);
+ }
+
+ template<typename function_type, typename... arg_types>
+ auto call(function_type func, arg_types&&... args) -> decltype(func())
+ {
+ Lock lock(this);
+ auto boundFunc = std::bind(func, this);
+ return boundFunc(this, std::forward<arg_types>(args)...);
+ }
+
+ // Unsafe section below, YOU are responsible that the compositor is locked or
+ // this is run through the mutex() method!
+
+ void add(Global *global);
+ void remove(Global *global);
+
+ /*!
+ * \brief Constructs and adds a new global with the given parameters
+ *
+ * Convenience function. i.e.
+ *
+ * compositor->add(new MyGlobal(compositor, version);
+ *
+ * can be written as:
+ *
+ * compositor->add<MyGlobal>(version);
+ *
+ * Returns the new global
+ */
+ template<typename global_type, typename... arg_types>
+ global_type *add(arg_types&&... args)
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ auto *global = new global_type(this, std::forward<arg_types>(args)...);
+ m_globals.append(global);
+ return global;
+ }
+
+ /*!
+ * \brief Removes all globals of the given type
+ *
+ * Convenience function
+ */
+ template<typename global_type, typename... arg_types>
+ void removeAll()
+ {
+ const auto globals = getAll<global_type>();
+ for (auto global : globals)
+ remove(global);
+ }
+
+ /*!
+ * \brief Returns a global with the given type, if any
+ */
+ template<typename global_type>
+ global_type *get()
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ for (auto *global : qAsConst(m_globals)) {
+ if (auto *casted = qobject_cast<global_type *>(global))
+ return casted;
+ }
+ return nullptr;
+ }
+
+ /*!
+ * \brief Returns all globals with the given type, if any
+ */
+ template<typename global_type>
+ QVector<global_type *> getAll()
+ {
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ QVector<global_type *> matching;
+ for (auto *global : qAsConst(m_globals)) {
+ if (auto *casted = qobject_cast<global_type *>(global))
+ matching.append(casted);
+ }
+ return matching;
+ }
+
+ uint nextSerial();
+ uint currentTimeMilliseconds();
+ wl_client *client(int index = 0);
+ void warnIfNotLockedByThread(const char* caller = "warnIfNotLockedbyThread");
+
+public:
+ // Only use this carefully from the test thread (i.e. lock first)
+ wl_display *m_display = nullptr;
+protected:
+ class Lock {
+ public:
+ explicit Lock(CoreCompositor *compositor)
+ : m_compositor(compositor)
+ , m_threadId(std::this_thread::get_id())
+ {
+ // Can't use a QMutexLocker here, as it's not movable
+ compositor->m_mutex.lock();
+ Q_ASSERT(compositor->m_lock == nullptr);
+ compositor->m_lock = this;
+ }
+ ~Lock()
+ {
+ Q_ASSERT(m_compositor->m_lock == this);
+ m_compositor->m_lock = nullptr;
+ m_compositor->m_mutex.unlock();
+ }
+
+ // Move semantics
+ Lock(Lock &&) = default;
+ Lock &operator=(Lock &&) = default;
+
+ // Disable copying
+ Lock(const Lock &) = delete;
+ Lock &operator=(const Lock &) = delete;
+
+ bool isOwnedByCurrentThread() const { return m_threadId == std::this_thread::get_id(); }
+ private:
+ CoreCompositor *m_compositor = nullptr;
+ std::thread::id m_threadId;
+ };
+ QByteArray m_socketName;
+ wl_event_loop *m_eventLoop = nullptr;
+ bool m_running = true;
+ QVector<Global *> m_globals;
+ QElapsedTimer m_timer;
+
+private:
+ Lock *m_lock = nullptr;
+ QMutex m_mutex;
+ std::thread m_dispatchThread;
+};
+
+template<typename container_type>
+QByteArray toByteArray(container_type container)
+{
+ return QByteArray(reinterpret_cast<const char *>(container.data()), sizeof (container[0]) * container.size());
+}
+
+template<typename return_type>
+return_type *fromResource(::wl_resource *resource) {
+ if (auto *r = return_type::Resource::fromResource(resource))
+ return static_cast<return_type *>(r->object());
+ return nullptr;
+}
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_CORECOMPOSITOR_H
diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp
new file mode 100644
index 000000000..729d481f8
--- /dev/null
+++ b/tests/auto/client/shared/coreprotocol.cpp
@@ -0,0 +1,422 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "coreprotocol.h"
+#include "datadevice.h"
+
+namespace MockCompositor {
+
+void Surface::sendFrameCallbacks()
+{
+ uint time = m_wlCompositor->m_compositor->currentTimeMilliseconds();
+ for (auto *callback : m_waitingFrameCallbacks)
+ callback->sendDone(time);
+ m_waitingFrameCallbacks.clear();
+}
+
+void Surface::sendEnter(Output *output)
+{
+ m_outputs.append(output);
+ const auto outputResources = output->resourceMap().values(resource()->client());
+ for (auto outputResource: outputResources)
+ wl_surface::send_enter(resource()->handle, outputResource->handle);
+}
+
+void Surface::sendLeave(Output *output)
+{
+ m_outputs.removeOne(output);
+ const auto outputResources = output->resourceMap().values(resource()->client());
+ for (auto outputResource: outputResources)
+ wl_surface::send_leave(resource()->handle, outputResource->handle);
+}
+
+void Surface::surface_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ for (auto *commit : m_commits)
+ delete commit->commitSpecific.frame;
+ bool removed = m_wlCompositor->m_surfaces.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+void Surface::surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y)
+{
+ Q_UNUSED(resource);
+ QPoint offset(x, y);
+ m_pending.buffer = fromResource<Buffer>(buffer);
+ m_pending.commitSpecific.attachOffset = offset;
+ m_pending.commitSpecific.attached = true;
+ emit attach(buffer, offset);
+}
+
+void Surface::surface_set_buffer_scale(QtWaylandServer::wl_surface::Resource *resource, int32_t scale)
+{
+ Q_UNUSED(resource);
+ m_pending.bufferScale = scale;
+}
+
+void Surface::surface_commit(Resource *resource)
+{
+ Q_UNUSED(resource);
+ m_committed = m_pending;
+ m_commits.append(new DoubleBufferedState(m_committed));
+
+ if (auto *frame = m_pending.commitSpecific.frame)
+ m_waitingFrameCallbacks.append(frame);
+
+ m_pending.commitSpecific = PerCommitData();
+ emit commit();
+ if (m_committed.commitSpecific.attached)
+ emit bufferCommitted();
+}
+
+void Surface::surface_frame(Resource *resource, uint32_t callback)
+{
+ // Although valid, there is really no point having multiple frame requests in the same commit.
+ // Make sure we don't do it
+ QCOMPARE(m_pending.commitSpecific.frame, nullptr);
+
+ auto *frame = new Callback(resource->client(), callback, 1);
+ m_pending.commitSpecific.frame = frame;
+}
+
+bool WlCompositor::isClean() {
+ for (auto *surface : qAsConst(m_surfaces)) {
+ if (!CursorRole::fromSurface(surface))
+ return false;
+ }
+ return true;
+}
+
+QString WlCompositor::dirtyMessage()
+{
+ if (isClean())
+ return "clean";
+ QStringList messages;
+ for (auto *s : qAsConst(m_surfaces)) {
+ QString role = s->m_role ? s->m_role->staticMetaObject.className(): "none/unknown";
+ messages << "Surface with role: " + role;
+ }
+ return "Dirty, surfaces left:\n\t" + messages.join("\n\t");
+}
+
+void Output::sendGeometry()
+{
+ const auto resources = resourceMap().values();
+ for (auto r : resources)
+ sendGeometry(r);
+}
+
+void Output::sendGeometry(Resource *resource)
+{
+ // TODO: check resource version as well?
+ wl_output::send_geometry(resource->handle,
+ m_data.position.x(), m_data.position.y(),
+ m_data.physicalSize.width(), m_data.physicalSize.height(),
+ m_data.subpixel, m_data.make, m_data.model, m_data.transform);
+}
+
+void Output::sendScale(int factor)
+{
+ Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION);
+ m_data.scale = factor;
+ const auto resources = resourceMap().values();
+ for (auto r : resources)
+ sendScale(r);
+}
+
+void Output::sendScale(Resource *resource)
+{
+ Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION);
+ // TODO: check resource version as well?
+ wl_output::send_scale(resource->handle, m_data.scale);
+}
+
+void Output::sendDone()
+{
+ Q_ASSERT(m_version >= WL_OUTPUT_DONE_SINCE_VERSION);
+ // TODO: check resource version as well?
+ const auto resources = resourceMap().values();
+ for (auto r : resources)
+ wl_output::send_done(r->handle);
+}
+
+void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource)
+{
+ sendGeometry(resource);
+ send_mode(resource->handle, mode_preferred | mode_current,
+ m_data.mode.resolution.width(), m_data.mode.resolution.height(), m_data.mode.refreshRate);
+ if (m_version >= WL_OUTPUT_SCALE_SINCE_VERSION)
+ sendScale(resource);
+
+ if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output::send_done(resource->handle);
+}
+
+// Seat stuff
+Seat::Seat(CoreCompositor *compositor, uint capabilities, int version) //TODO: check version
+ : QtWaylandServer::wl_seat(compositor->m_display, version)
+ , m_compositor(compositor)
+{
+ setCapabilities(capabilities);
+}
+
+Seat::~Seat()
+{
+ qDeleteAll(m_oldPointers);
+ delete m_pointer;
+}
+
+void Seat::setCapabilities(uint capabilities) {
+ // TODO: Add support for touch
+ Q_ASSERT(~capabilities & capability_touch);
+
+ m_capabilities = capabilities;
+
+ if (m_capabilities & capability_pointer) {
+ if (!m_pointer)
+ m_pointer = (new Pointer(this));
+ } else if (m_pointer) {
+ m_oldPointers << m_pointer;
+ m_pointer = nullptr;
+ }
+
+ if (m_capabilities & capability_keyboard) {
+ if (!m_keyboard)
+ m_keyboard = (new Keyboard(this));
+ } else if (m_keyboard) {
+ m_oldKeyboards << m_keyboard;
+ m_keyboard = nullptr;
+ }
+
+ for (auto *resource : resourceMap())
+ wl_seat::send_capabilities(resource->handle, capabilities);
+}
+
+void Seat::seat_get_pointer(Resource *resource, uint32_t id)
+{
+ if (~m_capabilities & capability_pointer) {
+ qWarning() << "Client requested a wl_pointer without the capability being available."
+ << "This Could be a race condition when hotunplugging,"
+ << "but is most likely a client error";
+ Pointer *pointer = new Pointer(this);
+ pointer->add(resource->client(), id, resource->version());
+ // TODO: mark as destroyed
+ m_oldPointers << pointer;
+ return;
+ }
+ m_pointer->add(resource->client(), id, resource->version());
+}
+
+void Seat::seat_get_keyboard(QtWaylandServer::wl_seat::Resource *resource, uint32_t id)
+{
+ if (~m_capabilities & capability_pointer) {
+ qWarning() << "Client requested a wl_keyboard without the capability being available."
+ << "This Could be a race condition when hotunplugging,"
+ << "but is most likely a client error";
+ Keyboard *keyboard = new Keyboard(this);
+ keyboard->add(resource->client(), id, resource->version());
+ // TODO: mark as destroyed
+ m_oldKeyboards << keyboard;
+ return;
+ }
+ m_keyboard->add(resource->client(), id, resource->version());
+}
+
+Surface *Pointer::cursorSurface()
+{
+ return m_cursorRole ? m_cursorRole->m_surface : nullptr;
+}
+
+uint Pointer::sendEnter(Surface *surface, const QPointF &position)
+{
+ wl_fixed_t x = wl_fixed_from_double(position.x());
+ wl_fixed_t y = wl_fixed_from_double(position.y());
+
+ uint serial = m_seat->m_compositor->nextSerial();
+ m_enterSerials << serial;
+ m_cursorRole = nullptr; // According to the protocol, the pointer image is undefined after enter
+
+ wl_client *client = surface->resource()->client();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ wl_pointer::send_enter(r->handle, serial, surface->resource()->handle, x ,y);
+ return serial;
+}
+
+uint Pointer::sendLeave(Surface *surface)
+{
+ uint serial = m_seat->m_compositor->nextSerial();
+
+ wl_client *client = surface->resource()->client();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ wl_pointer::send_leave(r->handle, serial, surface->resource()->handle);
+ return serial;
+}
+
+// Make sure you call enter, frame etc. first
+void Pointer::sendMotion(wl_client *client, const QPointF &position)
+{
+ wl_fixed_t x = wl_fixed_from_double(position.x());
+ wl_fixed_t y = wl_fixed_from_double(position.y());
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_motion(r->handle, time, x, y);
+}
+
+// Make sure you call enter, frame etc. first
+uint Pointer::sendButton(wl_client *client, uint button, uint state)
+{
+ Q_ASSERT(state == button_state_pressed || state == button_state_released);
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ uint serial = m_seat->m_compositor->nextSerial();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_button(r->handle, serial, time, button, state);
+ return serial;
+}
+
+// Make sure you call enter, frame etc. first
+void Pointer::sendAxis(wl_client *client, axis axis, qreal value)
+{
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ wl_fixed_t val = wl_fixed_from_double(value);
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis(r->handle, time, axis, val);
+}
+
+void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
+{
+ Q_UNUSED(resource);
+ auto *s = fromResource<Surface>(surface);
+ QVERIFY(s);
+
+ if (s->m_role) {
+ m_cursorRole = CursorRole::fromSurface(s);
+ QVERIFY(m_cursorRole);
+ } else {
+ m_cursorRole = new CursorRole(s); //TODO: make sure we don't leak CursorRole
+ s->m_role = m_cursorRole;
+ }
+
+ // Directly checking the last serial would be racy, we may just have sent leaves/enters which
+ // the client hasn't yet seen. Instead just check if the serial matches an enter serial since
+ // the last time the client sent a set_cursor request.
+ QVERIFY(m_enterSerials.contains(serial));
+ while (m_enterSerials.first() < serial) { m_enterSerials.removeFirst(); }
+
+ m_hotspot = QPoint(hotspot_x, hotspot_y);
+ emit setCursor(serial);
+}
+
+uint Keyboard::sendEnter(Surface *surface)
+{
+ auto serial = m_seat->m_compositor->nextSerial();
+ wl_client *client = surface->resource()->client();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_enter(r->handle, serial, surface->resource()->handle, QByteArray());
+ return serial;
+}
+
+uint Keyboard::sendLeave(Surface *surface)
+{
+ auto serial = m_seat->m_compositor->nextSerial();
+ wl_client *client = surface->resource()->client();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_leave(r->handle, serial, surface->resource()->handle);
+ return serial;
+}
+
+uint Keyboard::sendKey(wl_client *client, uint key, uint state)
+{
+ Q_ASSERT(state == key_state_pressed || state == key_state_released);
+ auto time = m_seat->m_compositor->currentTimeMilliseconds();
+ uint serial = m_seat->m_compositor->nextSerial();
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_key(r->handle, serial, time, key, state);
+ return serial;
+}
+
+// Shm implementation
+Shm::Shm(CoreCompositor *compositor, QVector<format> formats, int version)
+ : QtWaylandServer::wl_shm(compositor->m_display, version)
+ , m_compositor(compositor)
+ , m_formats(formats)
+{
+ // Some formats are specified as mandatory
+ Q_ASSERT(m_formats.contains(format_argb8888));
+ Q_ASSERT(m_formats.contains(format_xrgb8888));
+}
+
+bool Shm::isClean()
+{
+// for (ShmPool *pool : qAsConst(m_pools)) {
+// //TODO: return false if not cursor buffer
+// if (pool->m_buffers.isEmpty()) {
+// return false;
+// }
+// }
+ return true;
+}
+
+void Shm::shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size)
+{
+ Q_UNUSED(fd);
+ Q_UNUSED(size);
+ auto *pool = new ShmPool(this, resource->client(), id, 1);
+ m_pools.append(pool);
+}
+
+ShmPool::ShmPool(Shm *shm, wl_client *client, int id, int version)
+ : QtWaylandServer::wl_shm_pool(client, id, version)
+ , m_shm(shm)
+{
+}
+
+void ShmPool::shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
+{
+ QSize size(width, height);
+ new ShmBuffer(offset, size, stride, Shm::format(format), resource->client(), id);
+}
+
+void ShmPool::shm_pool_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ bool removed = m_shm->m_pools.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
new file mode 100644
index 000000000..5cef476c8
--- /dev/null
+++ b/tests/auto/client/shared/coreprotocol.h
@@ -0,0 +1,373 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_COREPROTOCOL_H
+#define MOCKCOMPOSITOR_COREPROTOCOL_H
+
+#include "corecompositor.h"
+
+#include <qwayland-server-wayland.h>
+
+namespace MockCompositor {
+
+class WlCompositor;
+class Output;
+class Pointer;
+class Keyboard;
+class CursorRole;
+class ShmPool;
+class ShmBuffer;
+class DataDevice;
+
+class Buffer : public QObject, public QtWaylandServer::wl_buffer
+{
+ Q_OBJECT
+public:
+ explicit Buffer(wl_client *client, int id, int version)
+ : QtWaylandServer::wl_buffer(client, id, version)
+ {
+ }
+ virtual QSize size() const = 0;
+ bool m_destroyed = false;
+
+protected:
+ void buffer_destroy_resource(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ m_destroyed = true;
+ // The client side resource has been destroyed, but we keep this object because it may be
+ // be used as a reference by e.g. surface for the currently committed buffer so it's not
+ // yet safe to free it.
+
+ //TODO: The memory should be freed by its factory
+ }
+ void buffer_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+};
+
+class Callback : public QObject, public QtWaylandServer::wl_callback
+{
+ Q_OBJECT
+public:
+ explicit Callback(wl_client *client, int id, int version = 1)
+ : QtWaylandServer::wl_callback(client, id, version)
+ {
+ }
+ ~Callback() override { if (!m_destroyed) wl_resource_destroy(resource()->handle); }
+ void send_done(uint32_t data) = delete; // use state-tracking method below instead
+ void sendDone(uint data) { Q_ASSERT(!m_done); QtWaylandServer::wl_callback::send_done(data); m_done = true; }
+ void sendDoneAndDestroy(uint data) { sendDone(data); wl_resource_destroy(resource()->handle); }
+ bool m_done = false;
+ bool m_destroyed = false;
+protected:
+ void callback_destroy_resource(Resource *resource) override { Q_UNUSED(resource); m_destroyed = true; }
+};
+
+class SurfaceRole : public QObject {
+ Q_OBJECT
+};
+
+class Surface : public QObject, public QtWaylandServer::wl_surface
+{
+ Q_OBJECT
+public:
+ explicit Surface(WlCompositor *wlCompositor, wl_client *client, int id, int version)
+ : QtWaylandServer::wl_surface(client, id, version)
+ , m_wlCompositor(wlCompositor)
+ {
+ }
+ ~Surface() override { qDeleteAll(m_commits); } // TODO: maybe make sure buffers are released?
+ void sendFrameCallbacks();
+ void sendEnter(Output *output);
+ void send_enter(::wl_resource *output) = delete;
+ void sendLeave(Output *output);
+ void send_leave(::wl_resource *output) = delete;
+
+ WlCompositor *m_wlCompositor;
+ struct PerCommitData {
+ Callback *frame = nullptr;
+ QPoint attachOffset;
+ bool attached = false;
+ };
+ struct DoubleBufferedState {
+ PerCommitData commitSpecific;
+ Buffer *buffer = nullptr;
+ uint configureSerial = 0;
+ int bufferScale = 1;
+ } m_pending, m_committed;
+ QVector<DoubleBufferedState *> m_commits;
+ QVector<Callback *> m_waitingFrameCallbacks;
+ QVector<Output *> m_outputs;
+ SurfaceRole *m_role = nullptr;
+
+signals:
+ void attach(void *buffer, QPoint offset);
+ void commit();
+ void bufferCommitted();
+
+protected:
+ void surface_destroy_resource(Resource *resource) override;
+ void surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+ void surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y) override;
+ void surface_set_buffer_scale(Resource *resource, int32_t scale) override;
+ void surface_commit(Resource *resource) override;
+ void surface_frame(Resource *resource, uint32_t callback) override;
+};
+
+class WlCompositor : public Global, public QtWaylandServer::wl_compositor
+{
+ Q_OBJECT
+public:
+ explicit WlCompositor(CoreCompositor *compositor, int version = 3)
+ : QtWaylandServer::wl_compositor(compositor->m_display, version)
+ , m_compositor(compositor)
+ {}
+ bool isClean() override;
+ QString dirtyMessage() override;
+ QVector<Surface *> m_surfaces;
+ CoreCompositor *m_compositor = nullptr;
+
+signals:
+ void surfaceCreated(Surface *surface);
+
+protected:
+ void compositor_create_surface(Resource *resource, uint32_t id) override
+ {
+ auto *surface = new Surface(this, resource->client(), id, resource->version());
+ m_surfaces.append(surface);
+ emit surfaceCreated(surface);
+ }
+};
+
+class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor
+{
+ Q_OBJECT
+public:
+ explicit SubCompositor(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::wl_subcompositor(compositor->m_display, version)
+ {}
+ // TODO
+};
+
+struct OutputMode {
+ explicit OutputMode() = default;
+ explicit OutputMode(const QSize &resolution, int refreshRate = 60000)
+ : resolution(resolution), refreshRate(refreshRate)
+ {}
+ QSize resolution = QSize(1920, 1080);
+ int refreshRate = 60000; // In mHz
+ //TODO: flags (they're currently hard-coded)
+
+ // in mm
+ QSize physicalSizeForDpi(int dpi) { return (QSizeF(resolution) * 25.4 / dpi).toSize(); }
+};
+
+struct OutputData {
+ using Subpixel = QtWaylandServer::wl_output::subpixel;
+ using Transform = QtWaylandServer::wl_output::transform;
+ explicit OutputData() = default;
+
+ // for geometry event
+ QPoint position;
+ QSize physicalSize = QSize(0, 0); // means unknown physical size
+ QString make = "Make";
+ QString model = "Model";
+ Subpixel subpixel = Subpixel::subpixel_unknown;
+ Transform transform = Transform::transform_normal;
+
+ int scale = 1; // for scale event
+ OutputMode mode; // for mode event
+};
+
+class Output : public Global, public QtWaylandServer::wl_output
+{
+ Q_OBJECT
+public:
+ explicit Output(CoreCompositor *compositor, OutputData data = OutputData(), int version = 2)
+ : QtWaylandServer::wl_output(compositor->m_display, version)
+ , m_data(std::move(data))
+ , m_version(version)
+ {}
+
+ void send_geometry() = delete;
+ void sendGeometry();
+ void sendGeometry(Resource *resource); // Sends to only one client
+
+ void send_scale(int32_t factor) = delete;
+ void sendScale(int factor);
+ void sendScale(Resource *resource); // Sends current scale to only one client
+
+ void sendDone();
+
+ int scale() const { return m_data.scale; }
+
+ OutputData m_data;
+ int m_version = 1; // TODO: remove on libwayland upgrade
+
+protected:
+ void output_bind_resource(Resource *resource) override;
+};
+
+class Seat : public Global, public QtWaylandServer::wl_seat
+{
+ Q_OBJECT
+public:
+ explicit Seat(CoreCompositor *compositor, uint capabilities, int version = 4);
+ ~Seat() override;
+ void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
+ void send_capabilities(uint capabilities) = delete; // Use wrapper instead
+ void setCapabilities(uint capabilities);
+
+ CoreCompositor *m_compositor = nullptr;
+
+ Pointer* m_pointer = nullptr;
+ QVector<Pointer *> m_oldPointers;
+
+ Keyboard* m_keyboard = nullptr;
+ QVector<Keyboard *> m_oldKeyboards;
+
+ uint m_capabilities = 0;
+
+protected:
+ void seat_bind_resource(Resource *resource) override
+ {
+ wl_seat::send_capabilities(resource->handle, m_capabilities);
+ }
+
+ void seat_get_pointer(Resource *resource, uint32_t id) override;
+ void seat_get_keyboard(Resource *resource, uint32_t id) override;
+// void seat_get_touch(Resource *resource, uint32_t id) override;
+
+// void seat_release(Resource *resource) override;
+};
+
+class Pointer : public QObject, public QtWaylandServer::wl_pointer
+{
+ Q_OBJECT
+public:
+ explicit Pointer(Seat *seat) : m_seat(seat) {}
+ Surface *cursorSurface();
+ CursorRole* m_cursorRole = nullptr; //TODO: cleanup
+ void send_enter() = delete;
+ uint sendEnter(Surface *surface, const QPointF &position);
+ void send_leave() = delete;
+ uint sendLeave(Surface *surface);
+ void sendMotion(wl_client *client, const QPointF &position);
+ uint sendButton(wl_client *client, uint button, uint state);
+ void sendAxis(wl_client *client, axis axis, qreal value);
+
+ Seat *m_seat = nullptr;
+ QVector<uint> m_enterSerials;
+ QPoint m_hotspot;
+
+signals:
+ void setCursor(uint serial); //TODO: add arguments?
+
+protected:
+ void pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) override;
+ //TODO
+};
+
+class CursorRole : public SurfaceRole {
+ Q_OBJECT
+public:
+ explicit CursorRole(Surface *surface) // TODO: needs some more args
+ : m_surface(surface)
+ {
+ }
+ static CursorRole *fromSurface(Surface *surface) { return qobject_cast<CursorRole *>(surface->m_role); }
+ Surface *m_surface = nullptr;
+};
+
+class Keyboard : public QObject, public QtWaylandServer::wl_keyboard
+{
+ Q_OBJECT
+public:
+ explicit Keyboard(Seat *seat) : m_seat(seat) {}
+ //TODO: Keymap event
+ uint sendEnter(Surface *surface);
+ uint sendLeave(Surface *surface);
+ uint sendKey(wl_client *client, uint key, uint state);
+ Seat *m_seat = nullptr;
+};
+
+class Shm : public Global, public QtWaylandServer::wl_shm
+{
+ Q_OBJECT
+public:
+ explicit Shm(CoreCompositor *compositor, QVector<format> formats = {format_argb8888, format_xrgb8888, format_rgb888}, int version = 1);
+ bool isClean() override;
+ CoreCompositor *m_compositor = nullptr;
+ QVector<ShmPool *> m_pools;
+ const QVector<format> m_formats;
+
+protected:
+ void shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) override;
+ void shm_bind_resource(Resource *resource) override
+ {
+ for (auto format : qAsConst(m_formats))
+ send_format(resource->handle, format);
+ }
+};
+
+class ShmPool : QObject, public QtWaylandServer::wl_shm_pool
+{
+ Q_OBJECT
+public:
+ explicit ShmPool(Shm *shm, wl_client *client, int id, int version = 1);
+ Shm *m_shm = nullptr;
+ QVector<ShmBuffer *> m_buffers;
+
+protected:
+ void shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) override;
+ void shm_pool_destroy_resource(Resource *resource) override;
+ void shm_pool_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
+};
+
+class ShmBuffer : public Buffer
+{
+ Q_OBJECT
+public:
+ static ShmBuffer *fromBuffer(Buffer *buffer) { return qobject_cast<ShmBuffer *>(buffer); }
+ explicit ShmBuffer(int offset, const QSize &size, int stride, Shm::format format, wl_client *client, int id, int version = 1)
+ : Buffer(client, id, version)
+ , m_offset(offset)
+ , m_size(size)
+ , m_stride(stride)
+ , m_format(format)
+ {
+ }
+ QSize size() const override { return m_size; }
+ const int m_offset;
+ const QSize m_size;
+ const int m_stride;
+ const Shm::format m_format;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_COREPROTOCOL_H
diff --git a/tests/auto/client/shared/datadevice.cpp b/tests/auto/client/shared/datadevice.cpp
new file mode 100644
index 000000000..dfa18952c
--- /dev/null
+++ b/tests/auto/client/shared/datadevice.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "datadevice.h"
+
+namespace MockCompositor {
+
+bool DataDeviceManager::isClean()
+{
+ for (auto *device : qAsConst(m_dataDevices)) {
+ // The client should not leak selection offers, i.e. if this fails, there is a missing
+ // data_offer.destroy request
+ if (!device->m_sentSelectionOffers.empty())
+ return false;
+ }
+ return true;
+}
+
+DataDevice *DataDeviceManager::deviceFor(Seat *seat)
+{
+ Q_ASSERT(seat);
+ if (auto *device = m_dataDevices.value(seat, nullptr))
+ return device;
+
+ auto *device = new DataDevice(this, seat);
+ m_dataDevices[seat] = device;
+ return device;
+}
+
+void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seatResource)
+{
+ auto *seat = fromResource<Seat>(seatResource);
+ QVERIFY(seat);
+ auto *device = deviceFor(seat);
+ device->add(resource->client(), id, resource->version());
+}
+
+DataDevice::~DataDevice()
+{
+ // If the client(s) hasn't deleted the wayland object, just ignore subsequent events
+ for (auto *r : resourceMap())
+ wl_resource_set_implementation(r->handle, nullptr, nullptr, nullptr);
+}
+
+DataOffer *DataDevice::sendDataOffer(wl_client *client, const QStringList &mimeTypes)
+{
+ Q_ASSERT(client);
+ auto *offer = new DataOffer(this, client, m_manager->m_version);
+ for (auto *resource : resourceMap().values(client))
+ wl_data_device::send_data_offer(resource->handle, offer->resource()->handle);
+ for (const auto &mimeType : mimeTypes)
+ offer->send_offer(mimeType);
+ return offer;
+}
+
+void DataDevice::sendSelection(DataOffer *offer)
+{
+ auto *client = offer->resource()->client();
+ for (auto *resource : resourceMap().values(client))
+ wl_data_device::send_selection(resource->handle, offer->resource()->handle);
+ m_sentSelectionOffers << offer;
+}
+
+void DataOffer::data_offer_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+void DataOffer::data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd)
+{
+ Q_UNUSED(resource);
+ emit receive(mime_type, fd);
+}
+
+void DataOffer::data_offer_destroy(QtWaylandServer::wl_data_offer::Resource *resource)
+{
+ bool removed = m_dataDevice->m_sentSelectionOffers.removeOne(this);
+ QVERIFY(removed);
+ wl_resource_destroy(resource->handle);
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/datadevice.h b/tests/auto/client/shared/datadevice.h
new file mode 100644
index 000000000..a96da86f0
--- /dev/null
+++ b/tests/auto/client/shared/datadevice.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_DATADEVICE_H
+#define MOCKCOMPOSITOR_DATADEVICE_H
+
+//TODO: need this?
+#include "coreprotocol.h"
+
+namespace MockCompositor {
+
+class DataOffer;
+
+class DataDeviceManager : public Global, public QtWaylandServer::wl_data_device_manager
+{
+ Q_OBJECT
+public:
+ explicit DataDeviceManager(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::wl_data_device_manager(compositor->m_display, version)
+ , m_version(version)
+ {}
+ ~DataDeviceManager() override { qDeleteAll(m_dataDevices); }
+ bool isClean() override;
+ DataDevice *deviceFor(Seat *seat);
+
+ int m_version = 1; // TODO: remove on libwayland upgrade
+ QMap<Seat *, DataDevice *> m_dataDevices;
+
+protected:
+ void data_device_manager_get_data_device(Resource *resource, uint32_t id, ::wl_resource *seatResource) override;
+};
+
+class DataDevice : public QtWaylandServer::wl_data_device
+{
+public:
+ explicit DataDevice(DataDeviceManager *manager, Seat *seat)
+ : m_manager(manager)
+ , m_seat(seat)
+ {}
+ ~DataDevice() override;
+ void send_data_offer(::wl_resource *resource) = delete;
+ DataOffer *sendDataOffer(::wl_client *client, const QStringList &mimeTypes = {});
+ DataOffer *sendDataOffer(const QStringList &mimeTypes = {});
+
+ void send_selection(::wl_resource *resource) = delete;
+ void sendSelection(DataOffer *offer);
+
+ DataDeviceManager *m_manager = nullptr;
+ Seat *m_seat = nullptr;
+ QVector<DataOffer *> m_sentSelectionOffers;
+
+protected:
+ void data_device_release(Resource *resource) override
+ {
+ int removed = m_manager->m_dataDevices.remove(m_seat);
+ QVERIFY(removed);
+ wl_resource_destroy(resource->handle);
+ }
+};
+
+class DataOffer : public QObject, public QtWaylandServer::wl_data_offer
+{
+ Q_OBJECT
+public:
+ explicit DataOffer(DataDevice *dataDevice, ::wl_client *client, int version)
+ : QtWaylandServer::wl_data_offer (client, 0, version)
+ , m_dataDevice(dataDevice)
+ {}
+
+ DataDevice *m_dataDevice = nullptr;
+
+signals:
+ void receive(QString mimeType, int fd);
+
+protected:
+ void data_offer_destroy_resource(Resource *resource) override;
+ void data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) override;
+// void data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type) override;
+ void data_offer_destroy(Resource *resource) override;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_DATADEVICE_H
diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp
index 797c05c44..6b9af4295 100644
--- a/tests/auto/client/shared/mockcompositor.cpp
+++ b/tests/auto/client/shared/mockcompositor.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -27,477 +27,65 @@
****************************************************************************/
#include "mockcompositor.h"
-#include "mockinput.h"
-#include "mockoutput.h"
-#include "mocksurface.h"
-#include "mockwlshell.h"
-#include "mockxdgshellv6.h"
-#include "mockiviapplication.h"
-#include <wayland-xdg-shell-unstable-v6-server-protocol.h>
-
-#include <stdio.h>
-MockCompositor::MockCompositor()
-{
- pthread_create(&m_thread, 0, run, this);
-
- m_mutex.lock();
- m_waitCondition.wait(&m_mutex);
- m_mutex.unlock();
-}
-
-MockCompositor::~MockCompositor()
-{
- m_alive = false;
- m_waitCondition.wakeOne();
- pthread_join(m_thread, 0);
-}
-
-void MockCompositor::lock()
-{
- m_mutex.lock();
-}
-
-void MockCompositor::unlock()
-{
- m_mutex.unlock();
-}
-
-void MockCompositor::applicationInitialized()
-{
- m_ready = true;
-}
-
-int MockCompositor::waylandFileDescriptor() const
-{
- return m_compositor->fileDescriptor();
-}
-
-void MockCompositor::processWaylandEvents()
-{
- m_waitCondition.wakeOne();
-}
-
-void MockCompositor::setOutputMode(const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor);
- command.parameters << size;
- processCommand(command);
-}
-
-void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos)
-{
- Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor);
- command.parameters << QVariant::fromValue(surface) << pos;
- processCommand(command);
-}
-
-void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code)
-{
- Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor);
- command.parameters << QVariant::fromValue(surface) << code;
- processCommand(command);
-}
-
-void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code)
-{
- Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor);
- command.parameters << QVariant::fromValue(surface) << code;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor);
- command.parameters << QVariant::fromValue(surface) << position << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor);
- command.parameters << QVariant::fromValue(surface) << position << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor);
- command.parameters << QVariant::fromValue(surface) << id;
- processCommand(command);
-}
-
-void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor);
- command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceMotion(const QPoint &position)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor);
- command.parameters << QVariant::fromValue(position);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface)
-{
- Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- processCommand(command);
-}
-
-void MockCompositor::sendAddOutput()
-{
- Command command = makeCommand(Impl::Compositor::sendAddOutput, m_compositor);
- processCommand(command);
-}
-
-void MockCompositor::sendRemoveOutput(const QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendRemoveOutput, m_compositor);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry)
-{
- Command command = makeCommand(Impl::Compositor::sendOutputGeometry, m_compositor);
- command.parameters << QVariant::fromValue(output);
- command.parameters << QVariant::fromValue(geometry);
- processCommand(command);
-}
-
-void MockCompositor::sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendSurfaceEnter, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output)
-{
- Command command = makeCommand(Impl::Compositor::sendSurfaceLeave, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(output);
- processCommand(command);
-}
-
-void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor);
- command.parameters << QVariant::fromValue(surface);
- command.parameters << QVariant::fromValue(size);
- processCommand(command);
-}
-
-void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size)
-{
- Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor);
- command.parameters << QVariant::fromValue(iviSurface);
- command.parameters << QVariant::fromValue(size);
- processCommand(command);
-}
-
-void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states)
-{
- Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor);
- command.parameters << QVariant::fromValue(toplevel);
- command.parameters << QVariant::fromValue(size);
- QByteArray statesBytes(reinterpret_cast<const char *>(states.data()),
- states.size() * static_cast<int>(sizeof(uint)));
- command.parameters << statesBytes;
- processCommand(command);
-}
-
-void MockCompositor::waitForStartDrag()
-{
- Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor);
- processCommand(command);
-}
-
-QSharedPointer<MockSurface> MockCompositor::surface()
-{
- QSharedPointer<MockSurface> result;
- lock();
- QVector<Impl::Surface *> surfaces = m_compositor->surfaces();
- foreach (Impl::Surface *surface, surfaces) {
- // we don't want to mistake the cursor surface for a window surface
- if (surface->isMapped()) {
- result = surface->mockSurface();
- break;
- }
- }
- unlock();
- return result;
-}
-
-QSharedPointer<MockOutput> MockCompositor::output(int index)
-{
- QSharedPointer<MockOutput> result;
- lock();
- if (Impl::Output *output = m_compositor->outputs().value(index, nullptr))
- result = output->mockOutput();
- unlock();
- return result;
-}
-
-QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index)
-{
- QSharedPointer<MockIviSurface> result;
- lock();
- if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr))
- result = toplevel->mockIviSurface();
- unlock();
- return result;
-}
-
-QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index)
-{
- QSharedPointer<MockXdgToplevelV6> result;
- lock();
- if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr))
- result = toplevel->mockToplevel();
- unlock();
- return result;
-}
-
-MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target)
-{
- Command command;
- command.callback = callback;
- command.target = target;
- return command;
-}
-
-void MockCompositor::processCommand(const Command &command)
-{
- lock();
- m_commandQueue << command;
- unlock();
-
- m_waitCondition.wakeOne();
-}
-
-void MockCompositor::dispatchCommands()
-{
- lock();
- int count = m_commandQueue.length();
- unlock();
-
- for (int i = 0; i < count; ++i) {
- lock();
- const Command command = m_commandQueue.takeFirst();
- unlock();
- command.callback(command.target, command.parameters);
- }
-}
-
-void *MockCompositor::run(void *data)
-{
- MockCompositor *controller = static_cast<MockCompositor *>(data);
-
- Impl::Compositor compositor;
-
- controller->m_compositor = &compositor;
- controller->m_waitCondition.wakeOne();
-
- while (!controller->m_ready) {
- controller->dispatchCommands();
- compositor.dispatchEvents(20);
- }
-
- while (controller->m_alive) {
- {
- QMutexLocker locker(&controller->m_mutex);
- if (controller->m_commandQueue.isEmpty())
- controller->m_waitCondition.wait(&controller->m_mutex);
- }
- controller->dispatchCommands();
- compositor.dispatchEvents(20);
- }
-
- return 0;
-}
-
-namespace Impl {
-
-Compositor::Compositor()
- : m_display(wl_display_create())
-{
- if (wl_display_add_socket(m_display, 0)) {
- fprintf(stderr, "Fatal: Failed to open server socket\n");
- exit(EXIT_FAILURE);
+namespace MockCompositor {
+
+DefaultCompositor::DefaultCompositor()
+{
+ {
+ Lock l(this);
+
+ // Globals: Should ideally always be at least the latest versions we support.
+ // Legacy versions can override in separate tests by removing and adding.
+ add<WlCompositor>();
+ add<SubCompositor>();
+ auto *output = add<Output>();
+ output->m_data.physicalSize = output->m_data.mode.physicalSizeForDpi(96);
+ add<Seat>(Seat::capability_pointer | Seat::capability_keyboard);
+ add<XdgWmBase>();
+ add<Shm>();
+ // TODO: other shells, viewporter, xdgoutput etc
+
+ QObject::connect(get<WlCompositor>(), &WlCompositor::surfaceCreated, [&] (Surface *surface){
+ QObject::connect(surface, &Surface::bufferCommitted, [=] {
+ if (m_config.autoRelease) {
+ // Pretend we made a copy of the buffer and just release it immediately
+ surface->m_committed.buffer->send_release();
+ }
+ if (m_config.autoEnter && surface->m_outputs.empty())
+ surface->sendEnter(get<Output>());
+ wl_display_flush_clients(m_display);
+ });
+ });
+
+ QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, [&] (XdgToplevel *toplevel) {
+ // Needed because lambdas don't support Qt::DirectConnection
+ exec([&]{
+ if (m_config.autoConfigure)
+ toplevel->sendCompleteConfigure();
+ });
+ });
}
-
- wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor);
-
- m_data_device_manager.reset(new DataDeviceManager(this, m_display));
-
- wl_display_init_shm(m_display);
-
- m_seat.reset(new Seat(this, m_display));
- m_pointer = m_seat->pointer();
- m_keyboard = m_seat->keyboard();
- m_touch = m_seat->touch();
-
- m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0)));
- m_iviApplication.reset(new IviApplication(m_display));
- m_wlShell.reset(new WlShell(m_display));
- m_xdgShellV6.reset(new XdgShellV6(m_display));
-
- m_loop = wl_display_get_event_loop(m_display);
- m_fd = wl_event_loop_get_fd(m_loop);
-}
-
-Compositor::~Compositor()
-{
- wl_display_destroy(m_display);
+ Q_ASSERT(isClean());
}
-void Compositor::dispatchEvents(int timeout)
+uint DefaultCompositor::sendXdgShellPing()
{
- wl_display_flush_clients(m_display);
- wl_event_loop_dispatch(m_loop, timeout);
+ warnIfNotLockedByThread(Q_FUNC_INFO);
+ uint serial = nextSerial();
+ auto *base = get<XdgWmBase>();
+ const auto resourceMap = base->resourceMap();
+ Q_ASSERT(resourceMap.size() == 1); // binding more than once shouldn't be needed
+ base->send_ping(resourceMap.first()->handle, serial);
+ return serial;
}
-static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id)
+void DefaultCompositor::xdgPingAndWaitForPong()
{
- Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource));
- compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor));
+ QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
+ uint serial = exec([=] { return sendXdgShellPing(); });
+ QTRY_COMPARE(pongSpy.count(), 1);
+ QTRY_COMPARE(pongSpy.first().at(0).toUInt(), serial);
}
-static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id)
-{
- Q_UNUSED(client);
- Q_UNUSED(compositorResource);
- Q_UNUSED(id);
-}
-
-void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id)
-{
- static const struct wl_compositor_interface compositorInterface = {
- compositor_create_surface,
- compositor_create_region
- };
-
- wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id);
- wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr);
-}
-
-static void unregisterResourceCallback(wl_listener *listener, void *data)
-{
- struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data);
- wl_list_remove(wl_resource_get_link(resource));
- delete listener;
-}
-
-void registerResource(wl_list *list, wl_resource *resource)
-{
- wl_list_insert(list, wl_resource_get_link(resource));
-
- wl_listener *listener = new wl_listener;
- listener->notify = unregisterResourceCallback;
-
- wl_resource_add_destroy_listener(resource, listener);
-}
-
-QVector<Surface *> Compositor::surfaces() const
-{
- return m_surfaces;
-}
-
-QVector<Output *> Compositor::outputs() const
-{
- return m_outputs;
-}
-
-IviApplication *Compositor::iviApplication() const
-{
- return m_iviApplication.data();
-}
-
-XdgShellV6 *Compositor::xdgShellV6() const
-{
- return m_xdgShellV6.data();
-}
-
-uint32_t Compositor::nextSerial()
-{
- return wl_display_next_serial(m_display);
-}
-
-void Compositor::addSurface(Surface *surface)
-{
- m_surfaces << surface;
-}
-
-void Compositor::removeSurface(Surface *surface)
-{
- m_surfaces.removeOne(surface);
- m_keyboard->handleSurfaceDestroyed(surface);
- m_pointer->handleSurfaceDestroyed(surface);
-}
-
-Surface *Compositor::resolveSurface(const QVariant &v)
-{
- QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >();
- return mockSurface ? mockSurface->handle() : nullptr;
-}
-
-Output *Compositor::resolveOutput(const QVariant &v)
-{
- QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >();
- return mockOutput ? mockOutput->handle() : nullptr;
-}
-
-IviSurface *Compositor::resolveIviSurface(const QVariant &v)
-{
- QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>();
- return mockIviSurface ? mockIviSurface->handle() : nullptr;
-}
-
-XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v)
-{
- QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>();
- return mockToplevel ? mockToplevel->handle() : nullptr;
-}
-
-}
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 51b6f4bfb..75ef1eaea 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -29,259 +29,59 @@
#ifndef MOCKCOMPOSITOR_H
#define MOCKCOMPOSITOR_H
-#include "mockxdgshellv6.h"
-#include "mockiviapplication.h"
+#include "corecompositor.h"
+#include "coreprotocol.h"
+#include "datadevice.h"
+#include "xdgshell.h"
-#include <pthread.h>
-#include <qglobal.h>
-#include <wayland-server.h>
+#include <QtGui/QGuiApplication>
-#include <QImage>
-#include <QMutex>
-#include <QRect>
-#include <QSharedPointer>
-#include <QVariant>
-#include <QVector>
-#include <QWaitCondition>
-
-namespace Impl {
-
-typedef void (**Implementation)(void);
-
-class Keyboard;
-class Pointer;
-class Touch;
-class Seat;
-class DataDeviceManager;
-class Surface;
-class Output;
-class IviApplication;
-class WlShell;
-class XdgShellV6;
-
-class Compositor
-{
-public:
- Compositor();
- ~Compositor();
-
- int fileDescriptor() const { return m_fd; }
- void dispatchEvents(int timeout = 0);
-
- uint32_t nextSerial();
- uint32_t time() { return ++m_time; }
-
- QVector<Surface *> surfaces() const;
- QVector<Output *> outputs() const;
-
- IviApplication *iviApplication() const;
- XdgShellV6 *xdgShellV6() const;
-
- void addSurface(Surface *surface);
- void removeSurface(Surface *surface);
-
- static void setKeyboardFocus(void *data, const QList<QVariant> &parameters);
- static void sendMousePress(void *data, const QList<QVariant> &parameters);
- static void sendMouseRelease(void *data, const QList<QVariant> &parameters);
- static void sendKeyPress(void *data, const QList<QVariant> &parameters);
- static void sendKeyRelease(void *data, const QList<QVariant> &parameters);
- static void sendTouchDown(void *data, const QList<QVariant> &parameters);
- static void sendTouchUp(void *data, const QList<QVariant> &parameters);
- static void sendTouchMotion(void *data, const QList<QVariant> &parameters);
- static void sendTouchFrame(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDataOffer(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceEnter(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceMotion(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceDrop(void *data, const QList<QVariant> &parameters);
- static void sendDataDeviceLeave(void *data, const QList<QVariant> &parameters);
- static void waitForStartDrag(void *data, const QList<QVariant> &parameters);
- static void setOutputMode(void *compositor, const QList<QVariant> &parameters);
- static void sendAddOutput(void *data, const QList<QVariant> &parameters);
- static void sendRemoveOutput(void *data, const QList<QVariant> &parameters);
- static void sendOutputGeometry(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceEnter(void *data, const QList<QVariant> &parameters);
- static void sendSurfaceLeave(void *data, const QList<QVariant> &parameters);
- static void sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendIviSurfaceConfigure(void *data, const QList<QVariant> &parameters);
- static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> &parameters);
-
-public:
- bool m_startDragSeen = false;
-
-private:
- static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id);
- static Surface *resolveSurface(const QVariant &v);
- static Output *resolveOutput(const QVariant &v);
- static IviSurface *resolveIviSurface(const QVariant &v);
- static XdgToplevelV6 *resolveToplevel(const QVariant &v);
-
- void initShm();
-
- QRect m_outputGeometry;
-
- wl_display *m_display = nullptr;
- wl_event_loop *m_loop = nullptr;
- wl_shm *m_shm = nullptr;
- int m_fd = -1;
-
- uint32_t m_time = 0;
-
- QScopedPointer<Seat> m_seat;
- Pointer *m_pointer = nullptr;
- Keyboard *m_keyboard = nullptr;
- Touch *m_touch = nullptr;
- QScopedPointer<DataDeviceManager> m_data_device_manager;
- QVector<Surface *> m_surfaces;
- QVector<Output *> m_outputs;
- QScopedPointer<IviApplication> m_iviApplication;
- QScopedPointer<WlShell> m_wlShell;
- QScopedPointer<XdgShellV6> m_xdgShellV6;
-};
-
-void registerResource(wl_list *list, wl_resource *resource);
-
-}
-
-class MockSurface
-{
-public:
- Impl::Surface *handle() const { return m_surface; }
-
- QImage image;
-
-private:
- MockSurface(Impl::Surface *surface);
- friend class Impl::Compositor;
- friend class Impl::Surface;
-
- Impl::Surface *m_surface = nullptr;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockSurface>)
-
-class MockIviSurface
-{
-public:
- Impl::IviSurface *handle() const { return m_iviSurface; }
- const uint iviId;
-
-private:
- MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {}
- friend class Impl::Compositor;
- friend class Impl::IviSurface;
-
- Impl::IviSurface *m_iviSurface;
-};
+#ifndef BTN_LEFT
+// As defined in linux/input-event-codes.h
+#define BTN_LEFT 0x110
+#endif
-Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
+namespace MockCompositor {
-class MockXdgToplevelV6 : public QObject
+class DefaultCompositor : public CoreCompositor
{
- Q_OBJECT
-public:
- Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
-
- void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
-
-signals:
- uint setMinimizedRequested();
- uint setMaximizedRequested();
- uint unsetMaximizedRequested();
- uint setFullscreenRequested();
- uint unsetFullscreenRequested();
- void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event
-
-private:
- MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
- friend class Impl::Compositor;
- friend class Impl::XdgToplevelV6;
-
- Impl::XdgToplevelV6 *m_toplevel;
-};
-
-Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>)
-
-class MockOutput {
public:
- Impl::Output *handle() const { return m_output; }
- MockOutput(Impl::Output *output);
-private:
- Impl::Output *m_output = nullptr;
+ explicit DefaultCompositor();
+ // Convenience functions
+ Output *output(int i = 0) { return getAll<Output>().value(i, nullptr); }
+ Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.value(i, nullptr); }
+ XdgSurface *xdgSurface(int i = 0) { return get<XdgWmBase>()->m_xdgSurfaces.value(i, nullptr); }
+ XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); }
+ XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); }
+ Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; }
+ Surface *cursorSurface() { auto *p = pointer(); return p ? p->cursorSurface() : nullptr; }
+ Keyboard *keyboard() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_keyboard; }
+ uint sendXdgShellPing();
+ void xdgPingAndWaitForPong();
+ // Things that can be changed run-time without confusing the client (i.e. don't require separate tests)
+ struct Config {
+ bool autoEnter = true;
+ bool autoRelease = true;
+ bool autoConfigure = false;
+ } m_config;
+ void resetConfig() { exec([&] { m_config = Config{}; }); }
};
-Q_DECLARE_METATYPE(QSharedPointer<MockOutput>)
-
-class MockCompositor
-{
-public:
- MockCompositor();
- ~MockCompositor();
-
- void applicationInitialized();
-
- int waylandFileDescriptor() const;
- void processWaylandEvents();
-
- void setOutputMode(const QSize &size);
- void setKeyboardFocus(const QSharedPointer<MockSurface> &surface);
- void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos);
- void sendMouseRelease(const QSharedPointer<MockSurface> &surface);
- void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code);
- void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code);
- void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
- void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
- void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
- void sendDataDeviceMotion(const QPoint &position);
- void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
- void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
- void sendAddOutput();
- void sendRemoveOutput(const QSharedPointer<MockOutput> &output);
- void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry);
- void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
- void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
- void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
- void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
- const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
- void waitForStartDrag();
-
- QSharedPointer<MockSurface> surface();
- QSharedPointer<MockOutput> output(int index = 0);
- QSharedPointer<MockIviSurface> iviSurface(int index = 0);
- QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0);
-
- void lock();
- void unlock();
-
-private:
- struct Command
- {
- typedef void (*Callback)(void *target, const QList<QVariant> &parameters);
-
- Callback callback;
- void *target = nullptr;
- QList<QVariant> parameters;
- };
-
- static Command makeCommand(Command::Callback callback, void *target);
-
- void processCommand(const Command &command);
- void dispatchCommands();
-
- static void *run(void *data);
-
- bool m_alive = true;
- bool m_ready = false;
- pthread_t m_thread;
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
-
- Impl::Compositor *m_compositor = nullptr;
-
- QList<Command> m_commandQueue;
-};
+} // namespace MockCompositor
+
+#define QCOMPOSITOR_VERIFY(expr) QVERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_TRY_VERIFY(expr) QTRY_VERIFY(exec([&]{ return expr; }))
+#define QCOMPOSITOR_COMPARE(expr, expr2) QCOMPARE(exec([&]{ return expr; }), expr2)
+#define QCOMPOSITOR_TRY_COMPARE(expr, expr2) QTRY_COMPARE(exec([&]{ return expr; }), expr2)
+
+#define QCOMPOSITOR_TEST_MAIN(test) \
+int main(int argc, char **argv) \
+{ \
+ setenv("XDG_RUNTIME_DIR", ".", 1); \
+ setenv("QT_QPA_PLATFORM", "wayland", 1); \
+ test tc; \
+ QGuiApplication app(argc, argv); \
+ return QTest::qExec(&tc, argc, argv); \
+} \
#endif
diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri
index f3cb4d5a2..c86183b3d 100644
--- a/tests/auto/client/shared/shared.pri
+++ b/tests/auto/client/shared/shared.pri
@@ -1,31 +1,26 @@
-CONFIG += testcase link_pkgconfig
-QT += testlib
-QT += core-private gui-private waylandclient-private
+QT += testlib waylandclient-private
+CONFIG += testcase wayland-scanner
+QMAKE_USE += wayland-server
-QMAKE_USE += wayland-client wayland-server
-
-CONFIG += wayland-scanner
WAYLANDSERVERSOURCES += \
- ../../../../src/3rdparty/protocol/ivi-application.xml \
- ../../../../src/3rdparty/protocol/wayland.xml \
- ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml
+ $$PWD/../../../../src/3rdparty/protocol/wayland.xml \
+ $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml \
+ $$PWD/../../../../src/3rdparty/protocol/text-input-unstable-v2.xml
INCLUDEPATH += ../shared
-SOURCES += \
- ../shared/mockcompositor.cpp \
- ../shared/mockinput.cpp \
- ../shared/mockiviapplication.cpp \
- ../shared/mockwlshell.cpp \
- ../shared/mockxdgshellv6.cpp \
- ../shared/mocksurface.cpp \
- ../shared/mockoutput.cpp
-
HEADERS += \
- ../shared/mockcompositor.h \
- ../shared/mockinput.h \
- ../shared/mockiviapplication.h \
- ../shared/mockwlshell.h \
- ../shared/mockxdgshellv6.h \
- ../shared/mocksurface.h \
- ../shared/mockoutput.h
+ $$PWD/corecompositor.h \
+ $$PWD/coreprotocol.h \
+ $$PWD/datadevice.h \
+ $$PWD/mockcompositor.h \
+ $$PWD/xdgshell.h \
+ $$PWD/textinput.h
+
+SOURCES += \
+ $$PWD/corecompositor.cpp \
+ $$PWD/coreprotocol.cpp \
+ $$PWD/datadevice.cpp \
+ $$PWD/mockcompositor.cpp \
+ $$PWD/xdgshell.cpp \
+ $$PWD/textinput.cpp
diff --git a/tests/auto/client/shared/textinput.cpp b/tests/auto/client/shared/textinput.cpp
new file mode 100644
index 000000000..f9fd287bb
--- /dev/null
+++ b/tests/auto/client/shared/textinput.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "textinput.h"
+
+namespace MockCompositor {
+
+TextInputManager::TextInputManager(CoreCompositor *compositor)
+{
+ init(compositor->m_display, 1);
+}
+
+void TextInputManager::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seatResource)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(id);
+ Q_UNUSED(seatResource);
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/textinput.h b/tests/auto/client/shared/textinput.h
new file mode 100644
index 000000000..85072e74b
--- /dev/null
+++ b/tests/auto/client/shared/textinput.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_TEXTINPUT_H
+#define MOCKCOMPOSITOR_TEXTINPUT_H
+
+#include "coreprotocol.h"
+#include <qwayland-server-text-input-unstable-v2.h>
+
+#include <QtGui/qpa/qplatformnativeinterface.h>
+
+namespace MockCompositor {
+
+class TextInputManager : public Global, public QtWaylandServer::zwp_text_input_manager_v2
+{
+ Q_OBJECT
+public:
+ TextInputManager(CoreCompositor *compositor);
+
+protected:
+ void zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_TEXTINPUT_H
diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp
new file mode 100644
index 000000000..13acc01e2
--- /dev/null
+++ b/tests/auto/client/shared/xdgshell.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "xdgshell.h"
+
+namespace MockCompositor {
+
+XdgWmBase::XdgWmBase(CoreCompositor *compositor, int version)
+ : QtWaylandServer::xdg_wm_base(compositor->m_display, version)
+ , m_compositor(compositor)
+{
+}
+
+XdgToplevel *XdgWmBase::toplevel(int i)
+{
+ int j = 0;
+ for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) {
+ if (auto *toplevel = xdgSurface->m_toplevel) {
+ if (j == i)
+ return toplevel;
+ ++j;
+ }
+ }
+ return nullptr;
+}
+
+XdgPopup *XdgWmBase::popup(int i)
+{
+ int j = 0;
+ for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) {
+ if (auto *popup = xdgSurface->m_popup) {
+ if (j == i)
+ return popup;
+ ++j;
+ }
+ }
+ return nullptr;
+}
+
+void XdgWmBase::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surface)
+{
+ auto *s = fromResource<Surface>(surface);
+ auto *xdgSurface = new XdgSurface(this, s, resource->client(), id, resource->version());
+ m_xdgSurfaces << xdgSurface;
+ emit xdgSurfaceCreated(xdgSurface);
+}
+
+void XdgWmBase::xdg_wm_base_pong(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ emit pong(serial);
+}
+
+XdgSurface::XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version)
+ : QtWaylandServer::xdg_surface(client, id, version)
+ , m_xdgWmBase(xdgWmBase)
+ , m_surface(surface)
+{
+ QVERIFY(!surface->m_pending.buffer);
+ QVERIFY(!surface->m_committed.buffer);
+ connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated);
+ connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured);
+ connect(surface, &Surface::commit, this, [this] {
+ m_committed = m_pending;
+
+ if (m_ackedConfigureSerial != m_committedConfigureSerial) {
+ m_committedConfigureSerial = m_ackedConfigureSerial;
+ emit configureCommitted(m_committedConfigureSerial);
+ }
+ });
+}
+
+void XdgSurface::sendConfigure(uint serial)
+{
+ Q_ASSERT(serial);
+ m_pendingConfigureSerials.append(serial);
+ m_configureSent = true;
+ xdg_surface::send_configure(serial);
+}
+
+uint XdgSurface::sendConfigure()
+{
+ const uint serial = m_xdgWmBase->m_compositor->nextSerial();
+ sendConfigure(serial);
+ return serial;
+}
+
+void XdgSurface::xdg_surface_get_toplevel(Resource *resource, uint32_t id)
+{
+ QVERIFY(!m_toplevel);
+ QVERIFY(!m_popup);
+ m_toplevel = new XdgToplevel(this, id, resource->version());
+ emit toplevelCreated(m_toplevel);
+}
+
+void XdgSurface::xdg_surface_get_popup(Resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner)
+{
+ Q_UNUSED(positioner);
+ QVERIFY(!m_toplevel);
+ QVERIFY(!m_popup);
+ auto *p = fromResource<XdgSurface>(parent);
+ m_popup = new XdgPopup(this, p, id, resource->version());
+}
+
+void XdgSurface::xdg_surface_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ bool removed = m_xdgWmBase->m_xdgSurfaces.removeOne(this);
+ Q_ASSERT(removed);
+ delete this;
+}
+
+void XdgSurface::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource)
+{
+ QVERIFY(m_popups.empty());
+ wl_resource_destroy(resource->handle);
+}
+
+void XdgSurface::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+ QRect rect(x, y, width, height);
+ QVERIFY(rect.isValid());
+ m_pending.windowGeometry = rect;
+}
+
+void XdgSurface::xdg_surface_ack_configure(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ QVERIFY2(m_pendingConfigureSerials.contains(serial), qPrintable(QString::number(serial)));
+ m_ackedConfigureSerial = serial;
+ while (!m_pendingConfigureSerials.empty()) {
+ uint s = m_pendingConfigureSerials.takeFirst();
+ if (s == serial)
+ return;
+ }
+}
+
+XdgToplevel::XdgToplevel(XdgSurface *xdgSurface, int id, int version)
+ : QtWaylandServer::xdg_toplevel(xdgSurface->resource()->client(), id, version)
+ , m_xdgSurface(xdgSurface)
+{
+ connect(surface(), &Surface::commit, this, [this] { m_committed = m_pending; });
+}
+
+void XdgToplevel::sendConfigure(const QSize &size, const QVector<uint> &states)
+{
+ send_configure(size.width(), size.height(), toByteArray(states));
+}
+
+uint XdgToplevel::sendCompleteConfigure(const QSize &size, const QVector<uint> &states)
+{
+ sendConfigure(size, states);
+ return m_xdgSurface->sendConfigure();
+}
+
+void XdgToplevel::xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+ QSize size(width, height);
+ QVERIFY(size.isValid());
+ m_pending.maxSize = size;
+}
+
+void XdgToplevel::xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+ QSize size(width, height);
+ QVERIFY(size.isValid());
+ m_pending.minSize = size;
+}
+
+XdgPopup::XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version)
+ : QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version)
+ , m_xdgSurface(xdgSurface)
+ , m_parentXdgSurface(parent)
+{
+ Q_ASSERT(m_xdgSurface);
+ Q_ASSERT(m_parentXdgSurface);
+ m_parentXdgSurface->m_popups << this;
+}
+
+void XdgPopup::sendConfigure(const QRect &geometry)
+{
+ send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height());
+}
+
+uint XdgPopup::sendCompleteConfigure(const QRect &geometry)
+{
+ sendConfigure(geometry);
+ return m_xdgSurface->sendConfigure();
+}
+
+void XdgPopup::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(seat); // TODO: verify correct seat as well
+ QVERIFY(!m_grabbed);
+ QVERIFY(m_parentXdgSurface->isValidPopupGrabParent());
+ m_xdgSurface->m_xdgWmBase->m_topmostGrabbingPopup = this;
+ m_grabbed = true;
+ m_grabSerial = serial;
+}
+
+void XdgPopup::xdg_popup_destroy(Resource *resource) {
+ Q_UNUSED(resource);
+ if (m_grabbed) {
+ auto *base = m_xdgSurface->m_xdgWmBase;
+ QCOMPARE(base->m_topmostGrabbingPopup, this);
+ base->m_topmostGrabbingPopup = this->m_parentXdgSurface->m_popup;
+ }
+ m_xdgSurface->m_popup = nullptr;
+ m_parentXdgSurface->m_popups.removeAll(this);
+ emit destroyRequested();
+}
+
+} // namespace MockCompositor
diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h
new file mode 100644
index 000000000..618babde7
--- /dev/null
+++ b/tests/auto/client/shared/xdgshell.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_XDGSHELL_H
+#define MOCKCOMPOSITOR_XDGSHELL_H
+
+#include "coreprotocol.h"
+#include <qwayland-server-xdg-shell.h>
+
+namespace MockCompositor {
+
+class XdgSurface;
+class XdgToplevel;
+class XdgPopup;
+using XdgPositioner = QtWaylandServer::xdg_positioner;
+
+class XdgWmBase : public Global, public QtWaylandServer::xdg_wm_base
+{
+ Q_OBJECT
+public:
+ explicit XdgWmBase(CoreCompositor *compositor, int version = 1);
+ using QtWaylandServer::xdg_wm_base::send_ping;
+ void send_ping(uint32_t) = delete; // It's a global, use resource specific instead
+ bool isClean() override { return m_xdgSurfaces.empty(); }
+ QString dirtyMessage() override { return m_xdgSurfaces.empty() ? "clean" : "remaining xdg surfaces"; }
+ QVector<XdgSurface *> m_xdgSurfaces;
+ XdgToplevel *toplevel(int i = 0);
+ XdgPopup *popup(int i = 0);
+ XdgPopup *m_topmostGrabbingPopup = nullptr;
+ CoreCompositor *m_compositor = nullptr;
+
+signals:
+ void pong(uint serial);
+ void xdgSurfaceCreated(XdgSurface *xdgSurface);
+ void toplevelCreated(XdgToplevel *toplevel);
+
+protected:
+ void xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override;
+ void xdg_wm_base_pong(Resource *resource, uint32_t serial) override;
+ void xdg_wm_base_create_positioner(Resource *resource, uint32_t id) override
+ {
+ new XdgPositioner(resource->client(), id, resource->version());
+ }
+};
+
+class XdgSurface : public QObject, public QtWaylandServer::xdg_surface
+{
+ Q_OBJECT
+public:
+ explicit XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version);
+ void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state
+ void sendConfigure(uint serial);
+ uint sendConfigure();
+ bool isTopmostGrabbingPopup() const { return m_popup && m_xdgWmBase->m_topmostGrabbingPopup == m_popup; }
+ bool isValidPopupGrabParent() const { return isTopmostGrabbingPopup() || (m_toplevel && !m_xdgWmBase->m_topmostGrabbingPopup); }
+
+ // Role objects
+ XdgToplevel *m_toplevel = nullptr;
+ XdgPopup *m_popup = nullptr;
+
+ XdgWmBase *m_xdgWmBase = nullptr;
+ Surface *m_surface = nullptr;
+ bool m_configureSent = false;
+ QVector<uint> m_pendingConfigureSerials;
+ uint m_ackedConfigureSerial = 0;
+ uint m_committedConfigureSerial = 0;
+ struct DoubleBufferedState {
+ QRect windowGeometry = {0, 0, 0, 0};
+ } m_pending, m_committed;
+ QVector<XdgPopup *> m_popups;
+
+public slots:
+ void verifyConfigured() { QVERIFY(m_configureSent); }
+
+signals:
+ void configureCommitted(uint);
+ void toplevelCreated(XdgToplevel *toplevel);
+
+protected:
+ void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override;
+ void xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parent, ::wl_resource *positioner) override;
+ void xdg_surface_destroy_resource(Resource *resource) override;
+ void xdg_surface_destroy(Resource *resource) override;
+ void xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override;
+ void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override;
+};
+
+class XdgToplevel : public QObject, public QtWaylandServer::xdg_toplevel
+{
+ Q_OBJECT
+public:
+ explicit XdgToplevel(XdgSurface *xdgSurface, int id, int version = 1);
+ void sendConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {});
+ uint sendCompleteConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {});
+ Surface *surface() { return m_xdgSurface->m_surface; }
+
+ XdgSurface *m_xdgSurface = nullptr;
+ struct DoubleBufferedState {
+ QSize minSize = {0, 0};
+ QSize maxSize = {0, 0};
+ } m_pending, m_committed;
+
+protected:
+ void xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) override;
+ void xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) override;
+};
+
+class XdgPopup : public QObject, public QtWaylandServer::xdg_popup
+{
+ Q_OBJECT
+public:
+ explicit XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version = 1);
+ void sendConfigure(const QRect &geometry);
+ uint sendCompleteConfigure(const QRect &geometry);
+ Surface *surface() { return m_xdgSurface->m_surface; }
+ XdgSurface *m_xdgSurface = nullptr;
+ XdgSurface *m_parentXdgSurface = nullptr;
+ bool m_grabbed = false;
+ uint m_grabSerial = 0;
+signals:
+ void destroyRequested();
+protected:
+ void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override;
+ void xdg_popup_destroy(Resource *resource) override;
+};
+
+} // namespace MockCompositor
+
+#endif // MOCKCOMPOSITOR_XDGSHELL_H
diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp
new file mode 100644
index 000000000..0dfaef5ea
--- /dev/null
+++ b/tests/auto/client/shared_old/mockcompositor.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include "mockinput.h"
+#include "mockoutput.h"
+#include "mocksurface.h"
+#include "mockwlshell.h"
+#include "mockxdgshellv6.h"
+#include "mockiviapplication.h"
+
+#include <wayland-xdg-shell-unstable-v6-server-protocol.h>
+
+#include <stdio.h>
+MockCompositor::MockCompositor()
+{
+ pthread_create(&m_thread, 0, run, this);
+
+ m_mutex.lock();
+ m_waitCondition.wait(&m_mutex);
+ m_mutex.unlock();
+}
+
+MockCompositor::~MockCompositor()
+{
+ m_alive = false;
+ m_waitCondition.wakeOne();
+ pthread_join(m_thread, 0);
+}
+
+void MockCompositor::lock()
+{
+ m_mutex.lock();
+}
+
+void MockCompositor::unlock()
+{
+ m_mutex.unlock();
+}
+
+void MockCompositor::applicationInitialized()
+{
+ m_ready = true;
+}
+
+int MockCompositor::waylandFileDescriptor() const
+{
+ return m_compositor->fileDescriptor();
+}
+
+void MockCompositor::processWaylandEvents()
+{
+ m_waitCondition.wakeOne();
+}
+
+void MockCompositor::setOutputMode(const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor);
+ command.parameters << size;
+ processCommand(command);
+}
+
+void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos)
+{
+ Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << pos;
+ processCommand(command);
+}
+
+void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code)
+{
+ Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << code;
+ processCommand(command);
+}
+
+void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code)
+{
+ Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << code;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << position << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << position << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << id;
+ processCommand(command);
+}
+
+void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor);
+ command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceMotion(const QPoint &position)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor);
+ command.parameters << QVariant::fromValue(position);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface)
+{
+ Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ processCommand(command);
+}
+
+void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor);
+ command.parameters << QVariant::fromValue(surface);
+ command.parameters << QVariant::fromValue(size);
+ processCommand(command);
+}
+
+void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size)
+{
+ Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor);
+ command.parameters << QVariant::fromValue(iviSurface);
+ command.parameters << QVariant::fromValue(size);
+ processCommand(command);
+}
+
+void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states)
+{
+ Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor);
+ command.parameters << QVariant::fromValue(toplevel);
+ command.parameters << QVariant::fromValue(size);
+ QByteArray statesBytes(reinterpret_cast<const char *>(states.data()),
+ states.size() * static_cast<int>(sizeof(uint)));
+ command.parameters << statesBytes;
+ processCommand(command);
+}
+
+void MockCompositor::waitForStartDrag()
+{
+ Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor);
+ processCommand(command);
+}
+
+QSharedPointer<MockSurface> MockCompositor::surface()
+{
+ QSharedPointer<MockSurface> result;
+ lock();
+ {
+ QVector<Impl::Surface *> surfaces = m_compositor->surfaces();
+ foreach (Impl::Surface *surface, surfaces) {
+ // we don't want to mistake the cursor surface for a window surface
+ if (surface->isMapped()) {
+ result = surface->mockSurface();
+ break;
+ }
+ }
+ }
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockOutput> MockCompositor::output(int index)
+{
+ QSharedPointer<MockOutput> result;
+ lock();
+ if (Impl::Output *output = m_compositor->outputs().value(index, nullptr))
+ result = output->mockOutput();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index)
+{
+ QSharedPointer<MockIviSurface> result;
+ lock();
+ if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr))
+ result = toplevel->mockIviSurface();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index)
+{
+ QSharedPointer<MockXdgToplevelV6> result;
+ lock();
+ if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr))
+ result = toplevel->mockToplevel();
+ unlock();
+ return result;
+}
+
+QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index)
+{
+ QSharedPointer<MockSurface> result;
+ lock();
+ if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr))
+ result = surface->mockSurface();
+ unlock();
+ return result;
+}
+
+MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target)
+{
+ Command command;
+ command.callback = callback;
+ command.target = target;
+ return command;
+}
+
+void MockCompositor::processCommand(const Command &command)
+{
+ lock();
+ m_commandQueue << command;
+ unlock();
+
+ m_waitCondition.wakeOne();
+}
+
+void MockCompositor::dispatchCommands()
+{
+ lock();
+ int count = m_commandQueue.length();
+ unlock();
+
+ for (int i = 0; i < count; ++i) {
+ lock();
+ const Command command = m_commandQueue.takeFirst();
+ unlock();
+ command.callback(command.target, command.parameters);
+ }
+}
+
+void *MockCompositor::run(void *data)
+{
+ MockCompositor *controller = static_cast<MockCompositor *>(data);
+
+ Impl::Compositor compositor(controller);
+
+ controller->m_compositor = &compositor;
+ controller->m_waitCondition.wakeOne();
+
+ while (!controller->m_ready) {
+ controller->dispatchCommands();
+ compositor.dispatchEvents(20);
+ }
+
+ while (controller->m_alive) {
+ {
+ QMutexLocker locker(&controller->m_mutex);
+ if (controller->m_commandQueue.isEmpty())
+ controller->m_waitCondition.wait(&controller->m_mutex);
+ }
+ controller->dispatchCommands();
+ compositor.dispatchEvents(20);
+ }
+
+ return 0;
+}
+
+namespace Impl {
+
+Compositor::Compositor(MockCompositor *mockCompositor)
+ : m_mockCompositor(mockCompositor), m_display(wl_display_create())
+{
+ if (wl_display_add_socket(m_display, 0)) {
+ fprintf(stderr, "Fatal: Failed to open server socket\n");
+ exit(EXIT_FAILURE);
+ }
+
+ wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor);
+
+ m_data_device_manager.reset(new DataDeviceManager(this, m_display));
+
+ wl_display_init_shm(m_display);
+
+ m_seat.reset(new Seat(this, m_display));
+ m_pointer = m_seat->pointer();
+ m_keyboard = m_seat->keyboard();
+ m_touch = m_seat->touch();
+
+ m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0)));
+ m_iviApplication.reset(new IviApplication(m_display));
+ m_wlShell.reset(new WlShell(m_display));
+ m_xdgShellV6.reset(new XdgShellV6(m_display));
+ m_fullScreenShellV1.reset(new FullScreenShellV1(m_display));
+
+ m_loop = wl_display_get_event_loop(m_display);
+ m_fd = wl_event_loop_get_fd(m_loop);
+}
+
+Compositor::~Compositor()
+{
+ wl_display_destroy(m_display);
+}
+
+void Compositor::dispatchEvents(int timeout)
+{
+ wl_display_flush_clients(m_display);
+ wl_event_loop_dispatch(m_loop, timeout);
+}
+
+static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id)
+{
+ Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource));
+ compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor));
+}
+
+static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id)
+{
+ Q_UNUSED(client);
+ Q_UNUSED(compositorResource);
+ Q_UNUSED(id);
+}
+
+void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id)
+{
+ static const struct wl_compositor_interface compositorInterface = {
+ compositor_create_surface,
+ compositor_create_region
+ };
+
+ wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id);
+ wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr);
+}
+
+static void unregisterResourceCallback(wl_listener *listener, void *data)
+{
+ struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data);
+ wl_list_remove(wl_resource_get_link(resource));
+ delete listener;
+}
+
+void registerResource(wl_list *list, wl_resource *resource)
+{
+ wl_list_insert(list, wl_resource_get_link(resource));
+
+ wl_listener *listener = new wl_listener;
+ listener->notify = unregisterResourceCallback;
+
+ wl_resource_add_destroy_listener(resource, listener);
+}
+
+QVector<Surface *> Compositor::surfaces() const
+{
+ return m_surfaces;
+}
+
+QVector<Output *> Compositor::outputs() const
+{
+ return m_outputs;
+}
+
+IviApplication *Compositor::iviApplication() const
+{
+ return m_iviApplication.data();
+}
+
+XdgShellV6 *Compositor::xdgShellV6() const
+{
+ return m_xdgShellV6.data();
+}
+
+FullScreenShellV1 *Compositor::fullScreenShellV1() const
+{
+ return m_fullScreenShellV1.data();
+}
+
+uint32_t Compositor::nextSerial()
+{
+ return wl_display_next_serial(m_display);
+}
+
+void Compositor::addSurface(Surface *surface)
+{
+ m_mockCompositor->lock();
+ m_surfaces << surface;
+ m_mockCompositor->unlock();
+}
+
+void Compositor::removeSurface(Surface *surface)
+{
+ m_mockCompositor->lock();
+ m_surfaces.removeOne(surface);
+ m_keyboard->handleSurfaceDestroyed(surface);
+ m_pointer->handleSurfaceDestroyed(surface);
+ m_fullScreenShellV1->removeSurface(surface);
+ m_mockCompositor->unlock();
+}
+
+Surface *Compositor::resolveSurface(const QVariant &v)
+{
+ QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >();
+ return mockSurface ? mockSurface->handle() : nullptr;
+}
+
+Output *Compositor::resolveOutput(const QVariant &v)
+{
+ QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >();
+ return mockOutput ? mockOutput->handle() : nullptr;
+}
+
+IviSurface *Compositor::resolveIviSurface(const QVariant &v)
+{
+ QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>();
+ return mockIviSurface ? mockIviSurface->handle() : nullptr;
+}
+
+XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v)
+{
+ QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>();
+ return mockToplevel ? mockToplevel->handle() : nullptr;
+}
+
+}
diff --git a/tests/auto/client/shared_old/mockcompositor.h b/tests/auto/client/shared_old/mockcompositor.h
new file mode 100644
index 000000000..2433ac005
--- /dev/null
+++ b/tests/auto/client/shared_old/mockcompositor.h
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKCOMPOSITOR_H
+#define MOCKCOMPOSITOR_H
+
+#include "mockxdgshellv6.h"
+#include "mockiviapplication.h"
+#include "mockfullscreenshellv1.h"
+
+#include <pthread.h>
+#include <qglobal.h>
+#include <wayland-server-core.h>
+
+#include <QImage>
+#include <QMutex>
+#include <QRect>
+#include <QSharedPointer>
+#include <QVariant>
+#include <QVector>
+#include <QWaitCondition>
+
+class MockCompositor;
+
+namespace Impl {
+
+typedef void (**Implementation)(void);
+
+class Keyboard;
+class Pointer;
+class Touch;
+class Seat;
+class DataDeviceManager;
+class Surface;
+class Output;
+class IviApplication;
+class WlShell;
+class XdgShellV6;
+
+class Compositor
+{
+public:
+ Compositor(MockCompositor *mockCompositor);
+ ~Compositor();
+
+ int fileDescriptor() const { return m_fd; }
+ void dispatchEvents(int timeout = 0);
+
+ uint32_t nextSerial();
+ uint32_t time() { return ++m_time; }
+
+ QVector<Surface *> surfaces() const;
+ QVector<Output *> outputs() const;
+
+ IviApplication *iviApplication() const;
+ XdgShellV6 *xdgShellV6() const;
+ FullScreenShellV1 *fullScreenShellV1() const;
+
+ void addSurface(Surface *surface);
+ void removeSurface(Surface *surface);
+
+ static void setKeyboardFocus(void *data, const QList<QVariant> &parameters);
+ static void sendMousePress(void *data, const QList<QVariant> &parameters);
+ static void sendMouseRelease(void *data, const QList<QVariant> &parameters);
+ static void sendKeyPress(void *data, const QList<QVariant> &parameters);
+ static void sendKeyRelease(void *data, const QList<QVariant> &parameters);
+ static void sendTouchDown(void *data, const QList<QVariant> &parameters);
+ static void sendTouchUp(void *data, const QList<QVariant> &parameters);
+ static void sendTouchMotion(void *data, const QList<QVariant> &parameters);
+ static void sendTouchFrame(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceDataOffer(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceEnter(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceMotion(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceDrop(void *data, const QList<QVariant> &parameters);
+ static void sendDataDeviceLeave(void *data, const QList<QVariant> &parameters);
+ static void waitForStartDrag(void *data, const QList<QVariant> &parameters);
+ static void setOutputMode(void *compositor, const QList<QVariant> &parameters);
+ static void sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters);
+ static void sendIviSurfaceConfigure(void *data, const QList<QVariant> &parameters);
+ static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> &parameters);
+
+public:
+ bool m_startDragSeen = false;
+
+private:
+ static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id);
+ static Surface *resolveSurface(const QVariant &v);
+ static Output *resolveOutput(const QVariant &v);
+ static IviSurface *resolveIviSurface(const QVariant &v);
+ static XdgToplevelV6 *resolveToplevel(const QVariant &v);
+
+ void initShm();
+
+ MockCompositor *m_mockCompositor = nullptr;
+ QRect m_outputGeometry;
+
+ wl_display *m_display = nullptr;
+ wl_event_loop *m_loop = nullptr;
+ int m_fd = -1;
+
+ uint32_t m_time = 0;
+
+ QScopedPointer<Seat> m_seat;
+ Pointer *m_pointer = nullptr;
+ Keyboard *m_keyboard = nullptr;
+ Touch *m_touch = nullptr;
+ QScopedPointer<DataDeviceManager> m_data_device_manager;
+ QVector<Surface *> m_surfaces;
+ QVector<Output *> m_outputs;
+ QScopedPointer<IviApplication> m_iviApplication;
+ QScopedPointer<WlShell> m_wlShell;
+ QScopedPointer<XdgShellV6> m_xdgShellV6;
+ QScopedPointer<FullScreenShellV1> m_fullScreenShellV1;
+};
+
+void registerResource(wl_list *list, wl_resource *resource);
+
+}
+
+class MockSurface
+{
+public:
+ Impl::Surface *handle() const { return m_surface; }
+
+ QImage image;
+
+private:
+ MockSurface(Impl::Surface *surface);
+ friend class Impl::Compositor;
+ friend class Impl::Surface;
+
+ Impl::Surface *m_surface = nullptr;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockSurface>)
+
+class MockIviSurface
+{
+public:
+ Impl::IviSurface *handle() const { return m_iviSurface; }
+ const uint iviId;
+
+private:
+ MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {}
+ friend class Impl::Compositor;
+ friend class Impl::IviSurface;
+
+ Impl::IviSurface *m_iviSurface;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>)
+
+class MockXdgToplevelV6 : public QObject
+{
+ Q_OBJECT
+public:
+ Impl::XdgToplevelV6 *handle() const { return m_toplevel; }
+
+ void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel);
+
+signals:
+ uint setMinimizedRequested();
+ uint setMaximizedRequested();
+ uint unsetMaximizedRequested();
+ uint setFullscreenRequested();
+ uint unsetFullscreenRequested();
+ void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event
+
+private:
+ MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {}
+ friend class Impl::Compositor;
+ friend class Impl::XdgToplevelV6;
+
+ Impl::XdgToplevelV6 *m_toplevel;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>)
+
+class MockOutput {
+public:
+ Impl::Output *handle() const { return m_output; }
+ MockOutput(Impl::Output *output);
+private:
+ Impl::Output *m_output = nullptr;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer<MockOutput>)
+
+class MockCompositor
+{
+public:
+ MockCompositor();
+ ~MockCompositor();
+
+ void applicationInitialized();
+
+ int waylandFileDescriptor() const;
+ void processWaylandEvents();
+
+ void setOutputMode(const QSize &size);
+ void setKeyboardFocus(const QSharedPointer<MockSurface> &surface);
+ void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos);
+ void sendMouseRelease(const QSharedPointer<MockSurface> &surface);
+ void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code);
+ void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code);
+ void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
+ void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id);
+ void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
+ void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
+ void sendDataDeviceMotion(const QPoint &position);
+ void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
+ void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
+ void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
+ void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output);
+ void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0));
+ void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size);
+ void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0),
+ const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED });
+ void waitForStartDrag();
+
+ QSharedPointer<MockSurface> surface();
+ QSharedPointer<MockOutput> output(int index = 0);
+ QSharedPointer<MockIviSurface> iviSurface(int index = 0);
+ QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0);
+ QSharedPointer<MockSurface> fullScreenShellV1Surface(int index = 0);
+
+ void lock();
+ void unlock();
+
+private:
+ struct Command
+ {
+ typedef void (*Callback)(void *target, const QList<QVariant> &parameters);
+
+ Callback callback;
+ void *target = nullptr;
+ QList<QVariant> parameters;
+ };
+
+ static Command makeCommand(Command::Callback callback, void *target);
+
+ void processCommand(const Command &command);
+ void dispatchCommands();
+
+ static void *run(void *data);
+
+ bool m_alive = true;
+ bool m_ready = false;
+ pthread_t m_thread;
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+
+ Impl::Compositor *m_compositor = nullptr;
+
+ QList<Command> m_commandQueue;
+};
+
+#endif
diff --git a/tests/auto/client/shared_old/mockfullscreenshellv1.cpp b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp
new file mode 100644
index 000000000..22c49cde6
--- /dev/null
+++ b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockfullscreenshellv1.h"
+#include "mocksurface.h"
+
+namespace Impl {
+
+void FullScreenShellV1::zwp_fullscreen_shell_v1_present_surface(Resource *resource, struct ::wl_resource *surface, uint32_t method, struct ::wl_resource *output)
+{
+ Q_UNUSED(resource)
+ Q_UNUSED(method)
+ Q_UNUSED(output)
+
+ m_surfaces.append(Surface::fromResource(surface));
+}
+
+} // namespace Impl
diff --git a/tests/auto/client/shared_old/mockfullscreenshellv1.h b/tests/auto/client/shared_old/mockfullscreenshellv1.h
new file mode 100644
index 000000000..819bbc186
--- /dev/null
+++ b/tests/auto/client/shared_old/mockfullscreenshellv1.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MOCKFULLSCREENSHELLV1_H
+#define MOCKFULLSCREENSHELLV1_H
+
+#include <qwayland-server-fullscreen-shell-unstable-v1.h>
+
+#include <QVector>
+
+namespace Impl {
+
+class Surface;
+class FullScreenShellV1;
+
+class FullScreenShellV1 : public QtWaylandServer::zwp_fullscreen_shell_v1
+{
+public:
+ explicit FullScreenShellV1(::wl_display *display) : zwp_fullscreen_shell_v1(display, 1) {}
+
+ QVector<Surface *> surfaces() const { return m_surfaces; }
+ void removeSurface(Surface *surface) { m_surfaces.removeOne(surface); }
+
+protected:
+ void zwp_fullscreen_shell_v1_present_surface(Resource *resource, struct ::wl_resource *surface, uint32_t method, struct ::wl_resource *output) override;
+
+private:
+ QVector<Surface *> m_surfaces;
+};
+
+} // namespace Impl
+
+#endif // MOCKFULLSCREENSHELLV1_H
diff --git a/tests/auto/client/shared/mockinput.cpp b/tests/auto/client/shared_old/mockinput.cpp
index 8b7592824..8b7592824 100644
--- a/tests/auto/client/shared/mockinput.cpp
+++ b/tests/auto/client/shared_old/mockinput.cpp
diff --git a/tests/auto/client/shared/mockinput.h b/tests/auto/client/shared_old/mockinput.h
index d9adb3621..d9adb3621 100644
--- a/tests/auto/client/shared/mockinput.h
+++ b/tests/auto/client/shared_old/mockinput.h
diff --git a/tests/auto/client/shared/mockiviapplication.cpp b/tests/auto/client/shared_old/mockiviapplication.cpp
index 29a308993..29a308993 100644
--- a/tests/auto/client/shared/mockiviapplication.cpp
+++ b/tests/auto/client/shared_old/mockiviapplication.cpp
diff --git a/tests/auto/client/shared/mockiviapplication.h b/tests/auto/client/shared_old/mockiviapplication.h
index 4d65eeaba..4d65eeaba 100644
--- a/tests/auto/client/shared/mockiviapplication.h
+++ b/tests/auto/client/shared_old/mockiviapplication.h
diff --git a/tests/auto/client/shared/mockoutput.cpp b/tests/auto/client/shared_old/mockoutput.cpp
index 13e0524ad..cb7285959 100644
--- a/tests/auto/client/shared/mockoutput.cpp
+++ b/tests/auto/client/shared_old/mockoutput.cpp
@@ -33,37 +33,6 @@
namespace Impl {
-void Compositor::sendAddOutput(void *data, const QList<QVariant> &parameters) {
- Q_UNUSED(parameters);
- Compositor *compositor = static_cast<Compositor *>(data);
- auto output = new Output(compositor->m_display, QSize(1920, 1200), QPoint(0, 0));
- compositor->m_outputs.append(output);
-
- // Wait for the client to bind to the output
- while (output->resourceMap().isEmpty())
- compositor->dispatchEvents();
-}
-
-void Compositor::sendRemoveOutput(void *data, const QList<QVariant> &parameters) {
- Compositor *compositor = static_cast<Compositor *>(data);
- Q_ASSERT(compositor);
- Output *output = resolveOutput(parameters.first());
- Q_ASSERT(output);
- bool wasRemoved = compositor->m_outputs.removeOne(output);
- Q_ASSERT(wasRemoved);
- delete output;
-}
-
-void Compositor::sendOutputGeometry(void *data, const QList<QVariant> &parameters)
-{
- Compositor *compositor = static_cast<Compositor *>(data);
- Q_ASSERT(compositor);
- Output *output = resolveOutput(parameters.first());
- Q_ASSERT(output);
- QRect geometry = parameters.at(1).toRect();
- output->sendGeometryAndMode(geometry);
-}
-
void Compositor::setOutputMode(void *data, const QList<QVariant> &parameters)
{
Compositor *compositor = static_cast<Compositor *>(data);
@@ -91,17 +60,6 @@ void Output::setCurrentMode(const QSize &size)
}
}
-void Output::sendGeometryAndMode(const QRect &geometry)
-{
- m_size = geometry.size();
- m_position = geometry.topLeft();
- for (Resource *resource : resourceMap()) {
- sendGeometry(resource);
- sendCurrentMode(resource);
- send_done(resource->handle);
- }
-}
-
void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource)
{
sendGeometry(resource);
diff --git a/tests/auto/client/shared/mockoutput.h b/tests/auto/client/shared_old/mockoutput.h
index 9f261d5d7..d5a2bb56b 100644
--- a/tests/auto/client/shared/mockoutput.h
+++ b/tests/auto/client/shared_old/mockoutput.h
@@ -44,7 +44,6 @@ public:
QSharedPointer<MockOutput> mockOutput() const { return m_mockOutput; }
void setCurrentMode(const QSize &size);
- void sendGeometryAndMode(const QRect &geometry);
protected:
void output_bind_resource(Resource *resource) override;
diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp
index 84dcda6b0..81a5edbd0 100644
--- a/tests/auto/client/shared/mocksurface.cpp
+++ b/tests/auto/client/shared_old/mocksurface.cpp
@@ -35,34 +35,6 @@
namespace Impl {
-void Compositor::sendSurfaceEnter(void *data, const QList<QVariant> &parameters)
-{
- Q_UNUSED(data);
- Surface *surface = resolveSurface(parameters.at(0));
- Output *output = resolveOutput(parameters.at(1));
- Q_ASSERT(surface && surface->resource());
- Q_ASSERT(output);
- auto outputResources = output->resourceMap().values(surface->resource()->client());
- Q_ASSERT(!outputResources.isEmpty());
-
- for (auto outputResource : outputResources)
- surface->send_enter(outputResource->handle);
-}
-
-void Compositor::sendSurfaceLeave(void *data, const QList<QVariant> &parameters)
-{
- Q_UNUSED(data);
- Surface *surface = resolveSurface(parameters.at(0));
- Output *output = resolveOutput(parameters.at(1));
- Q_ASSERT(surface && surface->resource());
- Q_ASSERT(output);
- auto outputResources = output->resourceMap().values(surface->resource()->client());
- Q_ASSERT(!outputResources.isEmpty());
-
- for (auto outputResource : outputResources)
- surface->send_leave(outputResource->handle);
-}
-
void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> &parameters)
{
Compositor *compositor = static_cast<Compositor *>(data);
diff --git a/tests/auto/client/shared/mocksurface.h b/tests/auto/client/shared_old/mocksurface.h
index 949dc23dd..949dc23dd 100644
--- a/tests/auto/client/shared/mocksurface.h
+++ b/tests/auto/client/shared_old/mocksurface.h
diff --git a/tests/auto/client/shared/mockwlshell.cpp b/tests/auto/client/shared_old/mockwlshell.cpp
index 50e539932..50e539932 100644
--- a/tests/auto/client/shared/mockwlshell.cpp
+++ b/tests/auto/client/shared_old/mockwlshell.cpp
diff --git a/tests/auto/client/shared/mockwlshell.h b/tests/auto/client/shared_old/mockwlshell.h
index 3da586ca8..3da586ca8 100644
--- a/tests/auto/client/shared/mockwlshell.h
+++ b/tests/auto/client/shared_old/mockwlshell.h
diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared_old/mockxdgshellv6.cpp
index 05eff74ad..05eff74ad 100644
--- a/tests/auto/client/shared/mockxdgshellv6.cpp
+++ b/tests/auto/client/shared_old/mockxdgshellv6.cpp
diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared_old/mockxdgshellv6.h
index a238fa562..a238fa562 100644
--- a/tests/auto/client/shared/mockxdgshellv6.h
+++ b/tests/auto/client/shared_old/mockxdgshellv6.h
diff --git a/tests/auto/client/shared_old/shared_old.pri b/tests/auto/client/shared_old/shared_old.pri
new file mode 100644
index 000000000..467e98115
--- /dev/null
+++ b/tests/auto/client/shared_old/shared_old.pri
@@ -0,0 +1,34 @@
+CONFIG += testcase link_pkgconfig
+QT += testlib
+QT += core-private gui-private waylandclient-private
+
+QMAKE_USE += wayland-client wayland-server
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += \
+ ../../../../src/3rdparty/protocol/ivi-application.xml \
+ ../../../../src/3rdparty/protocol/wayland.xml \
+ ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml \
+ ../../../../src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml
+
+INCLUDEPATH += ../shared_old
+
+SOURCES += \
+ ../shared_old/mockcompositor.cpp \
+ ../shared_old/mockfullscreenshellv1.cpp \
+ ../shared_old/mockinput.cpp \
+ ../shared_old/mockiviapplication.cpp \
+ ../shared_old/mockwlshell.cpp \
+ ../shared_old/mockxdgshellv6.cpp \
+ ../shared_old/mocksurface.cpp \
+ ../shared_old/mockoutput.cpp
+
+HEADERS += \
+ ../shared_old/mockcompositor.h \
+ ../shared_old/mockfullscreenshellv1.h \
+ ../shared_old/mockinput.h \
+ ../shared_old/mockiviapplication.h \
+ ../shared_old/mockwlshell.h \
+ ../shared_old/mockxdgshellv6.h \
+ ../shared_old/mocksurface.h \
+ ../shared_old/mockoutput.h
diff --git a/tests/auto/client/surface/surface.pro b/tests/auto/client/surface/surface.pro
new file mode 100644
index 000000000..36882aa2d
--- /dev/null
+++ b/tests/auto/client/surface/surface.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_surface
+SOURCES += tst_surface.cpp
+
diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp
new file mode 100644
index 000000000..dddff0866
--- /dev/null
+++ b/tests/auto/client/surface/tst_surface.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+class tst_surface : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void createDestroySurface();
+ void waitForFrameCallbackRaster();
+ void waitForFrameCallbackGl();
+ void negotiateShmFormat();
+};
+
+void tst_surface::createDestroySurface()
+{
+ QWindow window;
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(surface());
+
+ window.destroy();
+ QCOMPOSITOR_TRY_VERIFY(!surface());
+}
+
+void tst_surface::waitForFrameCallbackRaster()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ class TestWindow : public QRasterWindow {
+ public:
+ explicit TestWindow() { resize(40, 40); }
+ void paintEvent(QPaintEvent *event) override
+ {
+ Q_UNUSED(event);
+ update();
+ }
+ };
+ TestWindow window;
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ // We should get the first buffer without waiting for a frame callback
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+
+ // Make sure we follow frame callbacks for some frames
+ for (int i = 0; i < 5; ++i) {
+ xdgPingAndWaitForPong(); // Make sure things have happened on the client
+ exec([&] {
+ QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived
+ QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty());
+ xdgToplevel()->surface()->sendFrameCallbacks();
+ });
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+ }
+}
+
+void tst_surface::waitForFrameCallbackGl()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ class TestWindow : public QOpenGLWindow {
+ public:
+ explicit TestWindow()
+ {
+ resize(40, 40);
+ connect(this, &QOpenGLWindow::frameSwapped,
+ this, QOverload<>::of(&QPaintDeviceWindow::update));
+ update();
+ }
+ void paintGL() override
+ {
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ };
+ TestWindow window;
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ // We should get the first buffer without waiting for a frame callback
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+
+ // Make sure we follow frame callbacks for some frames
+ for (int i = 0; i < 5; ++i) {
+ xdgPingAndWaitForPong(); // Make sure things have happened on the client
+ exec([&] {
+ QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived
+ QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty());
+ xdgToplevel()->surface()->sendFrameCallbacks();
+ });
+ QTRY_COMPARE(bufferSpy.count(), 1);
+ bufferSpy.removeFirst();
+ }
+}
+
+void tst_surface::negotiateShmFormat()
+{
+ QSKIP("TODO: I'm not sure why we're choosing xrgb over argb in this case...");
+ QRasterWindow window;
+ window.setFlag(Qt::FramelessWindowHint); // decorations force alpha
+ QSurfaceFormat format;
+ format.setAlphaBufferSize(0);
+ window.setFormat(format);
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted);
+ const uint serial = exec([=] { return xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, serial);
+ exec([&] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ auto *shmBuffer = ShmBuffer::fromBuffer(buffer);
+ QVERIFY(shmBuffer);
+ qDebug() << "shmBuffer->m_format" << shmBuffer->m_format;
+ QCOMPARE(shmBuffer->m_format, Shm::format_xrgb8888);
+ });
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_surface)
+#include "tst_surface.moc"
diff --git a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
new file mode 100644
index 000000000..a628810d1
--- /dev/null
+++ b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+#include <QtGui/QScreen>
+
+#include <qwayland-server-xdg-output-unstable-v1.h>
+
+using namespace MockCompositor;
+
+// TODO: move to shared folder?
+class XdgOutputV1 : public QObject, public QtWaylandServer::zxdg_output_v1
+{
+public:
+ explicit XdgOutputV1(Output *output)
+ : m_output(output)
+ , m_logicalGeometry(m_output->m_data.position, QSize(m_output->m_data.mode.resolution / m_output->m_data.scale))
+ , m_name(QString("WL-%1").arg(s_nextId++))
+ {}
+
+ void addResource(wl_client *client, int id, int version)
+ {
+ auto *resource = add(client, id, version)->handle;
+ send_logical_size(resource, m_logicalGeometry.width(), m_logicalGeometry.height());
+ send_logical_position(resource, m_logicalGeometry.x(), m_logicalGeometry.y());
+ if (version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)
+ send_name(resource, m_name);
+ if (version >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION)
+ send_description(resource, m_description);
+ send_done(resource);
+ }
+ Output *m_output = nullptr;
+ QRect m_logicalGeometry;
+ QString m_name;
+ QString m_description = "This is an Xdg Output description";
+ static int s_nextId;
+};
+
+int XdgOutputV1::s_nextId = 1;
+
+class XdgOutputManagerV1 : public Global, public QtWaylandServer::zxdg_output_manager_v1
+{
+ Q_OBJECT
+public:
+ explicit XdgOutputManagerV1(CoreCompositor *compositor, int version = 2)
+ : QtWaylandServer::zxdg_output_manager_v1(compositor->m_display, version)
+ , m_version(version)
+ {}
+ int m_version = 1; // TODO: remove on libwayland upgrade
+ QMap<Output *, XdgOutputV1 *> m_xdgOutputs;
+ XdgOutputV1 *getXdgOutput(Output *output)
+ {
+ if (auto *xdgOutput = m_xdgOutputs.value(output))
+ return xdgOutput;
+ return m_xdgOutputs[output] = new XdgOutputV1(output); // TODO: free memory
+ }
+
+protected:
+ void zxdg_output_manager_v1_get_xdg_output(Resource *resource, uint32_t id, wl_resource *outputResource) override
+ {
+ auto *output = fromResource<Output>(outputResource);
+ auto *xdgOutput = getXdgOutput(output);
+ xdgOutput->addResource(resource->client(), id, resource->version());
+ }
+};
+
+class XdgOutputV1Compositor : public DefaultCompositor {
+public:
+ explicit XdgOutputV1Compositor()
+ {
+ exec([this] {
+ int version = 2; // version 2 of of unstable-v1
+ add<XdgOutputManagerV1>(version);
+ });
+ }
+ XdgOutputV1 *xdgOutput(int i = 0) { return get<XdgOutputManagerV1>()->getXdgOutput(output(i)); }
+};
+
+class tst_xdgoutput : public QObject, private XdgOutputV1Compositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup();
+ void primaryScreen();
+ void overrideGeometry();
+};
+
+void tst_xdgoutput::cleanup()
+{
+ QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // Only the default output should be left
+ QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
+}
+
+void tst_xdgoutput::primaryScreen()
+{
+ // Verify that the client has bound to the global
+ QCOMPOSITOR_TRY_COMPARE(get<XdgOutputManagerV1>()->resourceMap().size(), 1);
+ exec([=] {
+ auto *resource = xdgOutput()->resourceMap().value(client());
+ QCOMPARE(resource->version(), 2);
+ });
+ auto *s = QGuiApplication::primaryScreen();
+ QTRY_COMPARE(s->size(), QSize(1920, 1080));
+ QTRY_COMPARE(s->geometry().topLeft(), QPoint(0, 0));
+ QTRY_COMPARE(s->name(), QString("WL-1"));
+}
+
+void tst_xdgoutput::overrideGeometry()
+{
+ exec([=] {
+ auto *output = add<Output>();
+ auto *xdgOutput = get<XdgOutputManagerV1>()->getXdgOutput(output);
+ xdgOutput->m_logicalGeometry = QRect(10, 20, 800, 1200);
+ });
+
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
+ auto *s = QGuiApplication::screens()[1];
+
+ QTRY_COMPARE(s->size(), QSize(800, 1200));
+ QTRY_COMPARE(s->geometry().topLeft(), QPoint(10, 20));
+
+ exec([=] { remove(output(1)); });
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_xdgoutput)
+#include "tst_xdgoutput.moc"
diff --git a/tests/auto/client/xdgoutput/xdgoutput.pro b/tests/auto/client/xdgoutput/xdgoutput.pro
new file mode 100644
index 000000000..3cfbb6a76
--- /dev/null
+++ b/tests/auto/client/xdgoutput/xdgoutput.pro
@@ -0,0 +1,8 @@
+include (../shared/shared.pri)
+
+WAYLANDSERVERSOURCES += \
+ $$PWD/../../../../src/3rdparty/protocol/xdg-output-unstable-v1.xml
+
+TARGET = tst_xdgoutput
+SOURCES += tst_xdgoutput.cpp
+
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp
new file mode 100644
index 000000000..9b18abdc3
--- /dev/null
+++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp
@@ -0,0 +1,483 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+
+using namespace MockCompositor;
+
+class tst_xdgshell : public QObject, private DefaultCompositor
+{
+ Q_OBJECT
+private slots:
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void showMinimized();
+ void basicConfigure();
+ void configureSize();
+ void configureStates();
+ void popup();
+ void tooltipOnPopup();
+ void switchPopups();
+ void pongs();
+ void minMaxSize();
+ void windowGeometry();
+};
+
+void tst_xdgshell::showMinimized()
+{
+ QSKIP("TODO: This currently fails, needs a fix");
+ // On xdg-shell there's really no way for the compositor to tell the window if it's minimized
+ // There are wl_surface.enter events and so on, but there's really no way to differentiate
+ // between a window preview and an unminimized window.
+ QWindow window;
+ window.showMinimized();
+ QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until
+ QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged
+
+ // Make sure the window on the compositor side is/was created here, and not after the test
+ // finishes, as that may mess up for later tests.
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface());
+ QVERIFY(!window.isExposed());
+}
+
+void tst_xdgshell::basicConfigure()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+
+ QTRY_VERIFY(window.isVisible());
+ // The window should not be exposed before the first xdg_surface configure event
+ QTRY_VERIFY(!window.isExposed());
+
+ exec([=] {
+ xdgToplevel()->sendConfigure({0, 0}, {}); // Let the window decide the size
+ });
+
+ // Nothing should happen before the *xdg_surface* configure
+ QTRY_VERIFY(!window.isExposed()); //Window should not be exposed before the first configure event
+ QVERIFY(configureSpy.isEmpty());
+
+ const uint serial = exec([=] { return nextSerial(); });
+
+ exec([=] {
+ xdgSurface()->sendConfigure(serial);
+ });
+
+ // Finally, we're exposed
+ QTRY_VERIFY(window.isExposed());
+
+ // The client is now going to ack the configure
+ QTRY_COMPARE(configureSpy.count(), 1);
+ QCOMPARE(configureSpy.takeFirst().at(0).toUInt(), serial);
+
+ // And attach a buffer
+ exec([&] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), window.frameGeometry().size());
+ });
+}
+
+void tst_xdgshell::configureSize()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+
+ const QSize configureSize(60, 40);
+
+ exec([=] {
+ xdgToplevel()->sendCompleteConfigure(configureSize);
+ });
+
+ QTRY_COMPARE(configureSpy.count(), 1);
+
+ exec([=] {
+ Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), configureSize);
+ });
+}
+
+void tst_xdgshell::configureStates()
+{
+ QRasterWindow window;
+ window.resize(64, 48);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ const QSize windowedSize(320, 240);
+ const uint windowedSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(windowedSize, { XdgToplevel::state_activated });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, windowedSerial);
+ QCOMPARE(window.visibility(), QWindow::Windowed);
+ QCOMPARE(window.windowStates(), Qt::WindowNoState);
+ QCOMPARE(window.frameGeometry().size(), windowedSize);
+ // Toplevel windows don't know their position on xdg-shell
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue);
+// QVERIFY(window.isActive());
+ QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly
+
+ const QSize screenSize(640, 480);
+ const uint maximizedSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_maximized });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, maximizedSerial);
+ QCOMPARE(window.visibility(), QWindow::Maximized);
+ QCOMPARE(window.windowStates(), Qt::WindowMaximized);
+ QCOMPARE(window.frameGeometry().size(), screenSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+ const uint fullscreenSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_fullscreen });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, fullscreenSerial);
+ QCOMPARE(window.visibility(), QWindow::FullScreen);
+ QCOMPARE(window.windowStates(), Qt::WindowFullScreen);
+ QCOMPARE(window.frameGeometry().size(), screenSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+
+ // The window should remember its original size
+ const uint restoreSerial = exec([=] {
+ return xdgToplevel()->sendCompleteConfigure({0, 0}, { XdgToplevel::state_activated });
+ });
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, restoreSerial);
+ QCOMPARE(window.visibility(), QWindow::Windowed);
+ QCOMPARE(window.windowStates(), Qt::WindowNoState);
+ QCOMPARE(window.frameGeometry().size(), windowedSize);
+// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
+}
+
+void tst_xdgshell::popup()
+{
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override
+ {
+ QRasterWindow::mousePressEvent(event);
+ m_popup.reset(new QRasterWindow);
+ m_popup->setTransientParent(this);
+ m_popup->setFlags(Qt::Popup);
+ m_popup->resize(100, 100);
+ m_popup->show();
+ }
+ QScopedPointer<QRasterWindow> m_popup;
+ };
+ Window window;
+ window.resize(200, 200);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QSignalSpy toplevelConfigureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted);
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QTRY_COMPARE(toplevelConfigureSpy.count(), 1);
+
+ uint clickSerial = exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+ return serial;
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ });
+
+ QTRY_VERIFY(window.m_popup);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ QSignalSpy popupConfigureSpy(exec([=] { return xdgPopup()->m_xdgSurface; }), &XdgSurface::configureCommitted);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed);
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup()->m_grabSerial, clickSerial);
+
+ QRasterWindow *popup = window.m_popup.get();
+ QVERIFY(!popup->isExposed()); // wait for configure
+
+ //TODO: Verify it works with a different configure window geometry
+ exec([=] { xdgPopup()->sendConfigure(QRect(100, 100, 100, 100)); });
+
+ // Nothing should happen before the *xdg_surface* configure
+ QTRY_VERIFY(!popup->isExposed()); // Popup shouldn't be exposed before the first configure event
+ QVERIFY(popupConfigureSpy.isEmpty());
+
+ const uint configureSerial = exec([=] {
+ return xdgPopup()->m_xdgSurface->sendConfigure();
+ });
+
+ // Finally, we're exposed
+ QTRY_VERIFY(popup->isExposed());
+
+ // The client is now going to ack the configure
+ QTRY_COMPARE(popupConfigureSpy.count(), 1);
+ QCOMPARE(popupConfigureSpy.takeFirst().at(0).toUInt(), configureSerial);
+
+ // And attach a buffer
+ exec([&] {
+ Buffer *buffer = xdgPopup()->surface()->m_committed.buffer;
+ QVERIFY(buffer);
+ QCOMPARE(buffer->size(), popup->frameGeometry().size());
+ });
+}
+
+void tst_xdgshell::tooltipOnPopup()
+{
+ class Popup : public QRasterWindow {
+ public:
+ explicit Popup(QWindow *parent) {
+ setTransientParent(parent);
+ setFlags(Qt::Popup);
+ resize(100, 100);
+ show();
+ }
+ void mousePressEvent(QMouseEvent *event) override {
+ QRasterWindow::mousePressEvent(event);
+ m_tooltip = new QRasterWindow;
+ m_tooltip->setTransientParent(this);
+ m_tooltip->setFlags(Qt::ToolTip);
+ m_tooltip->resize(100, 100);
+ m_tooltip->show();
+ }
+ QWindow *m_tooltip = nullptr;
+ };
+
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override {
+ QRasterWindow::mousePressEvent(event);
+ m_popup = new Popup(this);
+ }
+ Popup *m_popup = nullptr;
+ };
+
+ Window window;
+ window.resize(200, 200);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+// p->sendFrame();
+ p->sendLeave(surface);
+// p->sendFrame();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed);
+
+ exec([=] {
+ auto *surface = xdgPopup()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame();
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+// p->sendFrame();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1));
+ exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial);
+ QCOMPOSITOR_TRY_VERIFY(!xdgPopup(1)->m_grabbed);
+
+ // Close the middle popup (according protocol this should also destroy any nested popups)
+ window.m_popup->close();
+
+ // Close order is verified in XdgSurface::xdg_surface_destroy
+
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
+}
+
+// QTBUG-65680
+void tst_xdgshell::switchPopups()
+{
+ class Popup : public QRasterWindow {
+ public:
+ explicit Popup(QWindow *parent) {
+ setTransientParent(parent);
+ setFlags(Qt::Popup);
+ resize(10, 10);
+ show();
+ }
+ };
+
+ class Window : public QRasterWindow {
+ public:
+ void mousePressEvent(QMouseEvent *event) override {
+ QRasterWindow::mousePressEvent(event);
+ if (!m_popups.empty())
+ m_popups.last()->setVisible(false);
+ }
+ // The bug this checks for, is the case where there is a flushWindowSystemEvents() call
+ // somewhere within setVisible(false) before the grab has been released.
+ // This leads to the the release event below—including its show() call—to be handled
+ // At a time where there is still an active grab, making it illegal for the new popup to
+ // grab.
+ void mouseReleaseEvent(QMouseEvent *event) override {
+ QRasterWindow::mouseReleaseEvent(event);
+ m_popups << new Popup(this);
+ }
+ ~Window() override { qDeleteAll(m_popups); }
+ QVector<Popup *> m_popups;
+ };
+
+ Window window;
+ window.resize(200, 200);
+ window.show();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
+
+ exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame(); //TODO: uncomment when we support seat v5
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+// p->sendFrame();
+ p->sendLeave(surface);
+// p->sendFrame();
+ });
+
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed);
+
+ QSignalSpy firstDestroyed(exec([=] { return xdgPopup(); }), &XdgPopup::destroyRequested);
+
+ exec([=] {
+ auto *surface = xdgToplevel()->surface();
+ auto *p = pointer();
+ p->sendEnter(surface, {100, 100});
+// p->sendFrame();
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
+// p->sendFrame();
+ });
+
+ // The client will now hide one popup and then show another
+ firstDestroyed.wait();
+
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
+ QCOMPOSITOR_TRY_VERIFY(!xdgPopup(1));
+
+ // Verify we still grabbed in case the client has checks to avoid illegal grabs
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed);
+
+ // Sometimes the popup will select another parent if one is illegal at the time it tries to
+ // grab. So we verify we got the intended parent on the compositor side.
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_parentXdgSurface == xdgToplevel()->m_xdgSurface);
+
+ // For good measure just check that configuring works as usual
+ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
+}
+
+void tst_xdgshell::pongs()
+{
+ // Create and show a window to trigger shell integration initialzation,
+ // otherwise we don't have anything to send ping events to.
+ QRasterWindow window;
+ window.resize(200, 200);
+ window.show();
+
+ // Verify that the client has bound to the global
+ QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1);
+
+ QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
+ const uint serial = exec([=] { return nextSerial(); });
+ exec([=] {
+ auto *base = get<XdgWmBase>();
+ wl_resource *resource = base->resourceMap().first()->handle;
+ base->send_ping(resource, serial);
+ });
+ QTRY_COMPARE(pongSpy.count(), 1);
+ QCOMPARE(pongSpy.first().at(0).toUInt(), serial);
+}
+
+void tst_xdgshell::minMaxSize()
+{
+ QRasterWindow window;
+ window.setMinimumSize(QSize(100, 100));
+ window.setMaximumSize(QSize(1000, 1000));
+ window.resize(400, 320);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100));
+ QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000));
+
+ window.setMaximumSize(QSize(500, 400));
+ QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(500, 400));
+
+ window.setMinimumSize(QSize(50, 40));
+ QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(50, 40));
+}
+
+void tst_xdgshell::windowGeometry()
+{
+ QRasterWindow window;
+ window.resize(400, 320);
+ window.show();
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
+
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size()));
+
+ window.resize(800, 600);
+ QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size()));
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_xdgshell)
+#include "tst_xdgshell.moc"
diff --git a/tests/auto/client/xdgshell/xdgshell.pro b/tests/auto/client/xdgshell/xdgshell.pro
new file mode 100644
index 000000000..d7c3f9df6
--- /dev/null
+++ b/tests/auto/client/xdgshell/xdgshell.pro
@@ -0,0 +1,5 @@
+include (../shared/shared.pri)
+
+TARGET = tst_xdgshell
+SOURCES += tst_xdgshell.cpp
+
diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
index 3c822325b..a397f60eb 100644
--- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
+++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp
@@ -331,6 +331,7 @@ void tst_WaylandClientXdgShellV6::windowStateChangedEvents()
void tst_WaylandClientXdgShellV6::windowGeometrySimple()
{
+ QSKIP("TODO: This test is flaky, figure out why.");
QWindow window;
window.show();
@@ -349,6 +350,7 @@ void tst_WaylandClientXdgShellV6::windowGeometrySimple()
void tst_WaylandClientXdgShellV6::windowGeometryFixed()
{
+ QSKIP("TODO: This test is flaky, figure out why.");
QWindow window;
window.resize(QSize(1337, 137));
window.setMaximumSize(window.size());
diff --git a/tests/auto/client/xdgshellv6/xdgshellv6.pro b/tests/auto/client/xdgshellv6/xdgshellv6.pro
index 4fec593df..cc8a22d83 100644
--- a/tests/auto/client/xdgshellv6/xdgshellv6.pro
+++ b/tests/auto/client/xdgshellv6/xdgshellv6.pro
@@ -1,4 +1,4 @@
-include (../shared/shared.pri)
+include (../shared_old/shared_old.pri)
TARGET = tst_client_xdgshellv6
SOURCES += tst_xdgshellv6.cpp
diff --git a/tests/auto/compositor/compositor/BLACKLIST b/tests/auto/compositor/compositor/BLACKLIST
deleted file mode 100644
index df4672be3..000000000
--- a/tests/auto/compositor/compositor/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[keyboardGrab]
-ubuntu-14.04
diff --git a/tests/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro
index 0ce2c6be0..500a92c61 100644
--- a/tests/auto/compositor/compositor/compositor.pro
+++ b/tests/auto/compositor/compositor/compositor.pro
@@ -11,8 +11,10 @@ qtConfig(xkbcommon): \
QMAKE_USE += xkbcommon
WAYLANDCLIENTSOURCES += \
- ../../../../src/3rdparty/protocol/xdg-shell-unstable-v5.xml \
../../../../src/3rdparty/protocol/ivi-application.xml \
+ ../../../../src/3rdparty/protocol/wayland.xml \
+ ../../../../src/3rdparty/protocol/xdg-shell.xml \
+ ../../../../src/3rdparty/protocol/viewporter.xml
SOURCES += \
tst_compositor.cpp \
diff --git a/tests/auto/compositor/compositor/mockclient.cpp b/tests/auto/compositor/compositor/mockclient.cpp
index f74314407..fa4d4c6b3 100644
--- a/tests/auto/compositor/compositor/mockclient.cpp
+++ b/tests/auto/compositor/compositor/mockclient.cpp
@@ -173,10 +173,12 @@ void MockClient::handleGlobal(uint32_t id, const QByteArray &interface)
wl_output_add_listener(output, &outputListener, this);
} else if (interface == "wl_shm") {
shm = static_cast<wl_shm *>(wl_registry_bind(registry, id, &wl_shm_interface, 1));
+ } else if (interface == "wp_viewporter") {
+ viewporter = static_cast<wp_viewporter *>(wl_registry_bind(registry, id, &wp_viewporter_interface, 1));
} else if (interface == "wl_shell") {
wlshell = static_cast<wl_shell *>(wl_registry_bind(registry, id, &wl_shell_interface, 1));
- } else if (interface == "xdg_shell") {
- xdgShell = static_cast<xdg_shell *>(wl_registry_bind(registry, id, &xdg_shell_interface, 1));
+ } else if (interface == "xdg_wm_base") {
+ xdgWmBase = static_cast<xdg_wm_base *>(wl_registry_bind(registry, id, &xdg_wm_base_interface, 1));
} else if (interface == "ivi_application") {
iviApplication = static_cast<ivi_application *>(wl_registry_bind(registry, id, &ivi_application_interface, 1));
} else if (interface == "wl_seat") {
@@ -205,7 +207,13 @@ wl_shell_surface *MockClient::createShellSurface(wl_surface *surface)
xdg_surface *MockClient::createXdgSurface(wl_surface *surface)
{
flushDisplay();
- return xdg_shell_get_xdg_surface(xdgShell, surface);
+ return xdg_wm_base_get_xdg_surface(xdgWmBase, surface);
+}
+
+xdg_toplevel *MockClient::createXdgToplevel(xdg_surface *xdgSurface)
+{
+ flushDisplay();
+ return xdg_surface_get_toplevel(xdgSurface);
}
ivi_surface *MockClient::createIviSurface(wl_surface *surface, uint iviId)
diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h
index 6bfb652ed..b537e9df1 100644
--- a/tests/auto/compositor/compositor/mockclient.h
+++ b/tests/auto/compositor/compositor/mockclient.h
@@ -26,9 +26,10 @@
**
****************************************************************************/
-#include <wayland-client.h>
-#include <qwayland-xdg-shell-unstable-v5.h>
+#include "wayland-wayland-client-protocol.h"
+#include <qwayland-xdg-shell.h>
#include <wayland-ivi-application-client-protocol.h>
+#include "wayland-viewporter-client-protocol.h"
#include <QObject>
#include <QImage>
@@ -60,6 +61,7 @@ public:
wl_surface *createSurface();
wl_shell_surface *createShellSurface(wl_surface *surface);
xdg_surface *createXdgSurface(wl_surface *surface);
+ xdg_toplevel *createXdgToplevel(xdg_surface *xdgSurface);
ivi_surface *createIviSurface(wl_surface *surface, uint iviId);
wl_display *display = nullptr;
@@ -68,7 +70,8 @@ public:
wl_shm *shm = nullptr;
wl_registry *registry = nullptr;
wl_shell *wlshell = nullptr;
- xdg_shell *xdgShell = nullptr;
+ xdg_wm_base *xdgWmBase = nullptr;
+ wp_viewporter *viewporter = nullptr;
ivi_application *iviApplication = nullptr;
QList<MockSeat *> m_seats;
diff --git a/tests/auto/compositor/compositor/mockkeyboard.h b/tests/auto/compositor/compositor/mockkeyboard.h
index 1090db597..fd7f06aee 100644
--- a/tests/auto/compositor/compositor/mockkeyboard.h
+++ b/tests/auto/compositor/compositor/mockkeyboard.h
@@ -30,7 +30,7 @@
#define MOCKKEYBOARD_H
#include <QObject>
-#include <wayland-client.h>
+#include "wayland-wayland-client-protocol.h"
class MockKeyboard : public QObject
{
diff --git a/tests/auto/compositor/compositor/mockpointer.h b/tests/auto/compositor/compositor/mockpointer.h
index 2054040fd..db6b2b69c 100644
--- a/tests/auto/compositor/compositor/mockpointer.h
+++ b/tests/auto/compositor/compositor/mockpointer.h
@@ -30,7 +30,7 @@
#define MOCKPOINTER_H
#include <QObject>
-#include <wayland-client.h>
+#include "wayland-wayland-client-protocol.h"
class MockPointer : public QObject
{
diff --git a/tests/auto/compositor/compositor/mockseat.h b/tests/auto/compositor/compositor/mockseat.h
index f8c103ed4..0d0f4074c 100644
--- a/tests/auto/compositor/compositor/mockseat.h
+++ b/tests/auto/compositor/compositor/mockseat.h
@@ -32,7 +32,7 @@
#include "mockkeyboard.h"
#include <QObject>
-#include <wayland-client.h>
+#include "wayland-wayland-client-protocol.h"
class MockSeat : public QObject
{
diff --git a/tests/auto/compositor/compositor/testcompositor.cpp b/tests/auto/compositor/compositor/testcompositor.cpp
index 710bb7b3a..d5967a416 100644
--- a/tests/auto/compositor/compositor/testcompositor.cpp
+++ b/tests/auto/compositor/compositor/testcompositor.cpp
@@ -30,7 +30,7 @@
#include "testseat.h"
#include "testkeyboardgrabber.h"
-#include <wayland-server.h>
+#include <wayland-server-core.h>
TestCompositor::TestCompositor(bool createInputDev)
: shell(new QWaylandWlShell(this))
@@ -41,7 +41,9 @@ TestCompositor::TestCompositor(bool createInputDev)
void TestCompositor::create()
{
- new QWaylandOutput(this, nullptr);
+ auto output = new QWaylandOutput(this, nullptr);
+ setDefaultOutput(output);
+
QWaylandCompositor::create();
connect(this, &QWaylandCompositor::surfaceCreated, this, &TestCompositor::onSurfaceCreated);
diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp
index e12aa564e..c425f2ba1 100644
--- a/tests/auto/compositor/compositor/tst_compositor.cpp
+++ b/tests/auto/compositor/compositor/tst_compositor.cpp
@@ -38,7 +38,7 @@
#include "qwaylandseat.h"
#include <QtGui/QScreen>
-#include <QtWaylandCompositor/QWaylandXdgShellV5>
+#include <QtWaylandCompositor/QWaylandXdgShell>
#include <QtWaylandCompositor/private/qwaylandxdgshellv6_p.h>
#include <QtWaylandCompositor/private/qwaylandkeyboard_p.h>
#include <QtWaylandCompositor/QWaylandIviApplication>
@@ -46,7 +46,8 @@
#include <QtWaylandCompositor/QWaylandSurface>
#include <QtWaylandCompositor/QWaylandResource>
#include <QtWaylandCompositor/QWaylandKeymap>
-#include <qwayland-xdg-shell-unstable-v5.h>
+#include <QtWaylandCompositor/QWaylandViewporter>
+#include <qwayland-xdg-shell.h>
#include <qwayland-ivi-application.h>
#include <QtTest/QtTest>
@@ -68,6 +69,7 @@ private slots:
void seatKeyboardFocus();
void seatMouseFocus();
void inputRegion();
+ void defaultInputRegionHiDpi();
void singleClient();
void multipleClients();
void geometry();
@@ -77,7 +79,7 @@ private slots:
void mapSurface();
void mapSurfaceHiDpi();
void frameCallback();
- void removeOutput();
+ void outputs();
void customSurface();
void advertisesXdgShellSupport();
@@ -94,6 +96,20 @@ private slots:
void convertsXdgEdgesToQtEdges();
void xdgShellV6Positioner();
+
+ void viewporterGlobal();
+ void viewportDestination();
+ void viewportSource();
+ void viewportSourceAndDestination();
+ void viewportDestruction();
+ void viewportProtocolErrors_data();
+ void viewportProtocolErrors();
+ void viewportClearDestination();
+ void viewportClearSource();
+ void viewportExistsError();
+ void viewportDestinationNoSurfaceError();
+ void viewportSourceNoSurfaceError();
+ void viewportHiDpi();
};
void tst_WaylandCompositor::init() {
@@ -434,7 +450,8 @@ void tst_WaylandCompositor::mapSurface()
QSignalSpy hasContentSpy(waylandSurface, SIGNAL(hasContentChanged()));
- QCOMPARE(waylandSurface->size(), QSize());
+ QCOMPARE(waylandSurface->bufferSize(), QSize());
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
QCOMPARE(waylandSurface->hasContent(), false);
QSize size(256, 256);
@@ -448,7 +465,8 @@ void tst_WaylandCompositor::mapSurface()
QTRY_COMPARE(hasContentSpy.count(), 1);
QCOMPARE(waylandSurface->hasContent(), true);
- QCOMPARE(waylandSurface->size(), size);
+ QCOMPARE(waylandSurface->bufferSize(), size);
+ QCOMPARE(waylandSurface->destinationSize(), size);
wl_surface_destroy(surface);
}
@@ -478,7 +496,8 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
wl_surface_damage(surface, 0, 0, surfaceSize.width(), surfaceSize.height());
auto verifyComittedState = [=]() {
- QCOMPARE(waylandSurface->size(), bufferSize);
+ QCOMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), surfaceSize);
QCOMPARE(waylandSurface->bufferScale(), bufferScale);
QCOMPARE(waylandSurface->hasContent(), true);
};
@@ -497,6 +516,9 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
QObject::connect(waylandSurface, &QWaylandSurface::sizeChanged, verifyComittedState);
QSignalSpy sizeSpy(waylandSurface, SIGNAL(sizeChanged()));
+ QObject::connect(waylandSurface, &QWaylandSurface::destinationSizeChanged, verifyComittedState);
+ QSignalSpy destinationSizeSpy(waylandSurface, SIGNAL(destinationSizeChanged()));
+
QObject::connect(waylandSurface, &QWaylandSurface::bufferScaleChanged, verifyComittedState);
QSignalSpy bufferScaleSpy(waylandSurface, SIGNAL(bufferScaleChanged()));
@@ -507,7 +529,8 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
QSignalSpy offsetSpy(waylandSurface, SIGNAL(offsetForNextFrame(const QPoint &)));
// No state should be applied before the commit
- QCOMPARE(waylandSurface->size(), QSize());
+ QCOMPARE(waylandSurface->bufferSize(), QSize());
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
QCOMPARE(waylandSurface->hasContent(), false);
QCOMPARE(waylandSurface->bufferScale(), 1);
QCOMPARE(offsetSpy.count(), 0);
@@ -516,6 +539,7 @@ void tst_WaylandCompositor::mapSurfaceHiDpi()
QTRY_COMPARE(hasContentSpy.count(), 1);
QTRY_COMPARE(sizeSpy.count(), 1);
+ QTRY_COMPARE(destinationSizeSpy.count(), 1);
QTRY_COMPARE(bufferScaleSpy.count(), 1);
QTRY_COMPARE(offsetSpy.count(), 1);
@@ -598,18 +622,33 @@ void tst_WaylandCompositor::frameCallback()
wl_surface_destroy(surface);
}
-void tst_WaylandCompositor::removeOutput()
+void tst_WaylandCompositor::outputs()
{
TestCompositor compositor;
+
+ QSignalSpy defaultOutputSpy(&compositor, SIGNAL(defaultOutputChanged()));
+
+ compositor.create();
+
+ QSignalSpy outputAddedSpy(&compositor, SIGNAL(outputAdded(QWaylandOutput*)));
+ QSignalSpy outputRemovedSpy(&compositor, SIGNAL(outputRemoved(QWaylandOutput*)));
+
QWindow window;
window.resize(800, 600);
+
auto output = new QWaylandOutput(&compositor, &window);
+ QTRY_COMPARE(outputAddedSpy.count(), 1);
+
+ compositor.setDefaultOutput(output);
+ QTRY_COMPARE(defaultOutputSpy.count(), 2);
- compositor.create();
MockClient client;
QTRY_COMPARE(client.m_outputs.size(), 2);
delete output;
+ QTRY_COMPARE(outputRemovedSpy.count(), 1);
+ QEXPECT_FAIL("", "FIXME: defaultOutputChanged() is not emitted when the default output is removed", Continue);
+ QTRY_COMPARE(defaultOutputSpy.count(), 3);
compositor.flushClients();
QTRY_COMPARE(client.m_outputs.size(), 1);
}
@@ -638,6 +677,8 @@ void tst_WaylandCompositor::customSurface()
MockClient client;
wl_surface *surface = client.createSurface();
QTRY_COMPARE(compositor.surfaces.size(), 1);
+ wl_surface_destroy(surface);
+ QTRY_COMPARE(compositor.surfaces.size(), 0);
}
void tst_WaylandCompositor::seatCapabilities()
@@ -826,11 +867,38 @@ void tst_WaylandCompositor::inputRegion()
QVERIFY(!waylandSurface->inputRegionContains(QPoint(1, 2)));
}
+void tst_WaylandCompositor::defaultInputRegionHiDpi()
+{
+ TestCompositor compositor(true);
+ compositor.create();
+
+ MockClient client;
+ wl_surface *surface = client.createSurface();
+
+ int bufferScale = 2;
+ QSize surfaceSize(16, 16);
+ QSize bufferSize = surfaceSize * bufferScale;
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, surfaceSize.width(), surfaceSize.height());
+ wl_surface_set_buffer_scale(surface, bufferScale);
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->bufferScale(), bufferScale);
+ QVERIFY(waylandSurface->inputRegionContains(QPoint(0, 0)));
+ QVERIFY(waylandSurface->inputRegionContains(QPoint(15, 15)));
+ QVERIFY(!waylandSurface->inputRegionContains(QPoint(-1, -1)));
+ QVERIFY(!waylandSurface->inputRegionContains(QPoint(16, 16)));
+}
+
class XdgTestCompositor: public TestCompositor {
Q_OBJECT
public:
XdgTestCompositor() : xdgShell(this) {}
- QWaylandXdgShellV5 xdgShell;
+ QWaylandXdgShell xdgShell;
};
void tst_WaylandCompositor::advertisesXdgShellSupport()
@@ -839,7 +907,7 @@ void tst_WaylandCompositor::advertisesXdgShellSupport()
compositor.create();
MockClient client;
- QTRY_VERIFY(client.xdgShell);
+ QTRY_VERIFY(client.xdgWmBase);
}
void tst_WaylandCompositor::createsXdgSurfaces()
@@ -848,19 +916,22 @@ void tst_WaylandCompositor::createsXdgSurfaces()
compositor.create();
MockClient client;
- QTRY_VERIFY(client.xdgShell);
+ QTRY_VERIFY(client.xdgWmBase);
- QSignalSpy xdgSurfaceCreatedSpy(&compositor.xdgShell, &QWaylandXdgShellV5::xdgSurfaceCreated);
- QWaylandXdgSurfaceV5 *xdgSurface = nullptr;
- QObject::connect(&compositor.xdgShell, &QWaylandXdgShellV5::xdgSurfaceCreated, [&](QWaylandXdgSurfaceV5 *s) {
+ QSignalSpy xdgSurfaceCreatedSpy(&compositor.xdgShell, &QWaylandXdgShell::xdgSurfaceCreated);
+ QWaylandXdgSurface *xdgSurface = nullptr;
+ QObject::connect(&compositor.xdgShell, &QWaylandXdgShell::xdgSurfaceCreated, [&](QWaylandXdgSurface *s) {
xdgSurface = s;
});
wl_surface *surface = client.createSurface();
- client.createXdgSurface(surface);
+ xdg_surface *clientXdgSurface = client.createXdgSurface(surface);
QTRY_COMPARE(xdgSurfaceCreatedSpy.count(), 1);
QTRY_VERIFY(xdgSurface);
QTRY_VERIFY(xdgSurface->surface());
+
+ xdg_surface_destroy(clientXdgSurface);
+ wl_surface_destroy(surface);
}
void tst_WaylandCompositor::reportsXdgSurfaceWindowGeometry()
@@ -868,27 +939,36 @@ void tst_WaylandCompositor::reportsXdgSurfaceWindowGeometry()
XdgTestCompositor compositor;
compositor.create();
- QWaylandXdgSurfaceV5 *xdgSurface = nullptr;
- QObject::connect(&compositor.xdgShell, &QWaylandXdgShellV5::xdgSurfaceCreated, [&](QWaylandXdgSurfaceV5 *s) {
+ QWaylandXdgSurface *xdgSurface = nullptr;
+ QObject::connect(&compositor.xdgShell, &QWaylandXdgShell::xdgSurfaceCreated, [&](QWaylandXdgSurface *s) {
xdgSurface = s;
});
MockClient client;
wl_surface *surface = client.createSurface();
xdg_surface *clientXdgSurface = client.createXdgSurface(surface);
+ xdg_toplevel *clientToplevel = client.createXdgToplevel(clientXdgSurface);
+
QSize size(256, 256);
ShmBuffer buffer(size, client.shm);
+
+ QTRY_VERIFY(xdgSurface);
+ //TODO: Here we should ideally be acking the configure, we're techically making a protocol error
+
wl_surface_attach(surface, buffer.handle, 0, 0);
wl_surface_damage(surface, 0, 0, size.width(), size.height());
wl_surface_commit(surface);
- QTRY_VERIFY(xdgSurface);
QTRY_COMPARE(xdgSurface->windowGeometry(), QRect(QPoint(0, 0), QSize(256, 256)));
xdg_surface_set_window_geometry(clientXdgSurface, 10, 20, 100, 50);
wl_surface_commit(surface);
QTRY_COMPARE(xdgSurface->windowGeometry(), QRect(QPoint(10, 20), QSize(100, 50)));
+
+ xdg_toplevel_destroy(clientToplevel);
+ xdg_surface_destroy(clientXdgSurface);
+ wl_surface_destroy(surface);
}
void tst_WaylandCompositor::setsXdgAppId()
@@ -896,19 +976,20 @@ void tst_WaylandCompositor::setsXdgAppId()
XdgTestCompositor compositor;
compositor.create();
- QWaylandXdgSurfaceV5 *xdgSurface = nullptr;
- QObject::connect(&compositor.xdgShell, &QWaylandXdgShellV5::xdgSurfaceCreated, [&](QWaylandXdgSurfaceV5 *s) {
- xdgSurface = s;
+ QWaylandXdgToplevel *toplevel = nullptr;
+ QObject::connect(&compositor.xdgShell, &QWaylandXdgShell::toplevelCreated, [&](QWaylandXdgToplevel *t) {
+ toplevel = t;
});
MockClient client;
wl_surface *surface = client.createSurface();
xdg_surface *clientXdgSurface = client.createXdgSurface(surface);
+ xdg_toplevel *clientToplevel = client.createXdgToplevel(clientXdgSurface);
- xdg_surface_set_app_id(clientXdgSurface, "org.foo.bar");
+ xdg_toplevel_set_app_id(clientToplevel, "org.foo.bar");
- QTRY_VERIFY(xdgSurface);
- QTRY_COMPARE(xdgSurface->appId(), QString("org.foo.bar"));
+ QTRY_VERIFY(toplevel);
+ QTRY_COMPARE(toplevel->appId(), QString("org.foo.bar"));
}
void tst_WaylandCompositor::sendsXdgConfigure()
@@ -916,111 +997,118 @@ void tst_WaylandCompositor::sendsXdgConfigure()
class MockXdgSurface : public QtWayland::xdg_surface
{
public:
- MockXdgSurface(::xdg_surface *xdgSurface) : QtWayland::xdg_surface(xdgSurface) {}
- void xdg_surface_configure(int32_t width, int32_t height, wl_array *rawStates, uint32_t serial) override
+ explicit MockXdgSurface(::xdg_surface *xdgSurface) : QtWayland::xdg_surface(xdgSurface) {}
+ void xdg_surface_configure(uint32_t serial) override { configureSerial = serial; }
+ uint configureSerial = 0;
+ };
+
+ class MockXdgToplevel : public QtWayland::xdg_toplevel
+ {
+ public:
+ explicit MockXdgToplevel(::xdg_toplevel *toplevel) : QtWayland::xdg_toplevel(toplevel) {}
+ void xdg_toplevel_configure(int32_t width, int32_t height, wl_array *rawStates) override
{
configureSize = QSize(width, height);
- configureSerial = serial;
-
uint *states = reinterpret_cast<uint*>(rawStates->data);
configureStates.clear();
size_t numStates = rawStates->size / sizeof(uint);
- for (size_t i = 0; i < numStates; ++i) {
+ for (size_t i = 0; i < numStates; ++i)
configureStates.push_back(states[i]);
- }
}
-
QList<uint> configureStates;
QSize configureSize;
- uint configureSerial;
};
XdgTestCompositor compositor;
compositor.create();
- QWaylandXdgSurfaceV5 *xdgSurface = nullptr;
- QObject::connect(&compositor.xdgShell, &QWaylandXdgShellV5::xdgSurfaceCreated, [&](QWaylandXdgSurfaceV5 *s) {
- xdgSurface = s;
+ QWaylandXdgToplevel *toplevel = nullptr;
+ QObject::connect(&compositor.xdgShell, &QWaylandXdgShell::toplevelCreated, [&](QWaylandXdgToplevel *t) {
+ toplevel = t;
});
MockClient client;
wl_surface *surface = client.createSurface();
+
xdg_surface *clientXdgSurface = client.createXdgSurface(surface);
MockXdgSurface mockXdgSurface(clientXdgSurface);
- QTRY_VERIFY(xdgSurface);
- QTRY_VERIFY(!xdgSurface->activated());
- QTRY_VERIFY(!xdgSurface->maximized());
- QTRY_VERIFY(!xdgSurface->fullscreen());
- QTRY_VERIFY(!xdgSurface->resizing());
+ xdg_toplevel *clientToplevel = client.createXdgToplevel(clientXdgSurface);
+ MockXdgToplevel mockToplevel(clientToplevel);
+
+ QTRY_VERIFY(toplevel);
+ QTRY_VERIFY(!toplevel->activated());
+ QTRY_VERIFY(!toplevel->maximized());
+ QTRY_VERIFY(!toplevel->fullscreen());
+ QTRY_VERIFY(!toplevel->resizing());
- xdgSurface->sendConfigure(QSize(10, 20), QVector<QWaylandXdgSurfaceV5::State>{QWaylandXdgSurfaceV5::State::ActivatedState});
+ toplevel->sendConfigure(QSize(10, 20), QVector<QWaylandXdgToplevel::State>{QWaylandXdgToplevel::State::ActivatedState});
compositor.flushClients();
- QTRY_COMPARE(mockXdgSurface.configureStates, QList<uint>{QWaylandXdgSurfaceV5::State::ActivatedState});
- QTRY_COMPARE(mockXdgSurface.configureSize, QSize(10, 20));
+ QTRY_COMPARE(mockToplevel.configureStates, QList<uint>{QWaylandXdgToplevel::State::ActivatedState});
+ QTRY_COMPARE(mockToplevel.configureSize, QSize(10, 20));
- xdgSurface->sendMaximized(QSize(800, 600));
+ toplevel->sendMaximized(QSize(800, 600));
compositor.flushClients();
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::MaximizedState));
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
- QTRY_COMPARE(mockXdgSurface.configureSize, QSize(800, 600));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::MaximizedState));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
+ QTRY_COMPARE(mockToplevel.configureSize, QSize(800, 600));
// There hasn't been any ack_configures, so state should still be unchanged
- QTRY_VERIFY(!xdgSurface->activated());
- QTRY_VERIFY(!xdgSurface->maximized());
+ QTRY_VERIFY(!toplevel->activated());
+ QTRY_VERIFY(!toplevel->maximized());
xdg_surface_ack_configure(clientXdgSurface, mockXdgSurface.configureSerial);
wl_display_dispatch_pending(client.display);
wl_display_flush(client.display);
- QTRY_VERIFY(xdgSurface->activated());
- QTRY_VERIFY(xdgSurface->maximized());
+ QTRY_VERIFY(toplevel->activated());
+ QTRY_VERIFY(toplevel->maximized());
- xdgSurface->sendUnmaximized();
+ toplevel->sendUnmaximized();
compositor.flushClients();
- QTRY_VERIFY(!mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::MaximizedState));
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
- QTRY_COMPARE(mockXdgSurface.configureSize, QSize(0, 0));
+ QTRY_VERIFY(!mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::MaximizedState));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
+ QTRY_COMPARE(mockToplevel.configureSize, QSize(0, 0));
// The unmaximized configure hasn't been acked, so maximized should still be true
- QTRY_VERIFY(xdgSurface->maximized());
- QTRY_VERIFY(xdgSurface->activated());
+ QTRY_VERIFY(toplevel->maximized());
+ QTRY_VERIFY(toplevel->activated());
- xdgSurface->sendResizing(QSize(800, 600));
+ toplevel->sendResizing(QSize(800, 600));
compositor.flushClients();
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ResizingState));
- QTRY_COMPARE(mockXdgSurface.configureSize, QSize(800, 600));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ResizingState));
+ QTRY_COMPARE(mockToplevel.configureSize, QSize(800, 600));
- xdgSurface->sendFullscreen(QSize(1024, 768));
+ toplevel->sendFullscreen(QSize(1024, 768));
compositor.flushClients();
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::FullscreenState));
- QTRY_COMPARE(mockXdgSurface.configureSize, QSize(1024, 768));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::FullscreenState));
+ QTRY_COMPARE(mockToplevel.configureSize, QSize(1024, 768));
uint fullscreenSerial = mockXdgSurface.configureSerial;
- xdgSurface->sendUnmaximized();
+ toplevel->sendUnmaximized();
compositor.flushClients();
- QTRY_VERIFY(mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
- QTRY_VERIFY(!mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::FullscreenState));
+ QTRY_VERIFY(mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
+ QTRY_VERIFY(!mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::FullscreenState));
- xdgSurface->sendConfigure(QSize(0, 0), QVector<QWaylandXdgSurfaceV5::State>{});
+ toplevel->sendConfigure(QSize(0, 0), QVector<QWaylandXdgToplevel::State>{});
compositor.flushClients();
- QTRY_VERIFY(!mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
+ QTRY_VERIFY(!mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
- xdgSurface->sendMaximized(QSize(800, 600));
+ toplevel->sendMaximized(QSize(800, 600));
compositor.flushClients();
- QTRY_VERIFY(!mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::ActivatedState));
+ QTRY_VERIFY(!mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::ActivatedState));
- xdgSurface->sendFullscreen(QSize(800, 600));
+ toplevel->sendFullscreen(QSize(800, 600));
compositor.flushClients();
- QTRY_VERIFY(!mockXdgSurface.configureStates.contains(QWaylandXdgSurfaceV5::State::MaximizedState));
+ QTRY_VERIFY(!mockToplevel.configureStates.contains(QWaylandXdgToplevel::State::MaximizedState));
// Verify that acking a configure that's not the most recently sent works
xdg_surface_ack_configure(clientXdgSurface, fullscreenSerial);
wl_display_dispatch_pending(client.display);
wl_display_flush(client.display);
- QTRY_VERIFY(xdgSurface->fullscreen());
- QTRY_VERIFY(xdgSurface->activated());
- QTRY_VERIFY(!xdgSurface->maximized());
- QTRY_VERIFY(!xdgSurface->resizing());
+ QTRY_VERIFY(toplevel->fullscreen());
+ QTRY_VERIFY(toplevel->activated());
+ QTRY_VERIFY(!toplevel->maximized());
+ QTRY_VERIFY(!toplevel->resizing());
}
class IviTestCompositor: public TestCompositor {
@@ -1211,5 +1299,406 @@ void tst_WaylandCompositor::xdgShellV6Positioner()
QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800 - 100 / 2 + 4, 2 + 600 / 2 - 50 + 8));
}
+class ViewporterTestCompositor: public TestCompositor {
+ Q_OBJECT
+public:
+ ViewporterTestCompositor() : viewporter(this) {}
+ QWaylandViewporter viewporter;
+};
+
+void tst_WaylandCompositor::viewporterGlobal()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+}
+
+void tst_WaylandCompositor::viewportDestination()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ const QSize destinationSize(128, 123);
+ wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height());
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), QSize(128, 123));
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect(QPoint(), bufferSize));
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportSource()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ const QRectF sourceGeometry(QPointF(10.5, 20.5), QSizeF(30, 40));
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(sourceGeometry.x()),
+ wl_fixed_from_double(sourceGeometry.y()),
+ wl_fixed_from_double(sourceGeometry.width()),
+ wl_fixed_from_double(sourceGeometry.height()));
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), sourceGeometry.size().toSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry);
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportSourceAndDestination()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+
+ const QSize destinationSize(128, 123);
+ wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height());
+
+ const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40));
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(sourceGeometry.x()),
+ wl_fixed_from_double(sourceGeometry.y()),
+ wl_fixed_from_double(sourceGeometry.width()),
+ wl_fixed_from_double(sourceGeometry.height()));
+
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), destinationSize);
+ QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry);
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportDestruction()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+
+ const QSize destinationSize(128, 123);
+ wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height());
+
+ const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40));
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(sourceGeometry.x()),
+ wl_fixed_from_double(sourceGeometry.y()),
+ wl_fixed_from_double(sourceGeometry.width()),
+ wl_fixed_from_double(sourceGeometry.height()));
+
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), destinationSize);
+ QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry);
+
+ wp_viewport_destroy(viewport);
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize);
+ QCOMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize));
+
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportProtocolErrors_data()
+{
+ QTest::addColumn<QRectF>("source");
+ QTest::addColumn<QSize>("destination");
+ QTest::addColumn<uint>("error");
+
+ QTest::newRow("invalid source position") << QRectF(-1, 0, 16, 16) << QSize(64, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE);
+ QTest::newRow("invalid source size") << QRectF(0, 0, -1, 16) << QSize(64, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE);
+ QTest::newRow("invalid destination size") << QRectF(0, 0, 16, 16) << QSize(-16, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE);
+ QTest::newRow("invalid non-integer source with unset size") << QRectF(0, 0, 15.5, 15.5) << QSize(-1, -1) << uint(WP_VIEWPORT_ERROR_BAD_SIZE);
+ QTest::newRow("bigger source than buffer") << QRectF(0, 0, 13337, 13337) << QSize(-1, -1) << uint(WP_VIEWPORT_ERROR_OUT_OF_BUFFER);
+}
+
+void tst_WaylandCompositor::viewportProtocolErrors()
+{
+ QFETCH(QRectF, source);
+ QFETCH(QSize, destination);
+ QFETCH(uint, error);
+
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(source.x()),
+ wl_fixed_from_double(source.y()),
+ wl_fixed_from_double(source.width()),
+ wl_fixed_from_double(source.height()));
+ wp_viewport_set_destination(viewport, destination.width(), destination.height());
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(client.error, EPROTO);
+ QCOMPARE(client.protocolError.interface, &wp_viewport_interface);
+ QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), error);
+}
+
+void tst_WaylandCompositor::viewportClearDestination()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+
+ const QSize destinationSize(128, 123);
+ wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height());
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize);
+ QCOMPARE(waylandSurface->destinationSize(), destinationSize);
+
+ wp_viewport_set_destination(viewport, -1, -1);
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize);
+ QCOMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize));
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportClearSource()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ QCOMPARE(waylandSurface->destinationSize(), QSize());
+ QCOMPARE(waylandSurface->sourceGeometry(), QRect());
+
+ const QSize bufferSize(64, 64);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ QRectF source(10, 20, 30, 40);
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(source.x()),
+ wl_fixed_from_double(source.y()),
+ wl_fixed_from_double(source.width()),
+ wl_fixed_from_double(source.height()));
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->sourceGeometry(), source);
+
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(-1),
+ wl_fixed_from_double(-1),
+ wl_fixed_from_double(-1),
+ wl_fixed_from_double(-1));
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize));
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+ QCOMPARE(client.error, 0);
+}
+
+void tst_WaylandCompositor::viewportExistsError()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ wp_viewporter_get_viewport(client.viewporter, surface);
+ wp_viewporter_get_viewport(client.viewporter, surface);
+
+ QTRY_COMPARE(client.error, EPROTO);
+ QCOMPARE(client.protocolError.interface, &wp_viewporter_interface);
+ QCOMPARE(static_cast<wp_viewporter_error>(client.protocolError.code), WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS);
+}
+
+void tst_WaylandCompositor::viewportDestinationNoSurfaceError()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ wl_surface_destroy(surface);
+ wp_viewport_set_destination(viewport, 32, 32);
+
+ QTRY_COMPARE(client.error, EPROTO);
+ QCOMPARE(client.protocolError.interface, &wp_viewport_interface);
+ QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), WP_VIEWPORT_ERROR_NO_SURFACE);
+}
+
+void tst_WaylandCompositor::viewportSourceNoSurfaceError()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ wl_surface_destroy(surface);
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(0),
+ wl_fixed_from_double(0),
+ wl_fixed_from_double(1),
+ wl_fixed_from_double(1));
+
+ QTRY_COMPARE(client.error, EPROTO);
+ QCOMPARE(client.protocolError.interface, &wp_viewport_interface);
+ QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), WP_VIEWPORT_ERROR_NO_SURFACE);
+}
+
+void tst_WaylandCompositor::viewportHiDpi()
+{
+ ViewporterTestCompositor compositor;
+ compositor.create();
+ MockClient client;
+ QTRY_VERIFY(client.viewporter);
+
+ wl_surface *surface = client.createSurface();
+ QTRY_COMPARE(compositor.surfaces.size(), 1);
+ QWaylandSurface *waylandSurface = compositor.surfaces.at(0);
+
+ const QSize bufferSize(128, 128);
+ ShmBuffer buffer(bufferSize, client.shm);
+ wl_surface_attach(surface, buffer.handle, 0, 0);
+ wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height());
+ constexpr int bufferScale = 2;
+ wl_surface_set_buffer_scale(surface, bufferScale);
+
+ wl_surface_commit(surface);
+ QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize / bufferScale);
+
+ wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface);
+ const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40));
+ wp_viewport_set_source(viewport,
+ wl_fixed_from_double(sourceGeometry.x()),
+ wl_fixed_from_double(sourceGeometry.y()),
+ wl_fixed_from_double(sourceGeometry.width()),
+ wl_fixed_from_double(sourceGeometry.height()));
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->destinationSize(), sourceGeometry.size());
+ QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry);
+ QCOMPARE(waylandSurface->bufferSize(), bufferSize);
+
+ const QSize destinationSize(128, 123);
+ wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height());
+ wl_surface_commit(surface);
+
+ QTRY_COMPARE(waylandSurface->destinationSize(), destinationSize);
+ QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry);
+ QCOMPARE(waylandSurface->bufferSize(), bufferSize);
+
+ QCOMPARE(client.error, 0);
+
+ wp_viewport_destroy(viewport);
+ wl_surface_destroy(surface);
+}
+
#include <tst_compositor.moc>
QTEST_MAIN(tst_WaylandCompositor);
diff --git a/tests/manual/wlscaler/main.cpp b/tests/manual/wlscaler/main.cpp
new file mode 100644
index 000000000..b836e3787
--- /dev/null
+++ b/tests/manual/wlscaler/main.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
+ return app.exec();
+}
diff --git a/tests/manual/wlscaler/main.qml b/tests/manual/wlscaler/main.qml
new file mode 100644
index 000000000..ddadf4801
--- /dev/null
+++ b/tests/manual/wlscaler/main.qml
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Window 2.2
+import QtWayland.Compositor 1.13
+
+WaylandCompositor {
+ id: comp
+ WaylandOutput {
+ id: output
+ compositor: comp
+ sizeFollowsWindow: true
+ window: Window {
+ id: win
+ width: 500
+ height: 500
+ visible: true
+ title: "wlscaler-compositor - " + comp.socketName
+ Repeater {
+ model: shellSurfaces
+ ShellSurfaceItem {
+ shellSurface: modelData
+ onSurfaceDestroyed: shellSurfaces.remove(index);
+ autoCreatePopupItems: true
+ }
+ }
+ }
+ }
+
+ WlScaler {}
+
+ ListModel { id: shellSurfaces }
+
+ XdgShell {
+ onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface});
+ }
+
+ // Including legacy shell extensions as well, as clients using wl-scaler
+ // probably use outdated shells as well.
+
+ WlShell {
+ onWlShellSurfaceCreated: shellSurfaces.append({shellSurface: shellSurface});
+ }
+
+ XdgShellV5 {
+ onXdgSurfaceCreated: shellSurfaces.append({shellSurface: xdgSurface});
+ }
+
+ XdgShellV6 {
+ onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface});
+ }
+}
diff --git a/tests/manual/wlscaler/qml.qrc b/tests/manual/wlscaler/qml.qrc
new file mode 100644
index 000000000..6b2d0a781
--- /dev/null
+++ b/tests/manual/wlscaler/qml.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>main.qml</file>
+</qresource>
+</RCC>
diff --git a/tests/manual/wlscaler/wlscaler.pro b/tests/manual/wlscaler/wlscaler.pro
new file mode 100644
index 000000000..e4ba825f8
--- /dev/null
+++ b/tests/manual/wlscaler/wlscaler.pro
@@ -0,0 +1,7 @@
+TEMPLATE = app
+
+QT += gui qml
+
+SOURCES += main.cpp
+
+RESOURCES += qml.qrc