Merge remote-tracking branch 'origin/dev' into wip/cmake

Change-Id: Ia76896a4d06240ccc3d95ba478b6ce273c7a0be2
This commit is contained in:
Alexandru Croitor 2020-03-26 10:15:35 +01:00
commit b27b0ac173
60 changed files with 3396 additions and 245 deletions

View File

@ -28,6 +28,7 @@
<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
https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
and the Linux DRM sub-system's AddFb2 ioctl.
This interface offers ways to create generic dmabuf-based
@ -129,8 +130,16 @@
binds to this interface. A roundtrip after binding guarantees that
the client has received all supported format-modifier pairs.
For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi ==
0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event.
It indicates that the server can support the format with an implicit
modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it
is as if no explicit modifier is specified. The effective modifier
will be derived from the dmabuf.
For the definition of the format and modifier codes, see the
zwp_linux_buffer_params_v1::create request.
zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add
requests.
</description>
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
<arg name="modifier_hi" type="uint"
@ -197,6 +206,11 @@
compression, etc. driver-specific modifications to the base format
defined by the DRM fourcc code.
Warning: It should be an error if the format/modifier pair was not
advertised with the modifier event. This is not enforced yet because
some implementations always accept DRM_FORMAT_MOD_INVALID. Also
version 2 of this protocol does not have the modifier event.
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.

View File

@ -9,7 +9,7 @@
"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",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
@ -65,11 +65,11 @@ Copyright (c) 2013 BMW Car IT GmbH"
"Description": "The primary selection extension allows copying text by selecting it and pasting it with the middle mouse button.",
"Homepage": "https://wayland.freedesktop.org",
"Version": "1",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/primary-selection/primary-selection-unstable-v1.xml",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/primary-selection/primary-selection-unstable-v1.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2015 2016 Red Hat"
"Copyright": "Copyright © 2015, 2016 Red Hat"
},
{
@ -82,13 +82,31 @@ Copyright (c) 2013 BMW Car IT GmbH"
"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",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/weston/raw/1.11/protocol/scaler.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2013-2014 Collabora, Ltd."
},
{
"Id": "wayland-tablet-protocol",
"Name": "Wayland Tablet Protocol",
"QDocModule": "qtwaylandcompositor",
"QtUsage": "Used in the Qt Wayland platform plugin",
"Files": "tablet-unstable-v2.xml",
"Description": "",
"Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v2, version 1",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/tablet/tablet-unstable-v2.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright 2014 © Stephen "Lyude" Chandler Paul
Copyright 2015-2016 © Red Hat, Inc."
},
{
"Id": "wayland-viewporter-protocol",
"Name": "Wayland Viewporter Protocol",
@ -99,7 +117,7 @@ Copyright (c) 2013 BMW Car IT GmbH"
"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",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/stable/viewporter/viewporter.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
@ -116,7 +134,7 @@ Copyright (c) 2013 BMW Car IT GmbH"
"Description": "The xdg-decoration protocol allows a compositor to announce support for server-side decorations.",
"Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1, version 1",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml?h=1.16",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
@ -132,8 +150,8 @@ Copyright (c) 2013 BMW Car IT GmbH"
"Description": "The XDG Output protocol is an extended way to describe output regions under Wayland",
"Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1, version 2",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/xdg-output/xdg-output-unstable-v1.xml?h=1.16",
"Version": "unstable v1, version 3",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/xdg-output/xdg-output-unstable-v1.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
@ -148,20 +166,23 @@ Copyright (c) 2013 BMW Car IT GmbH"
"Files": " xdg-shell-unstable-v5.xml xdg-shell-unstable-v6.xml xdg-shell.xml ../../compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5.cpp ../../compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h ../../compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-protocol.c ../../compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h ../../plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp ../../plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h ../../plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h ../../plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-protocol.c",
"Description": "The XDG-Shell protocol is an extended way to manage surfaces under Wayland compositors.",
"Homepage": "https://wayland.freedesktop.org",
"Version": "1.9.0",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/weston/tag/?id=1.9.0",
"Homepage": "https://gitlab.freedesktop.org/wayland/wayland-protocols/",
"Version": "1.18",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/tree/1.18/",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2008-2013 Kristian Høgsberg
Copyright © 2013 Rafael Antognolli
Copyright © 2013 Jasper St. Pierre
Copyright © 2010-2013 Intel Corporation"
Copyright © 2010-2013 Intel Corporation
Copyright © 2015-2017 Samsung Electronics Co., Ltd
Copyright © 2015-2017 Red Hat Inc.
"
},
{
"Id": "wayland-txt-input-unstable",
"Id": "wayland-text-input-unstable",
"Name": "Wayland Text Input Protocol",
"QDocModule": "qtwaylandcompositor",
"QtUsage": "Used in the Qt Wayland Compositor, and the Qt Wayland platform plugin.",
@ -169,6 +190,7 @@ Copyright © 2010-2013 Intel Corporation"
"Description": "Adds support for text input and input methods to applications.",
"Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v2",
"LicenseId": "HPND",
"License": "HPND License",
"LicenseFile": "HPND_LICENSE.txt",
@ -185,6 +207,8 @@ Copyright © 2015, 2016 Jan Arne Petersen"
"Description": "The linux dmabuf protocol is a way to create dmabuf-based wl_buffers",
"Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1, version 3",
"DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",
@ -200,6 +224,8 @@ Copyright © 2015, 2016 Jan Arne Petersen"
"Description": "Allows clients to request that the compositor creates its EGLStream.",
"Homepage": "https://github.com/NVIDIA/egl-wayland",
"Version": "1.1.1",
"DownloadLocation": "https://raw.githubusercontent.com/NVIDIA/egl-wayland/1.1.1/wayland-eglstream/wayland-eglstream-controller.xml",
"LicenseId": "MIT",
"License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt",

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@
reset.
</description>
<interface name="zxdg_output_manager_v1" version="2">
<interface name="zxdg_output_manager_v1" version="3">
<description summary="manage xdg_output objects">
A global factory interface for xdg_output objects.
</description>
@ -77,12 +77,17 @@
</request>
</interface>
<interface name="zxdg_output_v1" version="2">
<interface name="zxdg_output_v1" version="3">
<description summary="compositor logical output region">
An xdg_output describes part of the compositor geometry.
This typically corresponds to a monitor that displays part of the
compositor space.
For objects version 3 onwards, after all xdg_output properties have been
sent (when the object is created and when properties are updated), a
wl_output.done event is sent. This allows changes to the output
properties to be seen as atomic, even if they happen via multiple events.
</description>
<request name="destroy" type="destructor">
@ -157,6 +162,10 @@
This allows changes to the xdg_output properties to be seen as
atomic, even if they happen via multiple events.
For objects version 3 onwards, this event is deprecated. Compositors
are not required to send it anymore and must send wl_output.done
instead.
</description>
</event>
@ -197,10 +206,12 @@
output via :1'.
The description event is sent after creating an xdg_output (see
xdg_output_manager.get_xdg_output). This event is only sent once per
xdg_output_manager.get_xdg_output) and whenever the description
changes. The description is optional, and may not be sent at all.
For objects of version 2 and lower, this event is only sent once per
xdg_output, and the description does not change over the lifetime of
the wl_output global. The description is optional, and may not be sent
at all.
the wl_output global.
</description>
<arg name="description" type="string" summary="output description"/>
</event>

View File

@ -118,7 +118,9 @@
child surface relative to a parent surface. Rules can be defined to ensure
the child surface remains within the visible area's borders, and to
specify how the child surface changes its position, such as sliding along
an axis, or flipping around a rectangle.
an axis, or flipping around a rectangle. These positioner-created rules are
constrained by the requirement that a child surface must intersect with or
be at least partially adjacent to its parent surface.
See the various requests for details about possible rules.
@ -941,7 +943,8 @@
The x and y arguments passed when creating the popup object specify
where the top left of the popup should be placed, relative to the
local surface coordinates of the parent surface. See
xdg_surface.get_popup.
xdg_surface.get_popup. An xdg_popup must intersect with or be at least
partially adjacent to its parent surface.
The client must call wl_surface.commit on the corresponding wl_surface
for the xdg_popup state to take effect.

View File

@ -29,7 +29,7 @@
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="xdg_wm_base" version="1">
<interface name="xdg_wm_base" version="2">
<description summary="create desktop-style surfaces">
The xdg_wm_base interface is exposed as a global object enabling clients
to turn their wl_surfaces into windows in a desktop environment. It
@ -101,7 +101,7 @@
<description summary="check if the client is alive">
The ping event asks the client if it's still alive. Pass the
serial specified in the event back to the compositor by sending
a "pong" request back with the specified serial. See xdg_wm_base.ping.
a "pong" request back with the specified serial. See xdg_wm_base.pong.
Compositors can use this to determine if the client is still
alive. It's unspecified what will happen if the client doesn't
@ -115,7 +115,7 @@
</event>
</interface>
<interface name="xdg_positioner" version="1">
<interface name="xdg_positioner" version="2">
<description summary="child surface positioner">
The xdg_positioner provides a collection of rules for the placement of a
child surface relative to a parent surface. Rules can be defined to ensure
@ -359,7 +359,7 @@
</request>
</interface>
<interface name="xdg_surface" version="1">
<interface name="xdg_surface" version="2">
<description summary="desktop user interface surface base interface">
An interface that may be implemented by a wl_surface, for
implementations that provide a desktop-style user interface.
@ -528,7 +528,7 @@
</event>
</interface>
<interface name="xdg_toplevel" version="1">
<interface name="xdg_toplevel" version="2">
<description summary="toplevel surface">
This interface defines an xdg_surface role which allows a surface to,
among other things, set window-like properties such as maximize,
@ -604,6 +604,9 @@
For example, "org.freedesktop.FooViewer" where the .desktop file is
"org.freedesktop.FooViewer.desktop".
Like other properties, a set_app_id request can be sent after the
xdg_toplevel has been mapped to update the property.
See the desktop-entry specification [0] for more details on
application identifiers and how they relate to well-known D-Bus
names and .desktop files.
@ -724,6 +727,9 @@
<description summary="the surface is maximized">
The surface is maximized. The window geometry specified in the configure
event must be obeyed by the client.
The client should draw without shadow or other
decoration outside of the window geometry.
</description>
</entry>
<entry name="fullscreen" value="2" summary="the surface is fullscreen">
@ -750,6 +756,30 @@
keyboard or pointer focus.
</description>
</entry>
<entry name="tiled_left" value="5" since="2">
<description summary="the surface is tiled">
The window is currently in a tiled layout and the left edge is
considered to be adjacent to another part of the tiling grid.
</description>
</entry>
<entry name="tiled_right" value="6" since="2">
<description summary="the surface is tiled">
The window is currently in a tiled layout and the right edge is
considered to be adjacent to another part of the tiling grid.
</description>
</entry>
<entry name="tiled_top" value="7" since="2">
<description summary="the surface is tiled">
The window is currently in a tiled layout and the top edge is
considered to be adjacent to another part of the tiling grid.
</description>
</entry>
<entry name="tiled_bottom" value="8" since="2">
<description summary="the surface is tiled">
The window is currently in a tiled layout and the bottom edge is
considered to be adjacent to another part of the tiling grid.
</description>
</entry>
</enum>
<request name="set_max_size">
@ -839,12 +869,11 @@
Maximize the surface.
After requesting that the surface should be maximized, the compositor
will respond by emitting a configure event with the "maximized" state
and the required window geometry. The client should then update its
content, drawing it in a maximized state, i.e. without shadow or other
decoration outside of the window geometry. The client must also
acknowledge the configure when committing the new content (see
ack_configure).
will respond by emitting a configure event. Whether this configure
actually sets the window maximized is subject to compositor policies.
The client must then update its content, drawing in the configured
state. The client must also acknowledge the configure when committing
the new content (see ack_configure).
It is up to the compositor to decide how and where to maximize the
surface, for example which output and what region of the screen should
@ -854,8 +883,8 @@
a configure event with the "maximized" state.
If the surface is in a fullscreen state, this request has no direct
effect. It will alter the state the surface is returned to when
unmaximized if not overridden by the compositor.
effect. It may alter the state the surface is returned to when
unmaximized unless overridden by the compositor.
</description>
</request>
@ -864,13 +893,13 @@
Unmaximize the surface.
After requesting that the surface should be unmaximized, the compositor
will respond by emitting a configure event without the "maximized"
state. If available, the compositor will include the window geometry
dimensions the window had prior to being maximized in the configure
event. The client must then update its content, drawing it in a
regular state, i.e. potentially with shadow, etc. The client must also
acknowledge the configure when committing the new content (see
ack_configure).
will respond by emitting a configure event. Whether this actually
un-maximizes the window is subject to compositor policies.
If available and applicable, the compositor will include the window
geometry dimensions the window had prior to being maximized in the
configure event. The client must then update its content, drawing it in
the configured state. The client must also acknowledge the configure
when committing the new content (see ack_configure).
It is up to the compositor to position the surface after it was
unmaximized; usually the position the surface had before maximizing, if
@ -880,8 +909,8 @@
emit a configure event without the "maximized" state.
If the surface is in a fullscreen state, this request has no direct
effect. It will alter the state the surface is returned to when
unmaximized if not overridden by the compositor.
effect. It may alter the state the surface is returned to when
unmaximized unless overridden by the compositor.
</description>
</request>
@ -890,10 +919,10 @@
Make the surface fullscreen.
After requesting that the surface should be fullscreened, the
compositor will respond by emitting a configure event with the
"fullscreen" state and the fullscreen window geometry. The client must
also acknowledge the configure when committing the new content (see
ack_configure).
compositor will respond by emitting a configure event. Whether the
client is actually put into a fullscreen state is subject to compositor
policies. The client must also acknowledge the configure when
committing the new content (see ack_configure).
The output passed by the request indicates the client's preference as
to which display it should be set fullscreen on. If this value is NULL,
@ -919,8 +948,9 @@
Make the surface no longer fullscreen.
After requesting that the surface should be unfullscreened, the
compositor will respond by emitting a configure event without the
"fullscreen" state.
compositor will respond by emitting a configure event.
Whether this actually removes the fullscreen state of the client is
subject to compositor policies.
Making a surface unfullscreen sets states for the surface based on the following:
* the state(s) it may have had before becoming fullscreen
@ -989,7 +1019,7 @@
</event>
</interface>
<interface name="xdg_popup" version="1">
<interface name="xdg_popup" version="2">
<description summary="short-lived, popup surfaces for menus">
A popup surface is a short-lived, temporary surface. It can be used to
implement for example menus, popovers, tooltips and other similar user
@ -1007,9 +1037,6 @@
surface of their own is clicked should dismiss the popup using the destroy
request.
The parent surface must have either the xdg_toplevel or xdg_popup surface
role.
A newly created xdg_popup will be stacked on top of all previously created
xdg_popup surfaces associated with the same xdg_toplevel.

View File

@ -23,8 +23,11 @@ qtConfig(xkbcommon) {
QT_FOR_PRIVATE += xkbcommon_support-private
}
qtHaveModule(linuxaccessibility_support_private): \
QT_PRIVATE += linuxaccessibility_support_private
qtHaveModule(platformcompositor_support-private): \
QT_PRIVATE += platformcompositor_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT_PRIVATE += linuxaccessibility_support-private
QMAKE_USE += wayland-client
@ -36,6 +39,7 @@ WAYLANDCLIENTSOURCES += \
../extensions/qt-key-unstable-v1.xml \
../extensions/qt-windowmanager.xml \
../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
../3rdparty/protocol/tablet-unstable-v2.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
../3rdparty/protocol/xdg-output-unstable-v1.xml \
../3rdparty/protocol/wayland.xml
@ -52,6 +56,7 @@ SOURCES += qwaylandintegration.cpp \
qwaylandextendedsurface.cpp \
qwaylandsubsurface.cpp \
qwaylandsurface.cpp \
qwaylandtabletv2.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
@ -77,6 +82,7 @@ HEADERS += qwaylandintegration_p.h \
qwaylandextendedsurface_p.h \
qwaylandsubsurface_p.h \
qwaylandsurface_p.h \
qwaylandtabletv2_p.h \
qwaylandtouch_p.h \
qwaylandqtkey_p.h \
qwaylandabstractdecoration_p.h \

View File

@ -134,12 +134,15 @@
"#endif"
]
},
"use": "egl"
"use": "egl drm"
},
"vulkan-server-buffer": {
"label": "Vulkan Buffer Sharing",
"type": "compile",
"test": {
"head": [
"#define VK_USE_PLATFORM_WAYLAND_KHR 1"
],
"include": [
"vulkan/vulkan.h"
],
@ -150,6 +153,21 @@
"return 0;"
]
}
},
"egl_1_5-wayland": {
"label": "EGL 1.5 with Wayland Platform",
"type": "compile",
"test": {
"include": [
"EGL/egl.h",
"EGL/eglext.h",
"wayland-client.h"
],
"main": [
"eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, (struct wl_display *)(nullptr), nullptr);"
]
},
"use": "egl"
}
},
@ -200,7 +218,7 @@
},
"wayland-egl": {
"label": "EGL",
"condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl",
"condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl && (!config.qnx || tests.egl_1_5-wayland)",
"output": [ "privateFeature" ]
},
"wayland-brcm": {
@ -220,7 +238,7 @@
},
"wayland-drm-egl-server-buffer": {
"label": "DRM EGL",
"condition": "features.wayland-client && features.opengl && features.egl && tests.drm-egl-server",
"condition": "features.wayland-client && features.opengl && features.egl && tests.drm-egl-server && (!config.qnx || tests.egl_1_5-wayland)",
"output": [ "privateFeature" ]
},
"wayland-libhybris-egl-server-buffer": {
@ -235,7 +253,7 @@
},
"wayland-vulkan-server-buffer": {
"label": "Vulkan-based server buffer integration",
"condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer",
"condition": "features.wayland-client && features.vulkan && features.opengl && features.egl && tests.vulkan-server-buffer",
"output": [ "privateFeature" ]
},
"wayland-shm-emulation-server-buffer": {

View File

@ -42,6 +42,7 @@
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QLinearGradient>
#include <QtGui/QPainterPath>
#include <qpa/qwindowsysteminterface.h>

View File

@ -1,7 +1,7 @@
# We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private
QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/dmabuf-server/dmabuf-server.pri)

View File

@ -1,7 +1,7 @@
# We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private
QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/drm-egl-server/drm-egl-server.pri)

View File

@ -1,7 +1,7 @@
# We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private
QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri)

View File

@ -1,7 +1,7 @@
# We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private
QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/vulkan-server/vulkan-server.pri)

View File

@ -50,10 +50,11 @@ QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zw
, m_shell(shell)
, m_window(window)
{
auto screen = static_cast<QWaylandScreen *>(m_window->screen());
auto *screen = m_window->waylandScreen();
auto *output = screen ? screen->output() : nullptr;
m_shell->present_surface(m_window->wlSurface(),
QtWayland::zwp_fullscreen_shell_v1::present_method_default,
screen->output());
output);
}
} // namespace QtWaylandClient

View File

@ -74,7 +74,7 @@ QWaylandCursorTheme::~QWaylandCursorTheme()
wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
{
if (struct wl_cursor *cursor = m_cursors.value(shape, nullptr))
if (struct wl_cursor *cursor = m_cursors[shape])
return cursor;
static Q_CONSTEXPR struct ShapeAndName {
@ -206,7 +206,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
ShapeAndName{shape, ""}, byShape);
for (auto it = p.first; it != p.second; ++it) {
if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) {
m_cursors.insert(shape, cursor);
m_cursors[shape] = cursor;
return cursor;
}
}
@ -219,7 +219,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
return nullptr;
}
::wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation)
::wl_cursor *QWaylandCursorTheme::cursor(Qt::CursorShape shape)
{
struct wl_cursor *waylandCursor = nullptr;
@ -237,15 +237,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
return nullptr;
}
int frame = wl_cursor_frame(waylandCursor, millisecondsIntoAnimation);
::wl_cursor_image *image = waylandCursor->images[frame];
::wl_buffer *buffer = wl_cursor_image_get_buffer(image);
if (!buffer) {
qCWarning(lcQpaWayland) << "Could not find buffer for cursor";
return nullptr;
}
return image;
return waylandCursor;
}
QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)

View File

@ -75,7 +75,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
public:
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
~QWaylandCursorTheme();
::wl_cursor_image *cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation = 0);
::wl_cursor *cursor(Qt::CursorShape shape);
private:
enum WaylandCursor {
@ -109,13 +109,15 @@ private:
ResizeNorthWestCursor,
ResizeSouthEastCursor,
ResizeNorthEastCursor,
ResizeSouthWestCursor
ResizeSouthWestCursor,
NumWaylandCursors
};
explicit QWaylandCursorTheme(struct ::wl_cursor_theme *theme) : m_theme(theme) {}
struct ::wl_cursor *requestCursor(WaylandCursor shape);
struct ::wl_cursor_theme *m_theme = nullptr;
QMap<WaylandCursor, wl_cursor *> m_cursors;
wl_cursor *m_cursors[NumWaylandCursors] = {};
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor
@ -129,7 +131,6 @@ public:
void setPos(const QPoint &pos) override;
static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor);
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
private:
QWaylandDisplay *mDisplay = nullptr;

View File

@ -157,7 +157,9 @@ void QWaylandDataDevice::data_device_drop()
return;
}
QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions);
QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions,
QGuiApplication::mouseButtons(),
QGuiApplication::keyboardModifiers());
if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response);
@ -187,7 +189,9 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface,
supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}
const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions);
const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
QGuiApplication::mouseButtons(),
QGuiApplication::keyboardModifiers());
if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
@ -203,7 +207,9 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface,
void QWaylandDataDevice::data_device_leave()
{
if (m_dragWindow)
QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction);
QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction,
QGuiApplication::mouseButtons(),
QGuiApplication::keyboardModifiers());
QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
if (!drag) {
@ -232,7 +238,9 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe
supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}
QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions);
QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
QGuiApplication::mouseButtons(),
QGuiApplication::keyboardModifiers());
if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);

View File

@ -170,24 +170,27 @@ int QWaylandMimeData::readData(int fd, QByteArray &data) const
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
if (ready < 0) {
qWarning() << "QWaylandDataOffer: select() failed";
return -1;
} else if (ready == 0) {
qWarning("QWaylandDataOffer: timeout reading from pipe");
return -1;
} else {
char buf[4096];
int n = QT_READ(fd, buf, sizeof buf);
Q_FOREVER {
int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
if (ready < 0) {
qWarning() << "QWaylandDataOffer: select() failed";
return -1;
} else if (ready == 0) {
qWarning("QWaylandDataOffer: timeout reading from pipe");
return -1;
} else {
char buf[4096];
int n = QT_READ(fd, buf, sizeof buf);
if (n > 0) {
data.append(buf, n);
n = readData(fd, data);
} else if (n < 0) {
qWarning("QWaylandDataOffer: read() failed");
if (n < 0) {
qWarning("QWaylandDataOffer: read() failed");
return -1;
} else if (n == 0) {
return 0;
} else if (n > 0) {
data.append(buf, n);
}
}
return n;
}
}

View File

@ -69,6 +69,7 @@
#include "qwaylandextendedsurface_p.h"
#include "qwaylandsubsurface_p.h"
#include "qwaylandtouch_p.h"
#include "qwaylandtabletv2_p.h"
#include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
@ -114,6 +115,10 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
return nullptr;
}
// Make sure we don't pass NULL surfaces to libwayland (crashes)
Q_ASSERT(parent->wlSurface());
Q_ASSERT(window->wlSurface());
return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
}
@ -184,6 +189,18 @@ QWaylandDisplay::~QWaylandDisplay(void)
wl_display_disconnect(mDisplay);
}
void QWaylandDisplay::ensureScreen()
{
if (!mScreens.empty() || mPlaceholderScreen)
return; // There are real screens or we already have a fake one
qCInfo(lcQpaWayland) << "Creating a fake screen in order for Qt not to crash";
mPlaceholderScreen = new QPlatformPlaceholderScreen();
QWindowSystemInterface::handleScreenAdded(mPlaceholderScreen);
Q_ASSERT(!QGuiApplication::screens().empty());
}
void QWaylandDisplay::checkError() const
{
int ecode = wl_display_get_error(mDisplay);
@ -249,8 +266,7 @@ void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bo
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
for (int i = 0; i < mScreens.size(); ++i) {
QWaylandScreen *screen = static_cast<QWaylandScreen *>(mScreens.at(i));
for (auto screen : qAsConst(mScreens)) {
if (screen->output() == output)
return screen;
}
@ -263,6 +279,11 @@ void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
return;
mScreens.append(screen);
QWindowSystemInterface::handleScreenAdded(screen);
if (mPlaceholderScreen) {
QWindowSystemInterface::handleScreenRemoved(mPlaceholderScreen);
// handleScreenRemoved deletes the platform screen
mPlaceholderScreen = nullptr;
}
}
void QWaylandDisplay::waitForScreens()
@ -310,6 +331,8 @@ 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_tablet_manager_v2")) {
mTabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
#if QT_CONFIG(wayland_client_primary_selection)
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
@ -328,7 +351,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
forceRoundTrip();
}
} else if (interface == QLatin1String("zxdg_output_manager_v1")) {
mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version))));
mXdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
for (auto *screen : qAsConst(mWaitingScreens))
screen->initXdgOutput(xdgOutputManager());
forceRoundTrip();
@ -358,6 +381,8 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
for (QWaylandScreen *screen : qAsConst(mScreens)) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
// If this is the last screen, we have to add a fake screen, or Qt will break.
ensureScreen();
QWindowSystemInterface::handleScreenRemoved(screen);
break;
}

View File

@ -76,11 +76,11 @@ QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
class QSocketNotifier;
class QPlatformScreen;
class QPlatformPlaceholderScreen;
namespace QtWayland {
class qt_surface_extension;
class zwp_text_input_manager_v2;
class zxdg_output_manager_v1;
}
namespace QtWaylandClient {
@ -90,12 +90,14 @@ Q_WAYLAND_CLIENT_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaWayland);
class QWaylandInputDevice;
class QWaylandBuffer;
class QWaylandScreen;
class QWaylandXdgOutputManagerV1;
class QWaylandClientBufferIntegration;
class QWaylandWindowManagerIntegration;
class QWaylandDataDeviceManager;
#if QT_CONFIG(wayland_client_primary_selection)
class QWaylandPrimarySelectionDeviceManagerV1;
#endif
class QWaylandTabletManagerV2;
class QWaylandTouchExtension;
class QWaylandQtKeyExtension;
class QWaylandWindow;
@ -124,6 +126,8 @@ public:
#endif
QList<QWaylandScreen *> screens() const { return mScreens; }
QPlatformPlaceholderScreen *placeholderScreen() const { return mPlaceholderScreen; }
void ensureScreen();
QWaylandScreen *screenForOutput(struct wl_output *output) const;
void handleScreenInitialized(QWaylandScreen *screen);
@ -157,10 +161,11 @@ public:
QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); }
#endif
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
QWaylandTabletManagerV2 *tabletManager() const { return mTabletManager.data(); }
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QtWayland::zxdg_output_manager_v1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; }
@ -228,6 +233,7 @@ private:
QScopedPointer<QWaylandShm> mShm;
QList<QWaylandScreen *> mWaitingScreens;
QList<QWaylandScreen *> mScreens;
QPlatformPlaceholderScreen *mPlaceholderScreen = nullptr;
QList<QWaylandInputDevice *> mInputDevices;
QList<Listener> mRegistryListeners;
QWaylandIntegration *mWaylandIntegration = nullptr;
@ -243,12 +249,13 @@ private:
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
QScopedPointer<QWaylandTabletManagerV2> mTabletManager;
#if QT_CONFIG(wayland_client_primary_selection)
QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
#endif
QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QtWayland::zxdg_output_manager_v1> mXdgOutputManager;
QScopedPointer<QWaylandXdgOutputManagerV1> mXdgOutputManager;
QSocketNotifier *mReadNotifier = nullptr;
int mFd = -1;
int mWritableNotificationFd = -1;

View File

@ -50,6 +50,7 @@
#if QT_CONFIG(wayland_client_primary_selection)
#include "qwaylandprimaryselectionv1_p.h"
#endif
#include "qwaylandtabletv2_p.h"
#include "qwaylandtouch_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
@ -72,6 +73,7 @@
#endif
#include <QtGui/QGuiApplication>
#include <QtGui/QTouchDevice>
QT_BEGIN_NAMESPACE
@ -88,7 +90,7 @@ QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
// or the server didn't send an enter event first.
return;
}
mRepeatTimer.setInterval(mRepeatRate);
mRepeatTimer.setInterval(1000 / mRepeatRate);
handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers,
mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers,
mRepeatKey.text, true);
@ -143,6 +145,12 @@ QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const
QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
: mParent(seat)
{
#if QT_CONFIG(cursor)
mCursor.frameTimer.setSingleShot(true);
mCursor.frameTimer.callOnTimeout([&]() {
cursorTimerCallback();
});
#endif
}
QWaylandInputDevice::Pointer::~Pointer()
@ -224,7 +232,7 @@ public:
if (animated) {
m_frameCallback.reset(new WlCallback(frame(), [this](uint32_t time){
Q_UNUSED(time);
m_pointer->updateCursor();
m_pointer->cursorFrameCallback();
}));
}
commit();
@ -283,8 +291,8 @@ void QWaylandInputDevice::Pointer::updateCursorTheme()
if (!mCursor.theme)
return; // A warning has already been printed in loadCursorTheme
if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) {
int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square
if (auto *arrow = mCursor.theme->cursor(Qt::ArrowCursor)) {
int arrowPixelSize = qMax(arrow->images[0]->width, arrow->images[0]->height); // Not all cursor themes are square
while (scale > 1 && arrowPixelSize / scale < cursorSize())
--scale;
} else {
@ -326,12 +334,26 @@ void QWaylandInputDevice::Pointer::updateCursor()
// Set from shape using theme
uint time = seat()->mCursor.animationTimer.elapsed();
if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape, time)) {
if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) {
uint duration = 0;
int frame = wl_cursor_frame_and_duration(waylandCursor, time, &duration);
::wl_cursor_image *image = waylandCursor->images[frame];
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
if (!buffer) {
qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape;
return;
}
int bufferScale = mCursor.themeBufferScale;
QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
QSize size = QSize(image->width, image->height) / bufferScale;
bool animated = image->delay > 0;
bool animated = duration > 0;
if (animated) {
mCursor.gotFrameCallback = false;
mCursor.gotTimerCallback = false;
mCursor.frameTimer.start(duration);
}
getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated);
return;
}
@ -346,6 +368,22 @@ CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface()
return mCursor.surface.get();
}
void QWaylandInputDevice::Pointer::cursorTimerCallback()
{
mCursor.gotTimerCallback = true;
if (mCursor.gotFrameCallback) {
updateCursor();
}
}
void QWaylandInputDevice::Pointer::cursorFrameCallback()
{
mCursor.gotFrameCallback = true;
if (mCursor.gotTimerCallback) {
updateCursor();
}
}
#endif // QT_CONFIG(cursor)
QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
@ -382,6 +420,8 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
if (mQDisplay->textInputManager())
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
if (auto *tm = mQDisplay->tabletManager())
mTabletSeat.reset(new QWaylandTabletSeatV2(tm, this));
}
QWaylandInputDevice::~QWaylandInputDevice()
@ -594,8 +634,8 @@ class EnterEvent : public QWaylandPointerEvent
{
public:
EnterEvent(QWaylandWindow *surface, const QPointF &local, const QPointF &global)
: QWaylandPointerEvent(QWaylandPointerEvent::Enter, Qt::NoScrollPhase, surface, 0,
local, global, nullptr, Qt::NoModifier)
: QWaylandPointerEvent(QEvent::Enter, Qt::NoScrollPhase, surface, 0,
local, global, Qt::NoButton, Qt::NoButton, Qt::NoModifier)
{}
};
@ -639,8 +679,8 @@ class LeaveEvent : public QWaylandPointerEvent
{
public:
LeaveEvent(QWaylandWindow *surface, const QPointF &localPos, const QPointF &globalPos)
: QWaylandPointerEvent(QWaylandPointerEvent::Leave, Qt::NoScrollPhase, surface, 0,
localPos, globalPos, nullptr, Qt::NoModifier)
: QWaylandPointerEvent(QEvent::Leave, Qt::NoScrollPhase, surface, 0,
localPos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier)
{}
};
@ -669,8 +709,8 @@ class MotionEvent : public QWaylandPointerEvent
public:
MotionEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QWaylandPointerEvent::Motion, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, modifiers)
: QWaylandPointerEvent(QEvent::MouseMove, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, Qt::NoButton, modifiers)
{
}
};
@ -708,9 +748,10 @@ class PressEvent : public QWaylandPointerEvent
{
public:
PressEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QWaylandPointerEvent::Press, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, modifiers)
const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QEvent::MouseButtonPress, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, button, modifiers)
{
}
};
@ -719,9 +760,10 @@ class ReleaseEvent : public QWaylandPointerEvent
{
public:
ReleaseEvent(QWaylandWindow *surface, ulong timestamp, const QPointF &localPos,
const QPointF &globalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QWaylandPointerEvent::Release, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, modifiers)
const QPointF &globalPos, Qt::MouseButtons buttons, Qt::MouseButton button,
Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QEvent::MouseButtonRelease, Qt::NoScrollPhase, surface,
timestamp, localPos, globalPos, buttons, button, modifiers)
{
}
};
@ -782,9 +824,9 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
}
if (state)
setFrameEvent(new PressEvent(window, time, pos, global, mButtons, mParent->modifiers()));
setFrameEvent(new PressEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers()));
else
setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, mParent->modifiers()));
setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, qt_button, mParent->modifiers()));
}
void QWaylandInputDevice::Pointer::invalidateFocus()
@ -812,7 +854,7 @@ public:
WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers)
: QWaylandPointerEvent(QWaylandPointerEvent::Wheel, phase, surface, timestamp,
: QWaylandPointerEvent(QEvent::Wheel, phase, surface, timestamp,
local, global, pixelDelta, angleDelta, source, modifiers)
{
}
@ -968,6 +1010,8 @@ bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const
case axis_source_finger:
case axis_source_continuous:
return !delta.isNull();
default:
return false;
}
}
@ -1048,11 +1092,13 @@ void QWaylandInputDevice::Pointer::flushFrameEvent()
if (auto *event = mFrameData.event) {
if (auto window = event->surface) {
window->handleMouse(mParent, *event);
} else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) {
} else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
// If the window has been destroyed, we still need to report an up event, but it can't
// be handled by the destroyed window (obviously), so send the event here instead.
QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local,
event->global, event->buttons, event->modifiers);
event->global, event->buttons,
event->button, event->type,
event->modifiers);// , Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
}
delete mFrameData.event;
mFrameData.event = nullptr;
@ -1351,7 +1397,7 @@ void QWaylandInputDevice::Touch::touch_cancel()
void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition)
{
auto end = mTouch->mPendingTouchPoints.end();
auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; });
auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](const QWindowSystemInterface::TouchPoint &tp){ return tp.id == id; });
if (it == end) {
it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint());
it->id = id;
@ -1372,8 +1418,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co
return;
tp.area = QRectF(0, 0, 8, 8);
QMargins margins = win->frameMargins();
QPointF localPosition = surfacePosition - QPointF(margins.left(), margins.top());
QPointF localPosition = win->mapFromWlSurface(surfacePosition);
// TODO: This doesn't account for high dpi scaling for the delta, but at least it matches
// what we have for mouse input.
QPointF delta = localPosition - localPosition.toPoint();
@ -1381,7 +1426,10 @@ void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, co
tp.area.moveCenter(globalPosition);
}
tp.state = state;
// If the touch point was pressed earlier this frame, we don't want to overwrite its state.
if (tp.state != Qt::TouchPointPressed)
tp.state = state;
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
}

View File

@ -89,6 +89,7 @@ class QWaylandDisplay;
#if QT_CONFIG(wayland_client_primary_selection)
class QWaylandPrimarySelectionDeviceV1;
#endif
class QWaylandTabletSeatV2;
class QWaylandTextInput;
#if QT_CONFIG(cursor)
class QWaylandCursorTheme;
@ -127,6 +128,9 @@ public:
QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
#endif
void setTabletSeat(QWaylandTabletSeatV2 *tabletSeat);
QWaylandTabletSeatV2* tabletSeat() const;
void setTextInput(QWaylandTextInput *textInput);
QWaylandTextInput *textInput() const;
@ -183,6 +187,7 @@ private:
Touch *mTouch = nullptr;
QScopedPointer<QWaylandTextInput> mTextInput;
QScopedPointer<QWaylandTabletSeatV2> mTabletSeat;
uint32_t mTime = 0;
uint32_t mSerial = 0;
@ -286,6 +291,8 @@ public:
int idealCursorScale() const;
void updateCursorTheme();
void updateCursor();
void cursorTimerCallback();
void cursorFrameCallback();
CursorSurface *getOrCreateCursorSurface();
#endif
QWaylandInputDevice *seat() const { return mParent; }
@ -325,6 +332,9 @@ public:
QWaylandCursorTheme *theme = nullptr;
int themeBufferScale = 0;
QScopedPointer<CursorSurface> surface;
QTimer frameTimer;
bool gotFrameCallback = false;
bool gotTimerCallback = false;
} mCursor;
#endif
QPointF mSurfacePos;
@ -396,29 +406,21 @@ class QWaylandPointerEvent
{
Q_GADGET
public:
enum Type {
Enter,
Leave,
Motion,
Press,
Release,
Wheel
};
Q_ENUM(Type)
inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
inline QWaylandPointerEvent(QEvent::Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
ulong timestamp, const QPointF &localPos, const QPointF &globalPos,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
Qt::MouseButtons buttons, Qt::MouseButton button,
Qt::KeyboardModifiers modifiers)
: type(type)
, phase(phase)
, timestamp(timestamp)
, local(localPos)
, global(globalPos)
, buttons(buttons)
, button(button)
, modifiers(modifiers)
, surface(surface)
{}
inline QWaylandPointerEvent(Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
inline QWaylandPointerEvent(QEvent::Type type, Qt::ScrollPhase phase, QWaylandWindow *surface,
ulong timestamp, const QPointF &local, const QPointF &global,
const QPoint &pixelDelta, const QPoint &angleDelta,
Qt::MouseEventSource source,
@ -435,12 +437,13 @@ public:
, surface(surface)
{}
Type type;
QEvent::Type type = QEvent::None;
Qt::ScrollPhase phase = Qt::NoScrollPhase;
ulong timestamp = 0;
QPointF local;
QPointF global;
Qt::MouseButtons buttons;
Qt::MouseButton button = Qt::NoButton; // Button that caused the event (QMouseEvent::button)
Qt::KeyboardModifiers modifiers;
QPoint pixelDelta;
QPoint angleDelta;

View File

@ -66,7 +66,10 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformcursor.h>
#include <QtGui/QSurfaceFormat>
#if QT_CONFIG(opengl)
#include <QtGui/QOpenGLContext>
#include <QtPlatformCompositorSupport/qpa/qplatformbackingstoreopenglsupport.h>
#endif // QT_CONFIG(opengl)
#include <QSocketNotifier>
#include <qpa/qplatforminputcontextfactory_p.h>
@ -165,10 +168,10 @@ QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) cons
#if QT_CONFIG(vulkan)
if (window->surfaceType() == QSurface::VulkanSurface)
return new QWaylandVulkanWindow(window);
return new QWaylandVulkanWindow(window, mDisplay.data());
#endif // QT_CONFIG(vulkan)
return new QWaylandShmWindow(window);
return new QWaylandShmWindow(window, mDisplay.data());
}
#if QT_CONFIG(opengl)
@ -182,7 +185,11 @@ QPlatformOpenGLContext *QWaylandIntegration::createPlatformOpenGLContext(QOpenGL
QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow *window) const
{
return new QWaylandShmBackingStore(window);
auto *backingStore = new QWaylandShmBackingStore(window, mDisplay.data());
#if QT_CONFIG(opengl)
backingStore->setOpenGLSupport(new QPlatformBackingStoreOpenGLSupport(backingStore));
#endif
return backingStore;
}
QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
@ -200,10 +207,8 @@ void QWaylandIntegration::initialize()
QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data());
QObject::connect(sn, SIGNAL(activated(int)), mDisplay.data(), SLOT(flushRequests()));
if (mDisplay->screens().isEmpty()) {
qWarning() << "Running on a compositor with no screens is not supported";
::exit(EXIT_FAILURE);
}
// Qt does not support running with no screens
mDisplay->ensureScreen();
}
QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const
@ -427,6 +432,8 @@ void QWaylandIntegration::initializeShellIntegration()
qCWarning(lcQpaWayland) << "Loading shell integration failed.";
qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells;
}
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
}
QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id)

View File

@ -53,6 +53,12 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, uint id, uint version)
: QtWayland::zxdg_output_manager_v1(display->wl_registry(), id, qMin(3u, version))
, m_version(qMin(3u, version))
{
}
QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
: QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2))
, m_outputId(id)
@ -95,7 +101,7 @@ void QWaylandScreen::maybeInitialize()
updateXdgOutputProperties();
}
void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager)
void QWaylandScreen::initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager)
{
Q_ASSERT(xdgOutputManager);
if (zxdg_output_v1::isInitialized())
@ -165,11 +171,18 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
{
QList<QPlatformScreen *> list;
const QList<QWaylandScreen*> screens = mWaylandDisplay->screens();
list.reserve(screens.count());
auto *placeholder = mWaylandDisplay->placeholderScreen();
list.reserve(screens.count() + (placeholder ? 1 : 0));
for (QWaylandScreen *screen : qAsConst(screens)) {
if (screen->screen())
list << screen;
}
if (placeholder)
list << placeholder;
return list;
}
@ -210,9 +223,11 @@ QPlatformCursor *QWaylandScreen::cursor() const
}
#endif // QT_CONFIG(cursor)
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window)
{
QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window);
if (platformScreen->isPlaceholder())
return nullptr;
return static_cast<QWaylandScreen *>(platformScreen);
}
@ -262,10 +277,15 @@ void QWaylandScreen::output_scale(int32_t factor)
void QWaylandScreen::output_done()
{
mOutputDone = true;
if (mInitialized)
if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3)
mXdgOutputDone = true;
if (mInitialized) {
updateOutputProperties();
else
if (zxdg_output_v1::isInitialized())
updateXdgOutputProperties();
} else {
maybeInitialize();
}
}
void QWaylandScreen::updateOutputProperties()
@ -316,6 +336,9 @@ void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height)
void QWaylandScreen::zxdg_output_v1_done()
{
if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3))
qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor";
mXdgOutputDone = true;
if (mInitialized)
updateXdgOutputProperties();

View File

@ -57,7 +57,6 @@
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwayland-xdg-output-unstable-v1.h>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -65,6 +64,14 @@ namespace QtWaylandClient {
class QWaylandDisplay;
class QWaylandCursor;
class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgOutputManagerV1 : public QtWayland::zxdg_output_manager_v1 {
public:
QWaylandXdgOutputManagerV1(QWaylandDisplay *display, uint id, uint version);
uint version() const { return m_version; }
private:
uint m_version = 1; // TODO: remove when we upgrade minimum libwayland requriement to 1.10
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandScreen : public QPlatformScreen, QtWayland::wl_output, QtWayland::zxdg_output_v1
{
public:
@ -73,7 +80,7 @@ public:
void maybeInitialize();
void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager);
void initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager);
QWaylandDisplay *display() const;

View File

@ -47,6 +47,7 @@
#include <QtCore/qstandardpaths.h>
#include <QtCore/qtemporaryfile.h>
#include <QtGui/QPainter>
#include <QtGui/QTransform>
#include <QMutexLocker>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
@ -151,9 +152,9 @@ QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn)
}
QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window)
QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display)
: QPlatformBackingStore(window)
, mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display())
, mDisplay(display)
{
}
@ -328,7 +329,7 @@ void QWaylandShmBackingStore::updateDecorations()
qreal dp = sourceImage.devicePixelRatio();
int dpWidth = int(sourceImage.width() / dp);
int dpHeight = int(sourceImage.height() / dp);
QMatrix sourceMatrix;
QTransform sourceMatrix;
sourceMatrix.scale(dp, dp);
QRect target; // needs to be in device independent pixels

View File

@ -88,7 +88,7 @@ private:
class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingStore
{
public:
QWaylandShmBackingStore(QWindow *window);
QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display);
~QWaylandShmBackingStore() override;
QPaintDevice *paintDevice() override;

View File

@ -49,8 +49,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandShmWindow::QWaylandShmWindow(QWindow *window)
: QWaylandWindow(window)
QWaylandShmWindow::QWaylandShmWindow(QWindow *window, QWaylandDisplay *display)
: QWaylandWindow(window, display)
{
}

View File

@ -61,7 +61,7 @@ namespace QtWaylandClient {
class Q_WAYLAND_CLIENT_EXPORT QWaylandShmWindow : public QWaylandWindow
{
public:
QWaylandShmWindow(QWindow *window);
QWaylandShmWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandShmWindow() override;
WindowType windowType() const override;

View File

@ -72,8 +72,12 @@ QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
{
auto *screen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(screen))
auto *platformScreen = qScreen->handle();
if (platformScreen->isPlaceholder())
return;
auto *waylandScreen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(waylandScreen))
emit screensChanged();
}

View File

@ -0,0 +1,332 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "qwaylandtabletv2_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandsurface_p.h"
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version)
: zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1)))
{
// Create tabletSeats for all seats.
// This only works if we get the manager after all seats
const auto seats = display->inputDevices();
for (auto *seat : seats)
createTabletSeat(seat);
}
QWaylandTabletSeatV2 *QWaylandTabletManagerV2::createTabletSeat(QWaylandInputDevice *seat)
{
return new QWaylandTabletSeatV2(this, seat);
}
QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat)
: QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat()))
{
}
QWaylandTabletSeatV2::~QWaylandTabletSeatV2()
{
for (auto *tablet : m_tablets)
tablet->destroy();
for (auto *tool : m_tools)
tool->destroy();
for (auto *pad : m_pads)
pad->destroy();
destroy();
}
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id)
{
auto *tablet = new QWaylandTabletV2(id);
m_tablets.push_back(tablet);
connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); });
}
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id)
{
auto *tool = new QWaylandTabletToolV2(id);
m_tools.push_back(tool);
connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); });
}
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id)
{
auto *pad = new QWaylandTabletPadV2(id);
m_pads.push_back(pad);
connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); });
}
QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet)
: QtWayland::zwp_tablet_v2(tablet)
{
}
void QWaylandTabletV2::zwp_tablet_v2_removed()
{
destroy();
delete this;
}
QWaylandTabletToolV2::QWaylandTabletToolV2(::zwp_tablet_tool_v2 *tool)
: QtWayland::zwp_tablet_tool_v2(tool)
{
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type)
{
m_toolType = type(tool_type);
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo)
{
m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability)
{
if (capability == capability_rotation)
m_hasRotation = true;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_done()
{
switch (m_toolType) {
case type::type_airbrush:
case type::type_brush:
case type::type_pencil:
case type::type_pen:
m_pointerType = QTabletEvent::PointerType::Pen;
break;
case type::type_eraser:
m_pointerType = QTabletEvent::PointerType::Eraser;
break;
case type::type_mouse:
case type::type_lens:
m_pointerType = QTabletEvent::PointerType::Cursor;
break;
case type::type_finger:
m_pointerType = QTabletEvent::PointerType::UnknownPointer;
break;
}
switch (m_toolType) {
case type::type_airbrush:
m_tabletDevice = QTabletEvent::TabletDevice::Airbrush;
break;
case type::type_brush:
case type::type_pencil:
case type::type_pen:
case type::type_eraser:
m_tabletDevice = m_hasRotation ? QTabletEvent::TabletDevice::RotationStylus : QTabletEvent::TabletDevice::Stylus;
break;
case type::type_lens:
m_tabletDevice = QTabletEvent::TabletDevice::Puck;
break;
case type::type_mouse:
case type::type_finger:
m_tabletDevice = QTabletEvent::TabletDevice::NoDevice;
break;
}
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed()
{
destroy();
delete this;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface)
{
Q_UNUSED(tablet);
Q_UNUSED(serial);
if (Q_UNLIKELY(!surface)) {
qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface";
return;
}
m_pending.enteredSurface = true;
m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface);
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out()
{
m_pending.enteredSurface = false;
m_pending.proximitySurface = nullptr;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial)
{
Q_UNUSED(serial);
m_pending.down = true;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_up()
{
m_pending.down = false;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y)
{
m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y));
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure)
{
const int maxPressure = 65535;
m_pending.pressure = qreal(pressure)/maxPressure;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance)
{
m_pending.distance = distance;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y)
{
m_pending.xTilt = wl_fixed_to_double(tilt_x);
m_pending.yTilt = wl_fixed_to_double(tilt_y);
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees)
{
m_pending.rotation = wl_fixed_to_double(degrees);
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position)
{
m_pending.slider = qreal(position) / 65535;
}
static Qt::MouseButton mouseButtonFromTablet(uint button)
{
switch (button) {
case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT
case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS
case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2
default:
return Qt::NoButton;
}
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state)
{
Q_UNUSED(serial);
Qt::MouseButton mouseButton = mouseButtonFromTablet(button);
if (state == button_state_pressed)
m_pending.buttons |= mouseButton;
else
m_pending.buttons &= ~mouseButton;
}
void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time)
{
if (m_pending.proximitySurface && !m_applied.proximitySurface) {
QWindowSystemInterface::handleTabletEnterProximityEvent(m_tabletDevice, m_pointerType, m_uid);
m_applied.proximitySurface = m_pending.proximitySurface;
}
if (!(m_pending == m_applied) && m_pending.proximitySurface) {
if (!m_pending.proximitySurface) {
qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring";
return;
}
QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object());
QWindow *window = waylandWindow->window();
ulong timestamp = time;
const QPointF localPosition = waylandWindow->mapFromWlSurface(m_pending.surfacePosition);
QPointF delta = localPosition - localPosition.toPoint();
QPointF globalPosition = window->mapToGlobal(localPosition.toPoint());
globalPosition += delta;
Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton;
buttons |= m_pending.buttons;
qreal pressure = m_pending.pressure;
int xTilt = int(m_pending.xTilt);
int yTilt = int(m_pending.yTilt);
qreal tangentialPressure = m_pending.slider;
qreal rotation = m_pending.rotation;
int z = int(m_pending.distance);
QWindowSystemInterface::handleTabletEvent(window, timestamp, localPosition, globalPosition,
m_tabletDevice, m_pointerType, buttons, pressure,
xTilt, yTilt, tangentialPressure, rotation, z, m_uid);
}
if (!m_pending.proximitySurface && m_applied.enteredSurface) {
QWindowSystemInterface::handleTabletLeaveProximityEvent(m_tabletDevice, m_pointerType, m_uid);
m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface
}
m_applied = m_pending;
}
// TODO: delete when upgrading to c++20
bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const {
return
down == o.down &&
proximitySurface.data() == o.proximitySurface.data() &&
enteredSurface == o.enteredSurface &&
surfacePosition == o.surfacePosition &&
distance == o.distance &&
pressure == o.pressure &&
rotation == o.rotation &&
xTilt == o.xTilt &&
yTilt == o.yTilt &&
slider == o.slider &&
buttons == o.buttons;
}
QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad)
: QtWayland::zwp_tablet_pad_v2(pad)
{
}
void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed()
{
destroy();
delete this;
}
} // namespace QtWaylandClient
QT_END_NAMESPACE

View File

@ -0,0 +1,191 @@
/****************************************************************************
**
** Copyright (C) 2019 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 QWAYLANDTABLETV2_P_H
#define QWAYLANDTABLETV2_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 <QtWaylandClient/private/qwayland-tablet-unstable-v2.h>
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
#include <QtGui/QTabletEvent>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QPointF>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
class QWaylandDisplay;
class QWaylandInputDevice;
class QWaylandSurface;
class QWaylandTabletSeatV2;
class QWaylandTabletV2;
class QWaylandTabletToolV2;
class QWaylandTabletPadV2;
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletManagerV2 : public QtWayland::zwp_tablet_manager_v2
{
public:
explicit QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version);
QWaylandTabletSeatV2 *createTabletSeat(QWaylandInputDevice *seat);
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletSeatV2 : public QObject, public QtWayland::zwp_tablet_seat_v2
{
Q_OBJECT
public:
explicit QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat);
~QWaylandTabletSeatV2() override;
protected:
void zwp_tablet_seat_v2_tablet_added(struct ::zwp_tablet_v2 *id) override;
void zwp_tablet_seat_v2_tool_added(struct ::zwp_tablet_tool_v2 *id) override;
void zwp_tablet_seat_v2_pad_added(struct ::zwp_tablet_pad_v2 *id) override;
private:
QVector<QWaylandTabletV2 *> m_tablets;
QVector<QWaylandTabletToolV2 *> m_tools;
QVector<QWaylandTabletPadV2 *> m_pads;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletV2 : public QObject, public QtWayland::zwp_tablet_v2
{
Q_OBJECT
public:
explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet);
protected:
// void zwp_tablet_v2_name(const QString &name) override;
// void zwp_tablet_v2_id(uint32_t vid, uint32_t pid) override;
// void zwp_tablet_v2_path(const QString &path) override;
// void zwp_tablet_v2_done() override;
void zwp_tablet_v2_removed() override;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletToolV2 : public QObject, public QtWayland::zwp_tablet_tool_v2
{
Q_OBJECT
public:
explicit QWaylandTabletToolV2(::zwp_tablet_tool_v2 *tool);
protected:
void zwp_tablet_tool_v2_type(uint32_t tool_type) override;
void zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) override;
// void zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) override;
void zwp_tablet_tool_v2_capability(uint32_t capability) override;
void zwp_tablet_tool_v2_done() override;
void zwp_tablet_tool_v2_removed() override;
void zwp_tablet_tool_v2_proximity_in(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override;
void zwp_tablet_tool_v2_proximity_out() override;
void zwp_tablet_tool_v2_down(uint32_t serial) override;
void zwp_tablet_tool_v2_up() override;
void zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) override;
void zwp_tablet_tool_v2_pressure(uint32_t pressure) override;
void zwp_tablet_tool_v2_distance(uint32_t distance) override;
void zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) override;
void zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) override;
void zwp_tablet_tool_v2_slider(int32_t position) override;
// void zwp_tablet_tool_v2_wheel(wl_fixed_t degrees, int32_t clicks) override;
void zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) override;
void zwp_tablet_tool_v2_frame(uint32_t time) override;
private:
// Static state (sent before done event)
QTabletEvent::PointerType m_pointerType = QTabletEvent::PointerType::UnknownPointer;
QTabletEvent::TabletDevice m_tabletDevice = QTabletEvent::TabletDevice::NoDevice;
type m_toolType = type_pen;
bool m_hasRotation = false;
quint64 m_uid = 0;
// Accumulated state (applied on frame event)
struct State {
bool down = false;
QPointer<QWaylandSurface> proximitySurface;
bool enteredSurface = false; // Not enough with just proximitySurface, if the surface is deleted, we still want to send a leave event
QPointF surfacePosition;
uint distance = 0;
qreal pressure = 0;
qreal rotation = 0;
qreal xTilt = 0;
qreal yTilt = 0;
qreal slider = 0;
Qt::MouseButtons buttons = Qt::MouseButton::NoButton; // Actual buttons, down state -> left mouse is mapped inside the frame handler
//auto operator<=>(const Point&) const = default; // TODO: use this when upgrading to C++20
bool operator==(const State &o) const;
} m_pending, m_applied;
};
// We don't actually use this, but need to handle the "removed" event to comply with the protocol
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletPadV2 : public QObject, public QtWayland::zwp_tablet_pad_v2
{
Q_OBJECT
public:
explicit QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad);
protected:
// void zwp_tablet_pad_v2_group(struct ::zwp_tablet_pad_group_v2 *pad_group) override;
// void zwp_tablet_pad_v2_path(const QString &path) override;
// void zwp_tablet_pad_v2_buttons(uint32_t buttons) override;
// void zwp_tablet_pad_v2_done() override;
// void zwp_tablet_pad_v2_button(uint32_t time, uint32_t button, uint32_t state) override;
// void zwp_tablet_pad_v2_enter(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override;
// void zwp_tablet_pad_v2_leave(uint32_t serial, struct ::wl_surface *surface) override;
void zwp_tablet_pad_v2_removed() override;
};
} // namespace QtWaylandClient
QT_END_NAMESPACE
#endif // QWAYLANDTABLETV2_P_H

View File

@ -42,6 +42,8 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandsurface_p.h"
#include <QtGui/QTouchDevice>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -165,22 +167,28 @@ void QWaylandTouchExtension::sendTouchEvent()
QWindowSystemInterface::handleTouchEvent(mTargetWindow, mTimestamp, mTouchDevice, mTouchPoints);
Qt::TouchPointStates states = 0;
Qt::TouchPointStates states = {};
for (int i = 0; i < mTouchPoints.count(); ++i)
states |= mTouchPoints.at(i).state;
if (mFlags & QT_TOUCH_EXTENSION_FLAGS_MOUSE_FROM_TOUCH) {
if (states == Qt::TouchPointPressed)
const bool firstPress = states == Qt::TouchPointPressed;
if (firstPress)
mMouseSourceId = mTouchPoints.first().id;
for (int i = 0; i < mTouchPoints.count(); ++i) {
const QWindowSystemInterface::TouchPoint &tp(mTouchPoints.at(i));
if (tp.id == mMouseSourceId) {
Qt::MouseButtons buttons = tp.state == Qt::TouchPointReleased ? Qt::NoButton : Qt::LeftButton;
const bool released = tp.state == Qt::TouchPointReleased;
Qt::MouseButtons buttons = released ? Qt::NoButton : Qt::LeftButton;
QEvent::Type eventType = firstPress ? QEvent::MouseButtonPress
: released ? QEvent::MouseButtonRelease
: QEvent::MouseMove;
mLastMouseGlobal = tp.area.center();
QPoint globalPoint = mLastMouseGlobal.toPoint();
QPointF delta = mLastMouseGlobal - globalPoint;
mLastMouseLocal = mTargetWindow->mapFromGlobal(globalPoint) + delta;
QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, buttons);
QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal,
buttons, Qt::LeftButton, eventType);
if (buttons == Qt::NoButton)
mMouseSourceId = -1;
break;
@ -200,7 +208,7 @@ void QWaylandTouchExtension::touchCanceled()
mTouchPoints.clear();
mPrevTouchPoints.clear();
if (mMouseSourceId != -1)
QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, Qt::NoButton);
QWindowSystemInterface::handleMouseEvent(mTargetWindow, mTimestamp, mLastMouseLocal, mLastMouseGlobal, Qt::NoButton, Qt::LeftButton, QEvent::MouseButtonRelease);
}
void QWaylandTouchExtension::touch_extension_configure(uint32_t flags)

View File

@ -43,8 +43,8 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window)
: QWaylandWindow(window)
QWaylandVulkanWindow::QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display)
: QWaylandWindow(window, display)
{
}

View File

@ -50,7 +50,7 @@ namespace QtWaylandClient {
class QWaylandVulkanWindow : public QWaylandWindow
{
public:
explicit QWaylandVulkanWindow(QWindow *window);
explicit QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandVulkanWindow() override;
WindowType windowType() const override;

View File

@ -73,9 +73,9 @@ Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window)
QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
: QPlatformWindow(window)
, mDisplay(waylandScreen()->display())
, mDisplay(display)
, mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{
@ -125,9 +125,10 @@ void QWaylandWindow::initWindow()
if (shouldCreateSubSurface()) {
Q_ASSERT(!mSubSurfaceWindow);
QWaylandWindow *p = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
if (::wl_subsurface *ss = mDisplay->createSubSurface(this, p)) {
mSubSurfaceWindow = new QWaylandSubSurface(this, p, ss);
auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
if (parent->wlSurface()) {
if (::wl_subsurface *subsurface = mDisplay->createSubSurface(this, parent))
mSubSurfaceWindow = new QWaylandSubSurface(this, parent, subsurface);
}
} else if (shouldCreateShellSurface()) {
Q_ASSERT(!mShellSurface);
@ -176,7 +177,7 @@ void QWaylandWindow::initWindow()
}
}
mScale = waylandScreen()->scale();
mScale = waylandScreen() ? waylandScreen()->scale() : 1; // fallback to 1 if we don't have a real screen
// Enable high-dpi rendering. Scale() returns the screen scale factor and will
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
@ -401,14 +402,14 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
}
}
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
if (mSurface) {
if (auto *screen = mSurface->oldestEnteredScreen())
return screen;
}
return waylandScreen();
return QPlatformWindow::screen();
}
void QWaylandWindow::setVisible(bool visible)
@ -425,8 +426,7 @@ void QWaylandWindow::setVisible(bool visible)
// QWaylandShmBackingStore::beginPaint().
} else {
sendExposeEvent(QRect());
if (window()->type() == Qt::Popup)
closePopups(this);
closePopups(this);
reset();
}
}
@ -672,6 +672,19 @@ QRect QWaylandWindow::windowContentGeometry() const
return QRect(QPoint(), surfaceSize());
}
/*!
* Converts from wl_surface coordinates to Qt window coordinates. Qt window
* coordinates start inside (not including) the window decorations, while
* wl_surface coordinates start at the first pixel of the buffer. Potentially,
* this should be in the window shadow, although we don't have those. So for
* now, it's the first pixel of the decorations.
*/
QPointF QWaylandWindow::mapFromWlSurface(const QPointF &surfacePosition) const
{
const QMargins margins = frameMargins();
return QPointF(surfacePosition.x() - margins.left(), surfacePosition.y() - margins.top());
}
wl_surface *QWaylandWindow::wlSurface()
{
return mSurface ? mSurface->object() : nullptr;
@ -689,7 +702,11 @@ QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const
QWaylandScreen *QWaylandWindow::waylandScreen() const
{
return static_cast<QWaylandScreen *>(QPlatformWindow::screen());
auto *platformScreen = QPlatformWindow::screen();
Q_ASSERT(platformScreen);
if (platformScreen->isPlaceholder())
return nullptr;
return static_cast<QWaylandScreen *>(platformScreen);
}
void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
@ -852,7 +869,7 @@ QWaylandWindow *QWaylandWindow::transientParent() const
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
{
if (e.type == QWaylandPointerEvent::Leave) {
if (e.type == QEvent::Leave) {
if (mWindowDecoration) {
if (mMouseEventsInContentArea)
QWindowSystemInterface::handleLeaveEvent(window());
@ -869,24 +886,26 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
handleMouseEventWithDecoration(inputDevice, e);
} else {
switch (e.type) {
case QWaylandPointerEvent::Enter:
case QEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);
break;
case QWaylandPointerEvent::Press:
case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers);
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.button, e.type, e.modifiers);
break;
case QWaylandPointerEvent::Wheel:
case QEvent::Wheel:
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
e.pixelDelta, e.angleDelta, e.modifiers,
e.phase, e.source, false);
break;
default:
Q_UNREACHABLE();
}
}
#if QT_CONFIG(cursor)
if (e.type == QWaylandPointerEvent::Enter) {
if (e.type == QEvent::Enter) {
QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
if (contentGeometry.contains(e.local.toPoint()))
restoreMouseCursor(inputDevice);
@ -918,10 +937,8 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
geometry().size().width() - marg.right(),
geometry().size().height() - marg.bottom());
if (windowRect.contains(e.local.toPoint()) || mMousePressedInContentArea != Qt::NoButton) {
QPointF localTranslated = e.local;
const QPointF localTranslated = mapFromWlSurface(e.local);
QPointF globalTranslated = e.global;
localTranslated.setX(localTranslated.x() - marg.left());
localTranslated.setY(localTranslated.y() - marg.top());
globalTranslated.setX(globalTranslated.x() - marg.left());
globalTranslated.setY(globalTranslated.y() - marg.top());
if (!mMouseEventsInContentArea) {
@ -932,21 +949,23 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
}
switch (e.type) {
case QWaylandPointerEvent::Enter:
case QEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated);
break;
case QWaylandPointerEvent::Press:
case QWaylandPointerEvent::Release:
case QWaylandPointerEvent::Motion:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.modifiers);
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.button, e.type, e.modifiers);
break;
case QWaylandPointerEvent::Wheel: {
case QEvent::Wheel: {
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
localTranslated, globalTranslated,
e.pixelDelta, e.angleDelta, e.modifiers,
e.phase, e.source, false);
break;
}
default:
Q_UNREACHABLE();
}
mMouseEventsInContentArea = true;
@ -961,7 +980,7 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
void QWaylandWindow::handleScreensChanged()
{
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
QPlatformScreen *newScreen = calculateScreenFromSurfaceEvents();
if (newScreen == mLastReportedScreen)
return;
@ -969,7 +988,7 @@ void QWaylandWindow::handleScreensChanged()
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
mLastReportedScreen = newScreen;
int scale = newScreen->scale();
int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale();
if (scale != mScale) {
mScale = scale;
if (mSurface && mDisplay->compositorVersion() >= 3)

View File

@ -93,7 +93,7 @@ public:
Vulkan
};
QWaylandWindow(QWindow *window);
QWaylandWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandWindow() override;
virtual WindowType windowType() const = 0;
@ -129,6 +129,7 @@ public:
QMargins frameMargins() const override;
QSize surfaceSize() const;
QRect windowContentGeometry() const;
QPointF mapFromWlSurface(const QPointF &surfacePosition) const;
QWaylandSurface *waylandSurface() const { return mSurface.data(); }
::wl_surface *wlSurface();
@ -195,7 +196,7 @@ public:
void propagateSizeHints() override;
void addAttachOffset(const QPoint point);
bool startSystemMove(const QPoint &pos) override;
bool startSystemMove(const QPoint &pos);
void timerEvent(QTimerEvent *event) override;
void requestUpdate() override;
@ -241,7 +242,7 @@ protected:
bool mSentInitialResize = false;
QPoint mOffset;
int mScale = 1;
QWaylandScreen *mLastReportedScreen = nullptr;
QPlatformScreen *mLastReportedScreen = nullptr;
QIcon mWindowIcon;
@ -262,7 +263,7 @@ private:
void reset(bool sendDestroyEvent = true);
void sendExposeEvent(const QRect &rect);
static void closePopups(QWaylandWindow *parent);
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
QPlatformScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreensChanged();

View File

@ -467,6 +467,9 @@ bool Scanner::process()
if (m_xml->hasError())
return false;
printf("// This file was generated by qtwaylandscanner\n");
printf("// source file is %s\n\n", qPrintable(m_protocolFilePath));
for (auto b : qAsConst(m_includes))
printf("#include %s\n", b.constData());

View File

@ -5,11 +5,13 @@ SUBDIRS += \
datadevicev1 \
fullscreenshellv1 \
iviapplication \
nooutput \
output \
primaryselectionv1 \
seatv4 \
seatv5 \
surface \
tabletv2 \
wl_connect \
xdgdecorationv1 \
xdgoutput \

View File

@ -28,8 +28,8 @@
#include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtGui/QDrag>

View File

@ -0,0 +1,5 @@
include (../shared/shared.pri)
TARGET = tst_nooutput
SOURCES += tst_nooutput.cpp

View File

@ -0,0 +1,68 @@
/****************************************************************************
**
** 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 NoOutputCompositor : public DefaultCompositor {
public:
NoOutputCompositor()
{
exec([this] { removeAll<Output>(); });
m_config.autoConfigure = true;
}
};
class tst_nooutput : public QObject, private NoOutputCompositor
{
Q_OBJECT
private slots:
void cleanup()
{
// There should be no wl_outputs in this test
QCOMPOSITOR_COMPARE(getAll<Output>().size(), 0);
QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
}
void noScreens();
};
void tst_nooutput::noScreens()
{
QRasterWindow window;
window.resize(16, 16);
window.show();
// We have to handle showing a window when there are no real outputs
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
}
QCOMPOSITOR_TEST_MAIN(tst_nooutput)
#include "tst_nooutput.moc"

View File

@ -53,6 +53,7 @@ private slots:
void windowScreens();
void removePrimaryScreen();
void screenOrder();
void removeAllScreens();
};
void tst_output::primaryScreen()
@ -227,5 +228,47 @@ void tst_output::screenOrder()
});
}
// This is different from tst_nooutput::noScreens because here we have a screen at platform
// integration initialization, which we then remove.
void tst_output::removeAllScreens()
{
QRasterWindow window1;
window1.resize(400, 320);
window1.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface(0) && xdgSurface(0)->m_committedConfigureSerial);
const QString wlOutputPrimaryScreenModel = QGuiApplication::primaryScreen()->model();
// Get screen info so we can restore it after
auto screenInfo = exec([=] { return output()->m_data; });
exec([=] { remove(output()); });
// Make sure the wl_output is actually removed before we continue
QTRY_VERIFY(!QGuiApplication::primaryScreen() || QGuiApplication::primaryScreen()->model() != wlOutputPrimaryScreenModel);
// Adding a window while there are no screens should also work
QRasterWindow window2;
window2.resize(400, 320);
window2.show();
exec([=] { add<Output>(screenInfo); });
// Things should be back to normal
QTRY_VERIFY(QGuiApplication::primaryScreen());
QTRY_COMPARE(QGuiApplication::primaryScreen()->model(), wlOutputPrimaryScreenModel);
// Test that we don't leave any fake screens around after we get a wl_output back.
QTRY_COMPARE(QGuiApplication::screens().size(), 1);
// Qt may choose to recreate/hide windows in response to changing screens, so give the client
// some time to potentially mess up before we verify that the windows are visible.
xdgPingAndWaitForPong();
// Windows should be visible after we've reconnected the screen
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(0) && xdgToplevel(0)->m_xdgSurface->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1) && xdgToplevel(1)->m_xdgSurface->m_committedConfigureSerial);
}
QCOMPOSITOR_TEST_MAIN(tst_output)
#include "tst_output.moc"

View File

@ -30,8 +30,8 @@
#include <qwayland-server-wp-primary-selection-unstable-v1.h>
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h>

View File

@ -28,8 +28,8 @@
#include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
#include <QtGui/private/qguiapplication_p.h>
@ -289,8 +289,8 @@ void tst_seatv4::simpleAxis()
{
Event() = default;
const QPoint pixelDelta;
const QPoint angleDelta; // eights of a degree, positive is upwards, left
QPoint pixelDelta;
QPoint angleDelta; // eights of a degree, positive is upwards, left
};
QVector<Event> m_events;
};

View File

@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
using namespace MockCompositor;
@ -71,6 +71,7 @@ private slots:
void singleTapFloat();
void multiTouch();
void multiTouchUpAndMotionFrame();
void tapAndMoveInSameFrame();
};
void tst_seatv5::bindsToSeat()
@ -155,10 +156,10 @@ public:
, source(event->source())
{
}
const Qt::ScrollPhase phase{};
const QPoint pixelDelta;
const QPoint angleDelta; // eights of a degree, positive is upwards, left
const Qt::MouseEventSource source{};
Qt::ScrollPhase phase{};
QPoint pixelDelta;
QPoint angleDelta; // eights of a degree, positive is upwards, left
Qt::MouseEventSource source{};
};
QVector<Event> m_events;
};
@ -396,9 +397,9 @@ public:
, touchPoints(event->touchPoints())
{
}
const QEvent::Type type{};
const Qt::TouchPointStates touchPointStates{};
const QList<QTouchEvent::TouchPoint> touchPoints;
QEvent::Type type{};
Qt::TouchPointStates touchPointStates{};
QList<QTouchEvent::TouchPoint> touchPoints;
};
QVector<Event> m_events;
};
@ -586,5 +587,37 @@ void tst_seatv5::multiTouchUpAndMotionFrame()
QVERIFY(window.m_events.empty());
}
void tst_seatv5::tapAndMoveInSameFrame()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([=] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 0);
t->sendMotion(c, {33, 33}, 0);
t->sendFrame(c);
// Don't leave touch in a weird state
t->sendUp(c, 0);
t->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchBegin);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointPressed);
// Position isn't that important, we just want to make sure we actually get the pressed event
}
// Make sure we eventually release
QTRY_VERIFY(!window.m_events.empty());
QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased);
}
QCOMPOSITOR_TEST_MAIN(tst_seatv5)
#include "tst_seatv5.moc"

View File

@ -164,6 +164,16 @@ protected:
}
};
class Subsurface : public QObject, public QtWaylandServer::wl_subsurface
{
Q_OBJECT
public:
explicit Subsurface(wl_client *client, int id, int version)
: QtWaylandServer::wl_subsurface(client, id, version)
{
}
};
class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor
{
Q_OBJECT
@ -171,7 +181,20 @@ public:
explicit SubCompositor(CoreCompositor *compositor, int version = 1)
: QtWaylandServer::wl_subcompositor(compositor->m_display, version)
{}
// TODO
QVector<Subsurface *> m_subsurfaces;
signals:
void subsurfaceCreated(Subsurface *subsurface);
protected:
void subcompositor_get_subsurface(Resource *resource, uint32_t id, ::wl_resource *surface, ::wl_resource *parent) override
{
QTRY_VERIFY(parent);
QTRY_VERIFY(surface);
auto *subsurface = new Subsurface(resource->client(), id, resource->version());
m_subsurfaces.append(subsurface); // TODO: clean up?
emit subsurfaceCreated(subsurface);
}
};
struct OutputMode {

View File

@ -52,19 +52,16 @@ DefaultCompositor::DefaultCompositor()
// 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())
if (m_config.autoEnter && get<Output>() && 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();
});
});
QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, get<XdgWmBase>(), [&] (XdgToplevel *toplevel) {
if (m_config.autoConfigure)
toplevel->sendCompleteConfigure();
}, Qt::DirectConnection);
}
Q_ASSERT(isClean());
}

View File

@ -56,6 +56,7 @@ public:
// 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); }
Subsurface *subSurface(int i = 0) { return get<SubCompositor>()->m_subsurfaces.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); }

View File

@ -1,4 +1,4 @@
QT += testlib waylandclient-private
QT += testlib waylandclient-private opengl
CONFIG += testcase wayland-scanner
QMAKE_USE += wayland-server

View File

@ -83,7 +83,7 @@ XdgSurface::XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client
{
QVERIFY(!surface->m_pending.buffer);
QVERIFY(!surface->m_committed.buffer);
connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated);
connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated, Qt::DirectConnection);
connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured);
connect(surface, &Surface::commit, this, [this] {
m_committed = m_pending;

View File

@ -312,9 +312,9 @@ void *MockCompositor::run(void *data)
Impl::Compositor compositor(controller);
controller->m_compositor = &compositor;
controller->m_waitCondition.wakeOne();
while (!controller->m_ready) {
controller->m_waitCondition.wakeOne();
controller->dispatchCommands();
compositor.dispatchEvents(20);
}

View File

@ -1,6 +1,6 @@
CONFIG += testcase link_pkgconfig
QT += testlib
QT += core-private gui-private waylandclient-private
QT += core-private gui-private waylandclient-private opengl
QMAKE_USE += wayland-client wayland-server

View File

@ -29,7 +29,7 @@
#include "mockcompositor.h"
#include <QtGui/QRasterWindow>
#if QT_CONFIG(opengl)
#include <QtGui/QOpenGLWindow>
#include <QtOpenGL/QOpenGLWindow>
#endif
using namespace MockCompositor;
@ -45,6 +45,10 @@ private slots:
void waitForFrameCallbackGl();
#endif
void negotiateShmFormat();
// Subsurfaces
void createSubsurface();
void createSubsurfaceForHiddenParent();
};
void tst_surface::createDestroySurface()
@ -160,5 +164,42 @@ void tst_surface::negotiateShmFormat()
});
}
void tst_surface::createSubsurface()
{
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
exec([=] { xdgToplevel()->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial);
QRasterWindow subWindow;
subWindow.setParent(&window);
subWindow.resize(64, 64);
subWindow.show();
QCOMPOSITOR_TRY_VERIFY(subSurface());
}
// Used to cause a crash in libwayland (QTBUG-79674)
void tst_surface::createSubsurfaceForHiddenParent()
{
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
exec([=] { xdgToplevel()->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgSurface()->m_committedConfigureSerial);
window.hide();
QRasterWindow subWindow;
subWindow.setParent(&window);
subWindow.resize(64, 64);
subWindow.show();
// Make sure the client doesn't quit before it has a chance to crash
xdgPingAndWaitForPong();
}
QCOMPOSITOR_TEST_MAIN(tst_surface)
#include "tst_surface.moc"

View File

@ -0,0 +1,7 @@
include (../shared/shared.pri)
WAYLANDSERVERSOURCES += \
$$PWD/../../../../src/3rdparty/protocol/tablet-unstable-v2.xml
TARGET = tst_tabletv2
SOURCES += tst_tabletv2.cpp

View File

@ -0,0 +1,918 @@
/****************************************************************************
**
** 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 <qwayland-server-tablet-unstable-v2.h>
#include <QtGui/QRasterWindow>
using namespace MockCompositor;
constexpr int tabletVersion = 1; // protocol VERSION, not the name suffix (_v2)
class TabletManagerV2;
class TabletSeatV2;
class TabletV2 : public QObject, public QtWaylandServer::zwp_tablet_v2
{
Q_OBJECT
public:
explicit TabletV2(TabletSeatV2 *tabletSeat)
: m_tabletSeat(tabletSeat)
{
}
void send_removed() = delete;
void send_removed(struct ::wl_resource *resource) = delete;
void sendRemoved();
QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
protected:
void zwp_tablet_v2_destroy(Resource *resource) override;
};
class TabletToolV2 : public QObject, public QtWaylandServer::zwp_tablet_tool_v2
{
Q_OBJECT
public:
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
explicit TabletToolV2(TabletSeatV2 *tabletSeat, ToolType toolType, quint64 hardwareSerial)
: m_tabletSeat(tabletSeat)
, m_toolType(toolType)
, m_hardwareSerial(hardwareSerial)
{
}
wl_resource *toolResource() // for convenience
{
Q_ASSERT(resourceMap().size() == 1);
// Strictly speaking, there may be more than one resource for the tool, for intsance if
// if there are multiple clients, or a client has called get_tablet_seat multiple times.
// For now we'll pretend there can only be one resource.
return resourceMap().first()->handle;
}
void send_removed() = delete;
void send_removed(struct ::wl_resource *resource) = delete;
void sendRemoved();
uint sendProximityIn(TabletV2 *tablet, Surface *surface);
void sendProximityOut();
void sendMotion(QPointF position)
{
Q_ASSERT(m_proximitySurface);
for (auto *resource : resourceMap())
send_motion(resource->handle, wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
}
uint sendDown();
void sendUp() { send_up(toolResource()); }
void sendPressure(uint pressure);
void sendTilt(qreal tiltX, qreal tiltY) { send_tilt(toolResource(), wl_fixed_from_double(tiltX), wl_fixed_from_double(tiltY)); }
void sendRotation(qreal rotation) { send_rotation(toolResource(), wl_fixed_from_double(rotation)); }
uint sendButton(uint button, bool pressed);
uint sendFrame();
QPointer<TabletSeatV2> m_tabletSeat; // destruction order is not guaranteed
ToolType m_toolType = ToolType::type_pen;
quint64 m_hardwareSerial = 0;
QPointer<Surface> m_proximitySurface;
protected:
void zwp_tablet_tool_v2_destroy(Resource *resource) override;
};
class TabletPadV2 : public QObject, public QtWaylandServer::zwp_tablet_pad_v2
{
Q_OBJECT
public:
explicit TabletPadV2(TabletSeatV2 *tabletSeat)
: m_tabletSeat(tabletSeat)
{
}
void send_removed() = delete;
void send_removed(struct ::wl_resource *resource) = delete;
void sendRemoved();
QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
protected:
void zwp_tablet_pad_v2_destroy(Resource *resource) override;
};
class TabletSeatV2 : public QObject, public QtWaylandServer::zwp_tablet_seat_v2
{
Q_OBJECT
public:
explicit TabletSeatV2(TabletManagerV2 *manager, Seat *seat)
: m_manager(manager)
, m_seat(seat)
{}
TabletV2 *addTablet()
{
auto *tablet = new TabletV2(this);
m_tablets.append(tablet);
for (auto *resource : resourceMap())
sendTabletAdded(resource, tablet);
return tablet;
}
void sendTabletAdded(Resource *resource, TabletV2 *tablet)
{
// Although, not necessarily correct, assuming just one tablet_seat per client
auto *tabletResource = tablet->add(resource->client(), resource->version());
zwp_tablet_seat_v2::send_tablet_added(resource->handle, tabletResource->handle);
// TODO: send extra stuff before done?
tablet->send_done(tabletResource->handle);
}
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
TabletToolV2 *addTool(ToolType toolType = ToolType::type_pen, quint64 hardwareSerial = 0)
{
auto *tool = new TabletToolV2(this, toolType, hardwareSerial);
m_tools.append(tool);
for (auto *resource : resourceMap())
sendToolAdded(resource, tool);
return tool;
}
void sendToolAdded(Resource *resource, TabletToolV2 *tool)
{
// Although, not necessarily correct, assuming just one tablet_seat per client
auto *toolResource = tool->add(resource->client(), resource->version())->handle;
zwp_tablet_seat_v2::send_tool_added(resource->handle, toolResource);
tool->send_type(toolResource, tool->m_toolType);
if (tool->m_hardwareSerial) {
const uint hi = tool->m_hardwareSerial >> 32;
const uint lo = tool->m_hardwareSerial & 0xffffffff;
tool->send_hardware_serial(toolResource, hi, lo);
}
tool->send_done(toolResource);
}
TabletPadV2 *addPad()
{
auto *pad = new TabletPadV2(this);
m_pads.append(pad);
for (auto *resource : resourceMap())
sendPadAdded(resource, pad);
return pad;
}
void sendPadAdded(Resource *resource, TabletPadV2 *pad)
{
// Although, not necessarily correct, assuming just one tablet_seat per client
auto *padResource = pad->add(resource->client(), resource->version())->handle;
zwp_tablet_seat_v2::send_pad_added(resource->handle, padResource);
pad->send_done(padResource);
}
void removeAll()
{
const auto tools = m_tools;
for (auto *tool : tools)
tool->sendRemoved();
const auto tablets = m_tablets;
for (auto *tablet : tablets)
tablet->sendRemoved();
const auto pads = m_pads;
for (auto *pad : pads)
pad->sendRemoved();
}
TabletManagerV2 *m_manager = nullptr;
Seat *m_seat = nullptr;
QVector<TabletV2 *> m_tablets;
QVector<TabletV2 *> m_tabletsWaitingForDestroy;
QVector<TabletToolV2 *> m_tools;
QVector<TabletToolV2 *> m_toolsWaitingForDestroy;
QVector<TabletPadV2 *> m_pads;
QVector<TabletPadV2 *> m_padsWaitingForDestroy;
protected:
void zwp_tablet_seat_v2_bind_resource(Resource *resource)
{
for (auto *tablet : m_tablets)
sendTabletAdded(resource, tablet);
for (auto *tool : m_tools)
sendToolAdded(resource, tool);
for (auto *pad : m_pads)
sendPadAdded(resource, pad);
}
};
class TabletManagerV2 : public Global, public QtWaylandServer::zwp_tablet_manager_v2
{
Q_OBJECT
public:
explicit TabletManagerV2(CoreCompositor *compositor, int version = 1)
: QtWaylandServer::zwp_tablet_manager_v2(compositor->m_display, version)
, m_version(version)
{}
bool isClean() override
{
for (auto *seat : m_tabletSeats) {
if (!seat->m_tabletsWaitingForDestroy.empty())
return false;
if (!seat->m_toolsWaitingForDestroy.empty())
return false;
if (!seat->m_padsWaitingForDestroy.empty())
return false;
}
return true;
}
TabletSeatV2 *tabletSeatFor(Seat *seat)
{
Q_ASSERT(seat);
if (auto *tabletSeat = m_tabletSeats.value(seat, nullptr))
return tabletSeat;
auto *tabletSeat = new TabletSeatV2(this, seat);
m_tabletSeats[seat] = tabletSeat;
return tabletSeat;
}
int m_version = 1; // TODO: Remove on libwayland upgrade
QMap<Seat *, TabletSeatV2 *> m_tabletSeats;
protected:
void zwp_tablet_manager_v2_destroy(Resource *resource) override
{
// tablet_seats created from this object are unaffected and should be destroyed separately.
wl_resource_destroy(resource->handle);
}
void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t id, ::wl_resource *seatResource) override
{
auto *seat = fromResource<Seat>(seatResource);
QVERIFY(seat);
auto *tabletSeat = tabletSeatFor(seat);
tabletSeat->add(resource->client(), id, resource->version());
}
};
void TabletV2::sendRemoved()
{
for (auto *resource : resourceMap())
zwp_tablet_v2_send_removed(resource->handle);
bool removed = m_tabletSeat->m_tablets.removeOne(this);
QVERIFY(removed);
m_tabletSeat->m_tabletsWaitingForDestroy.append(this);
}
void TabletV2::zwp_tablet_v2_destroy(QtWaylandServer::zwp_tablet_v2::Resource *resource)
{
Q_UNUSED(resource)
if (m_tabletSeat) {
bool removed = m_tabletSeat->m_tabletsWaitingForDestroy.removeOne(this);
QVERIFY(removed);
}
wl_resource_destroy(resource->handle);
}
void TabletToolV2::sendRemoved()
{
for (auto *resource : resourceMap())
zwp_tablet_tool_v2_send_removed(resource->handle);
bool removed = m_tabletSeat->m_tools.removeOne(this);
QVERIFY(removed);
m_tabletSeat->m_toolsWaitingForDestroy.append(this);
}
uint TabletToolV2::sendProximityIn(TabletV2 *tablet, Surface *surface)
{
Q_ASSERT(!m_proximitySurface);
m_proximitySurface = surface;
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
auto *client = surface->resource()->client();
auto tabletResource = tablet->resourceMap().value(client)->handle;
send_proximity_in(toolResource(), serial, tabletResource, surface->resource()->handle);
return serial;
}
void TabletToolV2::sendProximityOut()
{
Q_ASSERT(m_proximitySurface);
send_proximity_out(toolResource());
m_proximitySurface = nullptr;
}
uint TabletToolV2::sendDown()
{
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
send_down(toolResource(), serial);
return serial;
}
void TabletToolV2::sendPressure(uint pressure)
{
Q_ASSERT(m_proximitySurface);
auto *client = m_proximitySurface->resource()->client();
auto toolResource = resourceMap().value(client)->handle;
send_pressure(toolResource, pressure);
}
uint TabletToolV2::sendButton(uint button, bool pressed)
{
button_state state = pressed ? button_state_pressed : button_state_released;
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
send_button(toolResource(), serial, button, state);
return serial;
}
uint TabletToolV2::sendFrame()
{
uint time = m_tabletSeat->m_seat->m_compositor->currentTimeMilliseconds();
for (auto *resource : resourceMap())
send_frame(resource->handle, time);
return time;
}
void TabletToolV2::zwp_tablet_tool_v2_destroy(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource)
{
if (m_tabletSeat) {
bool removed = m_tabletSeat->m_toolsWaitingForDestroy.removeOne(this);
QVERIFY(removed);
}
wl_resource_destroy(resource->handle);
}
void TabletPadV2::sendRemoved()
{
for (auto *resource : resourceMap())
zwp_tablet_pad_v2_send_removed(resource->handle);
bool removed = m_tabletSeat->m_pads.removeOne(this);
QVERIFY(removed);
m_tabletSeat->m_padsWaitingForDestroy.append(this);
}
void TabletPadV2::zwp_tablet_pad_v2_destroy(QtWaylandServer::zwp_tablet_pad_v2::Resource *resource)
{
if (m_tabletSeat) {
bool removed = m_tabletSeat->m_padsWaitingForDestroy.removeOne(this);
QVERIFY(removed);
}
wl_resource_destroy(resource->handle);
}
class TabletCompositor : public DefaultCompositor {
public:
explicit TabletCompositor()
{
exec([this] {
m_config.autoConfigure = true;
add<TabletManagerV2>(tabletVersion);
});
}
TabletSeatV2 *tabletSeat(int i = 0)
{
return get<TabletManagerV2>()->tabletSeatFor(get<Seat>(i));
}
TabletV2 *tablet(int i = 0, int iSeat = 0)
{
if (auto *ts = tabletSeat(iSeat))
return ts->m_tablets.value(i, nullptr);
return nullptr;
}
TabletToolV2 *tabletTool(int i = 0, int iSeat = 0)
{
if (auto *ts = tabletSeat(iSeat))
return ts->m_tools.value(i, nullptr);
return nullptr;
}
TabletPadV2 *tabletPad(int i = 0, int iSeat = 0)
{
if (auto *ts = tabletSeat(iSeat))
return ts->m_pads.value(i, nullptr);
return nullptr;
}
};
Q_DECLARE_METATYPE(QtWaylandServer::zwp_tablet_tool_v2::type);
Q_DECLARE_METATYPE(QTabletEvent::PointerType);
Q_DECLARE_METATYPE(Qt::MouseButton);
class tst_tabletv2 : public QObject, private TabletCompositor
{
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
Q_OBJECT
private slots:
void cleanup();
void bindsToManager();
void createsTabletSeat();
void destroysTablet();
void destroysTool();
void destroysPad();
void proximityEvents();
void moveEvent();
void pointerType_data();
void pointerType();
void hardwareSerial();
void buttons_data();
void buttons();
void tabletEvents();
};
class ProximityFilter : public QObject {
Q_OBJECT
public:
ProximityFilter() { qApp->installEventFilter(this); }
~ProximityFilter() override { qDeleteAll(m_events); }
QVector<QTabletEvent *> m_events;
int nextEventIndex = 0;
int numEvents() const { return m_events.size() - nextEventIndex; }
QTabletEvent *popEvent()
{
auto *event = m_events.value(nextEventIndex, nullptr);
if (event)
++nextEventIndex;
return event;
}
protected:
bool eventFilter(QObject *object, QEvent *event) override
{
Q_UNUSED(object);
switch (event->type()) {
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity: {
auto *e = static_cast<QTabletEvent *>(event);
auto *ev = new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->deviceType(),
e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
e->tangentialPressure(), e->rotation(), e->z(),
Qt::KeyboardModifier::NoModifier, e->uniqueId(),
e->button(), e->buttons());
m_events << ev;
break;
}
default:
break;
}
return false;
}
};
void tst_tabletv2::cleanup()
{
exec([&] {
tabletSeat()->removeAll();
});
QCOMPOSITOR_COMPARE(get<TabletManagerV2>()->m_tabletSeats.size(), 1);
QCOMPOSITOR_COMPARE(tabletSeat()->m_tablets.size(), 0);
QCOMPOSITOR_COMPARE(tabletSeat()->m_tools.size(), 0);
QCOMPOSITOR_COMPARE(tabletSeat()->m_pads.size(), 0);
QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
}
void tst_tabletv2::bindsToManager()
{
QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().size(), 1);
QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().first()->version(), tabletVersion);
}
void tst_tabletv2::createsTabletSeat()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->resourceMap().contains(client()));
QCOMPOSITOR_TRY_COMPARE(tabletSeat()->resourceMap().value(client())->version(), tabletVersion);
//TODO: Maybe also assert some capability reported though qt APIs?
}
void tst_tabletv2::destroysTablet()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
});
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
tablet()->sendRemoved();
});
QCOMPOSITOR_TRY_VERIFY(!tablet());
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_tabletsWaitingForDestroy.empty());
}
void tst_tabletv2::destroysTool()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTool();
});
QCOMPOSITOR_TRY_VERIFY(tabletTool());
exec([&] {
tabletTool()->sendRemoved();
});
QCOMPOSITOR_TRY_VERIFY(!tabletTool());
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_toolsWaitingForDestroy.empty());
}
void tst_tabletv2::destroysPad()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addPad();
});
QCOMPOSITOR_TRY_VERIFY(tabletPad());
exec([&] {
tabletPad()->sendRemoved();
});
QCOMPOSITOR_TRY_VERIFY(!tabletPad());
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_padsWaitingForDestroy.empty());
}
void tst_tabletv2::proximityEvents()
{
ProximityFilter filter;
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool();
});
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
auto *surface = xdgSurface()->m_surface;
auto *tool = tabletTool();
tool->sendProximityIn(tablet(), surface);
tool->sendFrame();
});
QTRY_COMPARE(filter.numEvents(), 1);
QTabletEvent *enterEvent = filter.popEvent();
QCOMPARE(enterEvent->type(), QEvent::TabletEnterProximity);
exec([&] {
auto *tool = tabletTool();
tool->sendProximityOut();
tool->sendFrame();
});
QTRY_COMPARE(filter.numEvents(), 1);
QTabletEvent *leaveEvent = filter.popEvent();
QCOMPARE(leaveEvent->type(), QEvent::TabletLeaveProximity);
}
class TabletWindow : public QRasterWindow {
Q_OBJECT
public:
~TabletWindow() override { qDeleteAll(m_events); }
void tabletEvent(QTabletEvent *e) override
{
m_events << new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->deviceType(),
e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
e->tangentialPressure(), e->rotation(), e->z(),
Qt::KeyboardModifier::NoModifier, e->uniqueId(), e->button(),
e->buttons());
emit tabletEventReceived(m_events.last());
}
int nextEventIndex = 0;
int numEvents() const { return m_events.size() - nextEventIndex; }
QTabletEvent *popEvent()
{
auto *event = m_events.value(nextEventIndex, nullptr);
if (event)
++nextEventIndex;
return event;
}
signals:
void tabletEventReceived(QTabletEvent *event);
private:
QVector<QTabletEvent *> m_events;
};
void tst_tabletv2::moveEvent()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool();
});
TabletWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
auto *surface = xdgSurface()->m_surface;
auto *tool = tabletTool();
tool->sendProximityIn(tablet(), surface);
QMargins margins = window.frameMargins();
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
tool->sendFrame();
});
QTRY_VERIFY(window.numEvents());
QTabletEvent *event = window.popEvent();
QCOMPARE(event->type(), QEvent::TabletMove);
QCOMPARE(event->pressure(), 0);
QCOMPARE(event->posF(), QPointF(12, 34));
}
void tst_tabletv2::pointerType_data()
{
QTest::addColumn<ToolType>("toolType");
QTest::addColumn<QTabletEvent::PointerType>("pointerType");
QTest::addColumn<QTabletEvent::TabletDevice>("tabletDevice");
QTest::newRow("pen") << ToolType::type_pen << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
QTest::newRow("eraser") << ToolType::type_eraser << QTabletEvent::PointerType::Eraser << QTabletEvent::TabletDevice::Stylus;
QTest::newRow("pencil") << ToolType::type_pencil << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
QTest::newRow("airbrush") << ToolType::type_airbrush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Airbrush;
QTest::newRow("brush") << ToolType::type_brush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus; // TODO: is TabletDevice::Stylus the right thing?
QTest::newRow("lens") << ToolType::type_lens << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::Puck;
// TODO: also add tests for FourDMouse and RotationStylus (also need to send capabilities)
// TODO: should these rather be mapped to touch/mouse events?
QTest::newRow("finger") << ToolType::type_finger << QTabletEvent::PointerType::UnknownPointer << QTabletEvent::TabletDevice::NoDevice;
QTest::newRow("mouse") << ToolType::type_mouse << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::NoDevice;
}
void tst_tabletv2::pointerType()
{
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
QFETCH(ToolType, toolType);
QFETCH(QTabletEvent::PointerType, pointerType);
QFETCH(QTabletEvent::TabletDevice, tabletDevice);
ProximityFilter filter;
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool(toolType);
});
TabletWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
auto *surface = xdgSurface()->m_surface;
auto *tool = tabletTool();
tool->sendProximityIn(tablet(), surface);
QMargins margins = window.frameMargins();
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
tool->sendFrame();
});
QTRY_COMPARE(filter.numEvents(), 1);
QTabletEvent *event = filter.popEvent();
QCOMPARE(event->pointerType(), pointerType);
QCOMPARE(event->deviceType(), tabletDevice);
QTRY_VERIFY(window.numEvents());
event = window.popEvent();
QCOMPARE(event->pointerType(), pointerType);
QCOMPARE(event->deviceType(), tabletDevice);
exec([&] {
tabletTool()->sendProximityOut();
tabletTool()->sendFrame();
});
QTRY_VERIFY(filter.numEvents());
event = filter.popEvent();
QCOMPARE(event->pointerType(), pointerType);
QCOMPARE(event->deviceType(), tabletDevice);
}
void tst_tabletv2::hardwareSerial()
{
ProximityFilter filter;
const quint64 uid = 0xbaba15dead15f00d;
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool(ToolType::type_pen, uid);
});
TabletWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
auto *surface = xdgSurface()->m_surface;
auto *tool = tabletTool();
tool->sendProximityIn(tablet(), surface);
QMargins margins = window.frameMargins();
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
tool->sendFrame();
});
QTRY_COMPARE(filter.numEvents(), 1);
QTabletEvent *event = filter.popEvent();
QCOMPARE(event->uniqueId(), uid);
QTRY_VERIFY(window.numEvents());
event = window.popEvent();
QCOMPARE(event->uniqueId(), uid);
exec([&] {
tabletTool()->sendProximityOut();
tabletTool()->sendFrame();
});
QTRY_VERIFY(filter.numEvents());
event = filter.popEvent();
QCOMPARE(event->uniqueId(), uid);
}
// As defined in linux/input-event-codes.h
#ifndef BTN_STYLUS
#define BTN_STYLUS 0x14b
#endif
#ifndef BTN_STYLUS2
#define BTN_STYLUS2 0x14c
#endif
void tst_tabletv2::buttons_data()
{
QTest::addColumn<uint>("tabletButton");
QTest::addColumn<Qt::MouseButton>("mouseButton");
QTest::newRow("BTN_STYLUS2") << uint(BTN_STYLUS2) << Qt::MouseButton::RightButton;
QTest::newRow("BTN_STYLUS") << uint(BTN_STYLUS) << Qt::MouseButton::MiddleButton;
}
void tst_tabletv2::buttons()
{
QFETCH(uint, tabletButton);
QFETCH(Qt::MouseButton, mouseButton);
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool();
});
TabletWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
tabletTool()->sendProximityIn(tablet(), xdgSurface()->m_surface);
QMargins margins = window.frameMargins();
tabletTool()->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
tabletTool()->sendFrame();
});
QTRY_VERIFY(window.numEvents());
window.popEvent();
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
tabletTool()->sendButton(tabletButton, true);
tabletTool()->sendFrame();
tabletTool()->sendButton(tabletButton, false);
tabletTool()->sendFrame();
});
QTRY_VERIFY(window.numEvents());
QTabletEvent *event = window.popEvent();
QCOMPARE(event->buttons(), mouseButton);
exec([&] {
tabletTool()->sendProximityOut();
tabletTool()->sendFrame();
});
}
void tst_tabletv2::tabletEvents()
{
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
exec([&] {
tabletSeat()->addTablet();
tabletSeat()->addTool();
});
TabletWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
const QPointF insideDecorations(window.frameMargins().left(), window.frameMargins().top());
QCOMPOSITOR_TRY_VERIFY(tablet());
exec([&] {
auto *surface = xdgSurface()->m_surface;
auto *tool = tabletTool();
// TODO: encapsulate this into a helper function?
tool->sendProximityIn(tablet(), surface);
tool->sendMotion(QPointF(12, 34) + insideDecorations);
tool->sendDown();
tool->sendPressure(65535);
tool->sendFrame();
});
QTRY_VERIFY(window.numEvents());
QTabletEvent *event = window.popEvent();
QCOMPARE(event->type(), QEvent::TabletPress);
QCOMPARE(event->pressure(), 1.0);
QCOMPARE(event->posF(), QPointF(12, 34));
// Values we didn't send should be 0
QCOMPARE(event->rotation(), 0);
QCOMPARE(event->xTilt(), 0);
QCOMPARE(event->yTilt(), 0);
exec([&] {
tabletTool()->sendMotion(QPointF(45, 56) + insideDecorations);
tabletTool()->sendPressure(65535/2);
tabletTool()->sendRotation(90);
tabletTool()->sendTilt(13, 37);
tabletTool()->sendFrame();
});
QTRY_VERIFY(window.numEvents());
event = window.popEvent();
QCOMPARE(event->type(), QEvent::TabletMove);
QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
QVERIFY(qAbs(event->rotation() - 90) < 0.01);
QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
QCOMPARE(event->posF(), QPointF(45, 56));
// Verify that the values stay the same if we don't update them
exec([&] {
tabletTool()->sendMotion(QPointF(10, 11) + insideDecorations); // Change position only
tabletTool()->sendFrame();
});
QTRY_VERIFY(window.numEvents());
event = window.popEvent();
QCOMPARE(event->type(), QEvent::TabletMove);
QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
QVERIFY(qAbs(event->rotation() - 90) < 0.01);
QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
QCOMPARE(event->posF(), QPointF(10, 11));
exec([&] {
tabletTool()->sendPressure(0);
tabletTool()->sendUp();
tabletTool()->sendFrame();
tabletTool()->sendProximityOut();
tabletTool()->sendFrame();
});
QTRY_VERIFY(window.numEvents());
event = window.popEvent();
QCOMPARE(event->type(), QEvent::TabletRelease);
QCOMPARE(event->pressure(), 0);
QCOMPARE(event->posF(), QPointF(10, 11));
}
QCOMPOSITOR_TEST_MAIN(tst_tabletv2)
#include "tst_tabletv2.moc"

View File

@ -30,8 +30,8 @@
#include <qwayland-server-xdg-decoration-unstable-v1.h>
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h>

View File

@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QScreen>
#include <qwayland-server-xdg-output-unstable-v1.h>

View File

@ -27,8 +27,8 @@
****************************************************************************/
#include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
@ -46,6 +46,7 @@ private slots:
void popup();
void tooltipOnPopup();
void switchPopups();
void hidePopupParent();
void pongs();
void minMaxSize();
void windowGeometry();
@ -219,8 +220,8 @@ void tst_xdgshell::popup()
p->sendFrame(c);
uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
p->sendButton(c, BTN_LEFT, Pointer::button_state_released);
return serial;
p->sendFrame(c);
return serial;
});
QTRY_VERIFY(window.m_popup);
@ -429,6 +430,50 @@ void tst_xdgshell::switchPopups()
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
}
void tst_xdgshell::hidePopupParent()
{
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());
exec([=] { xdgToplevel()->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
exec([=] {
auto *surface = xdgToplevel()->surface();
auto *p = pointer();
auto *c = client();
p->sendEnter(surface, {100, 100});
p->sendFrame(c);
p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed);
p->sendButton(c, BTN_LEFT, Pointer::button_state_released);
p->sendFrame(c);
});
QCOMPOSITOR_TRY_VERIFY(xdgPopup());
exec([=] {
xdgPopup()->sendConfigure(QRect(100, 100, 100, 100));
xdgPopup()->m_xdgSurface->sendConfigure();
});
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
window.hide();
QCOMPOSITOR_TRY_VERIFY(!xdgToplevel());
}
void tst_xdgshell::pongs()
{
// Create and show a window to trigger shell integration initialzation,

View File

@ -403,7 +403,7 @@ void tst_WaylandClientXdgShellV6::flushUnconfiguredXdgSurface()
m_compositor->sendShellSurfaceConfigure(surface);
QTRY_COMPARE(surface->image.size(), window.frameGeometry().size());
QTRY_COMPARE(surface->image.pixel(window.frameMargins().left(), window.frameMargins().top()), color.rgba());
QVERIFY(window.isExposed());
QTRY_VERIFY(window.isExposed());
}
void tst_WaylandClientXdgShellV6::dontSpamExposeEvents()