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"> <description summary="factory for creating dmabuf-based wl_buffers">
Following the interfaces from: 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.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. and the Linux DRM sub-system's AddFb2 ioctl.
This interface offers ways to create generic dmabuf-based This interface offers ways to create generic dmabuf-based
@ -129,8 +130,16 @@
binds to this interface. A roundtrip after binding guarantees that binds to this interface. A roundtrip after binding guarantees that
the client has received all supported format-modifier pairs. 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 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> </description>
<arg name="format" type="uint" summary="DRM_FORMAT code"/> <arg name="format" type="uint" summary="DRM_FORMAT code"/>
<arg name="modifier_hi" type="uint" <arg name="modifier_hi" type="uint"
@ -197,6 +206,11 @@
compression, etc. driver-specific modifications to the base format compression, etc. driver-specific modifications to the base format
defined by the DRM fourcc code. 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. 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 The error PLANE_SET is raised if attempting to set a plane that
was already set. was already set.

View File

@ -9,7 +9,7 @@
"Description": "A Wayland shell for displaying a single surface per output", "Description": "A Wayland shell for displaying a single surface per output",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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.", "Description": "The primary selection extension allows copying text by selecting it and pasting it with the middle mouse button.",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "1", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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", "Description": "The Wayland scaler extension allows a client to scale or crop a surface without modifying the buffer",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "2", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2013-2014 Collabora, Ltd." "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", "Id": "wayland-viewporter-protocol",
"Name": "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", "Description": "The Wayland viewporter extension allows a client to scale or crop a surface without modifying the buffer",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "1", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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.", "Description": "The xdg-decoration protocol allows a compositor to announce support for server-side decorations.",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1, version 1", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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", "Description": "The XDG Output protocol is an extended way to describe output regions under Wayland",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v1, version 2", "Version": "unstable v1, version 3",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/xdg-output/xdg-output-unstable-v1.xml?h=1.16", "DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/unstable/xdg-output/xdg-output-unstable-v1.xml",
"LicenseId": "MIT", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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", "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.", "Description": "The XDG-Shell protocol is an extended way to manage surfaces under Wayland compositors.",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://gitlab.freedesktop.org/wayland/wayland-protocols/",
"Version": "1.9.0", "Version": "1.18",
"DownloadLocation": "https://cgit.freedesktop.org/wayland/weston/tag/?id=1.9.0", "DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/tree/1.18/",
"LicenseId": "MIT", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "LicenseFile": "MIT_LICENSE.txt",
"Copyright": "Copyright © 2008-2013 Kristian Høgsberg "Copyright": "Copyright © 2008-2013 Kristian Høgsberg
Copyright © 2013 Rafael Antognolli Copyright © 2013 Rafael Antognolli
Copyright © 2013 Jasper St. Pierre 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", "Name": "Wayland Text Input Protocol",
"QDocModule": "qtwaylandcompositor", "QDocModule": "qtwaylandcompositor",
"QtUsage": "Used in the Qt Wayland Compositor, and the Qt Wayland platform plugin.", "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.", "Description": "Adds support for text input and input methods to applications.",
"Homepage": "https://wayland.freedesktop.org", "Homepage": "https://wayland.freedesktop.org",
"Version": "unstable v2",
"LicenseId": "HPND", "LicenseId": "HPND",
"License": "HPND License", "License": "HPND License",
"LicenseFile": "HPND_LICENSE.txt", "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", "Description": "The linux dmabuf protocol is a way to create dmabuf-based wl_buffers",
"Homepage": "https://wayland.freedesktop.org", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "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.", "Description": "Allows clients to request that the compositor creates its EGLStream.",
"Homepage": "https://github.com/NVIDIA/egl-wayland", "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", "LicenseId": "MIT",
"License": "MIT License", "License": "MIT License",
"LicenseFile": "MIT_LICENSE.txt", "LicenseFile": "MIT_LICENSE.txt",

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@
reset. reset.
</description> </description>
<interface name="zxdg_output_manager_v1" version="2"> <interface name="zxdg_output_manager_v1" version="3">
<description summary="manage xdg_output objects"> <description summary="manage xdg_output objects">
A global factory interface for xdg_output objects. A global factory interface for xdg_output objects.
</description> </description>
@ -77,12 +77,17 @@
</request> </request>
</interface> </interface>
<interface name="zxdg_output_v1" version="2"> <interface name="zxdg_output_v1" version="3">
<description summary="compositor logical output region"> <description summary="compositor logical output region">
An xdg_output describes part of the compositor geometry. An xdg_output describes part of the compositor geometry.
This typically corresponds to a monitor that displays part of the This typically corresponds to a monitor that displays part of the
compositor space. 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> </description>
<request name="destroy" type="destructor"> <request name="destroy" type="destructor">
@ -157,6 +162,10 @@
This allows changes to the xdg_output properties to be seen as This allows changes to the xdg_output properties to be seen as
atomic, even if they happen via multiple events. 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> </description>
</event> </event>
@ -197,10 +206,12 @@
output via :1'. output via :1'.
The description event is sent after creating an xdg_output (see 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 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 the wl_output global.
at all.
</description> </description>
<arg name="description" type="string" summary="output description"/> <arg name="description" type="string" summary="output description"/>
</event> </event>

View File

@ -118,7 +118,9 @@
child surface relative to a parent surface. Rules can be defined to ensure 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 the child surface remains within the visible area's borders, and to
specify how the child surface changes its position, such as sliding along 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. 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 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 where the top left of the popup should be placed, relative to the
local surface coordinates of the parent surface. See 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 The client must call wl_surface.commit on the corresponding wl_surface
for the xdg_popup state to take effect. for the xdg_popup state to take effect.

View File

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

View File

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

View File

@ -134,12 +134,15 @@
"#endif" "#endif"
] ]
}, },
"use": "egl" "use": "egl drm"
}, },
"vulkan-server-buffer": { "vulkan-server-buffer": {
"label": "Vulkan Buffer Sharing", "label": "Vulkan Buffer Sharing",
"type": "compile", "type": "compile",
"test": { "test": {
"head": [
"#define VK_USE_PLATFORM_WAYLAND_KHR 1"
],
"include": [ "include": [
"vulkan/vulkan.h" "vulkan/vulkan.h"
], ],
@ -150,6 +153,21 @@
"return 0;" "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": { "wayland-egl": {
"label": "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" ] "output": [ "privateFeature" ]
}, },
"wayland-brcm": { "wayland-brcm": {
@ -220,7 +238,7 @@
}, },
"wayland-drm-egl-server-buffer": { "wayland-drm-egl-server-buffer": {
"label": "DRM EGL", "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" ] "output": [ "privateFeature" ]
}, },
"wayland-libhybris-egl-server-buffer": { "wayland-libhybris-egl-server-buffer": {
@ -235,7 +253,7 @@
}, },
"wayland-vulkan-server-buffer": { "wayland-vulkan-server-buffer": {
"label": "Vulkan-based server buffer integration", "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" ] "output": [ "privateFeature" ]
}, },
"wayland-shm-emulation-server-buffer": { "wayland-shm-emulation-server-buffer": {

View File

@ -42,6 +42,7 @@
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtGui/QPalette> #include <QtGui/QPalette>
#include <QtGui/QLinearGradient> #include <QtGui/QLinearGradient>
#include <QtGui/QPainterPath>
#include <qpa/qwindowsysteminterface.h> #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 # We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/dmabuf-server/dmabuf-server.pri) 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 # We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/drm-egl-server/drm-egl-server.pri) 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 # We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/shm-emulation-server/shm-emulation-server.pri) 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 # We have a bunch of C code with casts, so we can't have this option
QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual
QT += waylandclient-private QT += waylandclient-private opengl
include(../../../../hardwareintegration/client/vulkan-server/vulkan-server.pri) include(../../../../hardwareintegration/client/vulkan-server/vulkan-server.pri)

View File

@ -50,10 +50,11 @@ QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zw
, m_shell(shell) , m_shell(shell)
, m_window(window) , 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(), m_shell->present_surface(m_window->wlSurface(),
QtWayland::zwp_fullscreen_shell_v1::present_method_default, QtWayland::zwp_fullscreen_shell_v1::present_method_default,
screen->output()); output);
} }
} // namespace QtWaylandClient } // namespace QtWaylandClient

View File

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

View File

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

View File

@ -157,7 +157,9 @@ void QWaylandDataDevice::data_device_drop()
return; 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) { if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response); 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; 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) { if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); 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() void QWaylandDataDevice::data_device_leave()
{ {
if (m_dragWindow) 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(); QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
if (!drag) { 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; 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) { if (drag) {
static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); 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_sec = 1;
timeout.tv_usec = 0; timeout.tv_usec = 0;
int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); Q_FOREVER {
if (ready < 0) { int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
qWarning() << "QWaylandDataOffer: select() failed"; if (ready < 0) {
return -1; qWarning() << "QWaylandDataOffer: select() failed";
} else if (ready == 0) { return -1;
qWarning("QWaylandDataOffer: timeout reading from pipe"); } else if (ready == 0) {
return -1; qWarning("QWaylandDataOffer: timeout reading from pipe");
} else { return -1;
char buf[4096]; } else {
int n = QT_READ(fd, buf, sizeof buf); char buf[4096];
int n = QT_READ(fd, buf, sizeof buf);
if (n > 0) { if (n < 0) {
data.append(buf, n); qWarning("QWaylandDataOffer: read() failed");
n = readData(fd, data); return -1;
} else if (n < 0) { } else if (n == 0) {
qWarning("QWaylandDataOffer: read() failed"); return 0;
} else if (n > 0) {
data.append(buf, n);
}
} }
return n;
} }
} }

View File

@ -69,6 +69,7 @@
#include "qwaylandextendedsurface_p.h" #include "qwaylandextendedsurface_p.h"
#include "qwaylandsubsurface_p.h" #include "qwaylandsubsurface_p.h"
#include "qwaylandtouch_p.h" #include "qwaylandtouch_p.h"
#include "qwaylandtabletv2_p.h"
#include "qwaylandqtkey_p.h" #include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
@ -114,6 +115,10 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
return nullptr; 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()); return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
} }
@ -184,6 +189,18 @@ QWaylandDisplay::~QWaylandDisplay(void)
wl_display_disconnect(mDisplay); 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 void QWaylandDisplay::checkError() const
{ {
int ecode = wl_display_get_error(mDisplay); 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 QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{ {
for (int i = 0; i < mScreens.size(); ++i) { for (auto screen : qAsConst(mScreens)) {
QWaylandScreen *screen = static_cast<QWaylandScreen *>(mScreens.at(i));
if (screen->output() == output) if (screen->output() == output)
return screen; return screen;
} }
@ -263,6 +279,11 @@ void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
return; return;
mScreens.append(screen); mScreens.append(screen);
QWindowSystemInterface::handleScreenAdded(screen); QWindowSystemInterface::handleScreenAdded(screen);
if (mPlaceholderScreen) {
QWindowSystemInterface::handleScreenRemoved(mPlaceholderScreen);
// handleScreenRemoved deletes the platform screen
mPlaceholderScreen = nullptr;
}
} }
void QWaylandDisplay::waitForScreens() void QWaylandDisplay::waitForScreens()
@ -310,6 +331,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
mTouchExtension.reset(new QWaylandTouchExtension(this, id)); mTouchExtension.reset(new QWaylandTouchExtension(this, id));
} else if (interface == QStringLiteral("zqt_key_v1")) { } else if (interface == QStringLiteral("zqt_key_v1")) {
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); 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) #if QT_CONFIG(wayland_client_primary_selection)
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
@ -328,7 +351,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
forceRoundTrip(); forceRoundTrip();
} }
} else if (interface == QLatin1String("zxdg_output_manager_v1")) { } 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)) for (auto *screen : qAsConst(mWaitingScreens))
screen->initXdgOutput(xdgOutputManager()); screen->initXdgOutput(xdgOutputManager());
forceRoundTrip(); forceRoundTrip();
@ -358,6 +381,8 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
for (QWaylandScreen *screen : qAsConst(mScreens)) { for (QWaylandScreen *screen : qAsConst(mScreens)) {
if (screen->outputId() == id) { if (screen->outputId() == id) {
mScreens.removeOne(screen); mScreens.removeOne(screen);
// If this is the last screen, we have to add a fake screen, or Qt will break.
ensureScreen();
QWindowSystemInterface::handleScreenRemoved(screen); QWindowSystemInterface::handleScreenRemoved(screen);
break; break;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -53,6 +53,12 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient { 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) QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
: QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2)) : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2))
, m_outputId(id) , m_outputId(id)
@ -95,7 +101,7 @@ void QWaylandScreen::maybeInitialize()
updateXdgOutputProperties(); updateXdgOutputProperties();
} }
void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager) void QWaylandScreen::initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager)
{ {
Q_ASSERT(xdgOutputManager); Q_ASSERT(xdgOutputManager);
if (zxdg_output_v1::isInitialized()) if (zxdg_output_v1::isInitialized())
@ -165,11 +171,18 @@ QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
{ {
QList<QPlatformScreen *> list; QList<QPlatformScreen *> list;
const QList<QWaylandScreen*> screens = mWaylandDisplay->screens(); 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)) { for (QWaylandScreen *screen : qAsConst(screens)) {
if (screen->screen()) if (screen->screen())
list << screen; list << screen;
} }
if (placeholder)
list << placeholder;
return list; return list;
} }
@ -210,9 +223,11 @@ QPlatformCursor *QWaylandScreen::cursor() const
} }
#endif // QT_CONFIG(cursor) #endif // QT_CONFIG(cursor)
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window)
{ {
QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window); QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window);
if (platformScreen->isPlaceholder())
return nullptr;
return static_cast<QWaylandScreen *>(platformScreen); return static_cast<QWaylandScreen *>(platformScreen);
} }
@ -262,10 +277,15 @@ void QWaylandScreen::output_scale(int32_t factor)
void QWaylandScreen::output_done() void QWaylandScreen::output_done()
{ {
mOutputDone = true; mOutputDone = true;
if (mInitialized) if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3)
mXdgOutputDone = true;
if (mInitialized) {
updateOutputProperties(); updateOutputProperties();
else if (zxdg_output_v1::isInitialized())
updateXdgOutputProperties();
} else {
maybeInitialize(); maybeInitialize();
}
} }
void QWaylandScreen::updateOutputProperties() 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() 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; mXdgOutputDone = true;
if (mInitialized) if (mInitialized)
updateXdgOutputProperties(); updateXdgOutputProperties();

View File

@ -57,7 +57,6 @@
#include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwayland-xdg-output-unstable-v1.h> #include <QtWaylandClient/private/qwayland-xdg-output-unstable-v1.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace QtWaylandClient { namespace QtWaylandClient {
@ -65,6 +64,14 @@ namespace QtWaylandClient {
class QWaylandDisplay; class QWaylandDisplay;
class QWaylandCursor; 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 class Q_WAYLAND_CLIENT_EXPORT QWaylandScreen : public QPlatformScreen, QtWayland::wl_output, QtWayland::zxdg_output_v1
{ {
public: public:
@ -73,7 +80,7 @@ public:
void maybeInitialize(); void maybeInitialize();
void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager); void initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager);
QWaylandDisplay *display() const; QWaylandDisplay *display() const;

View File

@ -47,6 +47,7 @@
#include <QtCore/qstandardpaths.h> #include <QtCore/qstandardpaths.h>
#include <QtCore/qtemporaryfile.h> #include <QtCore/qtemporaryfile.h>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtGui/QTransform>
#include <QMutexLocker> #include <QMutexLocker>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> #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) : QPlatformBackingStore(window)
, mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display()) , mDisplay(display)
{ {
} }
@ -328,7 +329,7 @@ void QWaylandShmBackingStore::updateDecorations()
qreal dp = sourceImage.devicePixelRatio(); qreal dp = sourceImage.devicePixelRatio();
int dpWidth = int(sourceImage.width() / dp); int dpWidth = int(sourceImage.width() / dp);
int dpHeight = int(sourceImage.height() / dp); int dpHeight = int(sourceImage.height() / dp);
QMatrix sourceMatrix; QTransform sourceMatrix;
sourceMatrix.scale(dp, dp); sourceMatrix.scale(dp, dp);
QRect target; // needs to be in device independent pixels QRect target; // needs to be in device independent pixels

View File

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

View File

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

View File

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

View File

@ -72,8 +72,12 @@ QWaylandSurface *QWaylandSurface::fromWlSurface(::wl_surface *surface)
void QWaylandSurface::handleScreenRemoved(QScreen *qScreen) void QWaylandSurface::handleScreenRemoved(QScreen *qScreen)
{ {
auto *screen = static_cast<QWaylandScreen *>(qScreen->handle()); auto *platformScreen = qScreen->handle();
if (m_screens.removeOne(screen)) if (platformScreen->isPlaceholder())
return;
auto *waylandScreen = static_cast<QWaylandScreen *>(qScreen->handle());
if (m_screens.removeOne(waylandScreen))
emit screensChanged(); 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 "qwaylanddisplay_p.h"
#include "qwaylandsurface_p.h" #include "qwaylandsurface_p.h"
#include <QtGui/QTouchDevice>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace QtWaylandClient { namespace QtWaylandClient {
@ -165,22 +167,28 @@ void QWaylandTouchExtension::sendTouchEvent()
QWindowSystemInterface::handleTouchEvent(mTargetWindow, mTimestamp, mTouchDevice, mTouchPoints); QWindowSystemInterface::handleTouchEvent(mTargetWindow, mTimestamp, mTouchDevice, mTouchPoints);
Qt::TouchPointStates states = 0; Qt::TouchPointStates states = {};
for (int i = 0; i < mTouchPoints.count(); ++i) for (int i = 0; i < mTouchPoints.count(); ++i)
states |= mTouchPoints.at(i).state; states |= mTouchPoints.at(i).state;
if (mFlags & QT_TOUCH_EXTENSION_FLAGS_MOUSE_FROM_TOUCH) { 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; mMouseSourceId = mTouchPoints.first().id;
for (int i = 0; i < mTouchPoints.count(); ++i) { for (int i = 0; i < mTouchPoints.count(); ++i) {
const QWindowSystemInterface::TouchPoint &tp(mTouchPoints.at(i)); const QWindowSystemInterface::TouchPoint &tp(mTouchPoints.at(i));
if (tp.id == mMouseSourceId) { 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(); mLastMouseGlobal = tp.area.center();
QPoint globalPoint = mLastMouseGlobal.toPoint(); QPoint globalPoint = mLastMouseGlobal.toPoint();
QPointF delta = mLastMouseGlobal - globalPoint; QPointF delta = mLastMouseGlobal - globalPoint;
mLastMouseLocal = mTargetWindow->mapFromGlobal(globalPoint) + delta; 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) if (buttons == Qt::NoButton)
mMouseSourceId = -1; mMouseSourceId = -1;
break; break;
@ -200,7 +208,7 @@ void QWaylandTouchExtension::touchCanceled()
mTouchPoints.clear(); mTouchPoints.clear();
mPrevTouchPoints.clear(); mPrevTouchPoints.clear();
if (mMouseSourceId != -1) 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) void QWaylandTouchExtension::touch_extension_configure(uint32_t flags)

View File

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

View File

@ -50,7 +50,7 @@ namespace QtWaylandClient {
class QWaylandVulkanWindow : public QWaylandWindow class QWaylandVulkanWindow : public QWaylandWindow
{ {
public: public:
explicit QWaylandVulkanWindow(QWindow *window); explicit QWaylandVulkanWindow(QWindow *window, QWaylandDisplay *display);
~QWaylandVulkanWindow() override; ~QWaylandVulkanWindow() override;
WindowType windowType() const 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::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window) QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
: QPlatformWindow(window) : QPlatformWindow(window)
, mDisplay(waylandScreen()->display()) , mDisplay(display)
, mFrameQueue(mDisplay->createEventQueue()) , mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{ {
@ -125,9 +125,10 @@ void QWaylandWindow::initWindow()
if (shouldCreateSubSurface()) { if (shouldCreateSubSurface()) {
Q_ASSERT(!mSubSurfaceWindow); Q_ASSERT(!mSubSurfaceWindow);
QWaylandWindow *p = static_cast<QWaylandWindow *>(QPlatformWindow::parent()); auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
if (::wl_subsurface *ss = mDisplay->createSubSurface(this, p)) { if (parent->wlSurface()) {
mSubSurfaceWindow = new QWaylandSubSurface(this, p, ss); if (::wl_subsurface *subsurface = mDisplay->createSubSurface(this, parent))
mSubSurfaceWindow = new QWaylandSubSurface(this, parent, subsurface);
} }
} else if (shouldCreateShellSurface()) { } else if (shouldCreateShellSurface()) {
Q_ASSERT(!mShellSurface); 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 // 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() // 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 (mSurface) {
if (auto *screen = mSurface->oldestEnteredScreen()) if (auto *screen = mSurface->oldestEnteredScreen())
return screen; return screen;
} }
return waylandScreen(); return QPlatformWindow::screen();
} }
void QWaylandWindow::setVisible(bool visible) void QWaylandWindow::setVisible(bool visible)
@ -425,8 +426,7 @@ void QWaylandWindow::setVisible(bool visible)
// QWaylandShmBackingStore::beginPaint(). // QWaylandShmBackingStore::beginPaint().
} else { } else {
sendExposeEvent(QRect()); sendExposeEvent(QRect());
if (window()->type() == Qt::Popup) closePopups(this);
closePopups(this);
reset(); reset();
} }
} }
@ -672,6 +672,19 @@ QRect QWaylandWindow::windowContentGeometry() const
return QRect(QPoint(), surfaceSize()); 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() wl_surface *QWaylandWindow::wlSurface()
{ {
return mSurface ? mSurface->object() : nullptr; return mSurface ? mSurface->object() : nullptr;
@ -689,7 +702,11 @@ QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const
QWaylandScreen *QWaylandWindow::waylandScreen() 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) void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
@ -852,7 +869,7 @@ QWaylandWindow *QWaylandWindow::transientParent() const
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e) void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e)
{ {
if (e.type == QWaylandPointerEvent::Leave) { if (e.type == QEvent::Leave) {
if (mWindowDecoration) { if (mWindowDecoration) {
if (mMouseEventsInContentArea) if (mMouseEventsInContentArea)
QWindowSystemInterface::handleLeaveEvent(window()); QWindowSystemInterface::handleLeaveEvent(window());
@ -869,24 +886,26 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
handleMouseEventWithDecoration(inputDevice, e); handleMouseEventWithDecoration(inputDevice, e);
} else { } else {
switch (e.type) { switch (e.type) {
case QWaylandPointerEvent::Enter: case QEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global); QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global);
break; break;
case QWaylandPointerEvent::Press: case QEvent::MouseButtonPress:
case QWaylandPointerEvent::Release: case QEvent::MouseButtonRelease:
case QWaylandPointerEvent::Motion: case QEvent::MouseMove:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.modifiers); QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.button, e.type, e.modifiers);
break; break;
case QWaylandPointerEvent::Wheel: case QEvent::Wheel:
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global, QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
e.pixelDelta, e.angleDelta, e.modifiers, e.pixelDelta, e.angleDelta, e.modifiers,
e.phase, e.source, false); e.phase, e.source, false);
break; break;
default:
Q_UNREACHABLE();
} }
} }
#if QT_CONFIG(cursor) #if QT_CONFIG(cursor)
if (e.type == QWaylandPointerEvent::Enter) { if (e.type == QEvent::Enter) {
QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins()); QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
if (contentGeometry.contains(e.local.toPoint())) if (contentGeometry.contains(e.local.toPoint()))
restoreMouseCursor(inputDevice); restoreMouseCursor(inputDevice);
@ -918,10 +937,8 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
geometry().size().width() - marg.right(), geometry().size().width() - marg.right(),
geometry().size().height() - marg.bottom()); geometry().size().height() - marg.bottom());
if (windowRect.contains(e.local.toPoint()) || mMousePressedInContentArea != Qt::NoButton) { if (windowRect.contains(e.local.toPoint()) || mMousePressedInContentArea != Qt::NoButton) {
QPointF localTranslated = e.local; const QPointF localTranslated = mapFromWlSurface(e.local);
QPointF globalTranslated = e.global; QPointF globalTranslated = e.global;
localTranslated.setX(localTranslated.x() - marg.left());
localTranslated.setY(localTranslated.y() - marg.top());
globalTranslated.setX(globalTranslated.x() - marg.left()); globalTranslated.setX(globalTranslated.x() - marg.left());
globalTranslated.setY(globalTranslated.y() - marg.top()); globalTranslated.setY(globalTranslated.y() - marg.top());
if (!mMouseEventsInContentArea) { if (!mMouseEventsInContentArea) {
@ -932,21 +949,23 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
} }
switch (e.type) { switch (e.type) {
case QWaylandPointerEvent::Enter: case QEvent::Enter:
QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated); QWindowSystemInterface::handleEnterEvent(window(), localTranslated, globalTranslated);
break; break;
case QWaylandPointerEvent::Press: case QEvent::MouseButtonPress:
case QWaylandPointerEvent::Release: case QEvent::MouseButtonRelease:
case QWaylandPointerEvent::Motion: case QEvent::MouseMove:
QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.modifiers); QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, localTranslated, globalTranslated, e.buttons, e.button, e.type, e.modifiers);
break; break;
case QWaylandPointerEvent::Wheel: { case QEvent::Wheel: {
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
localTranslated, globalTranslated, localTranslated, globalTranslated,
e.pixelDelta, e.angleDelta, e.modifiers, e.pixelDelta, e.angleDelta, e.modifiers,
e.phase, e.source, false); e.phase, e.source, false);
break; break;
} }
default:
Q_UNREACHABLE();
} }
mMouseEventsInContentArea = true; mMouseEventsInContentArea = true;
@ -961,7 +980,7 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
void QWaylandWindow::handleScreensChanged() void QWaylandWindow::handleScreensChanged()
{ {
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents(); QPlatformScreen *newScreen = calculateScreenFromSurfaceEvents();
if (newScreen == mLastReportedScreen) if (newScreen == mLastReportedScreen)
return; return;
@ -969,7 +988,7 @@ void QWaylandWindow::handleScreensChanged()
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
mLastReportedScreen = newScreen; mLastReportedScreen = newScreen;
int scale = newScreen->scale(); int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale();
if (scale != mScale) { if (scale != mScale) {
mScale = scale; mScale = scale;
if (mSurface && mDisplay->compositorVersion() >= 3) if (mSurface && mDisplay->compositorVersion() >= 3)

View File

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

View File

@ -467,6 +467,9 @@ bool Scanner::process()
if (m_xml->hasError()) if (m_xml->hasError())
return false; 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)) for (auto b : qAsConst(m_includes))
printf("#include %s\n", b.constData()); printf("#include %s\n", b.constData());

View File

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

View File

@ -28,8 +28,8 @@
#include "mockcompositor.h" #include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow> #include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QDrag> #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 windowScreens();
void removePrimaryScreen(); void removePrimaryScreen();
void screenOrder(); void screenOrder();
void removeAllScreens();
}; };
void tst_output::primaryScreen() 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) QCOMPOSITOR_TEST_MAIN(tst_output)
#include "tst_output.moc" #include "tst_output.moc"

View File

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

View File

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

View File

@ -27,8 +27,8 @@
****************************************************************************/ ****************************************************************************/
#include "mockcompositor.h" #include "mockcompositor.h"
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow> #include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
using namespace MockCompositor; using namespace MockCompositor;
@ -71,6 +71,7 @@ private slots:
void singleTapFloat(); void singleTapFloat();
void multiTouch(); void multiTouch();
void multiTouchUpAndMotionFrame(); void multiTouchUpAndMotionFrame();
void tapAndMoveInSameFrame();
}; };
void tst_seatv5::bindsToSeat() void tst_seatv5::bindsToSeat()
@ -155,10 +156,10 @@ public:
, source(event->source()) , source(event->source())
{ {
} }
const Qt::ScrollPhase phase{}; Qt::ScrollPhase phase{};
const QPoint pixelDelta; QPoint pixelDelta;
const QPoint angleDelta; // eights of a degree, positive is upwards, left QPoint angleDelta; // eights of a degree, positive is upwards, left
const Qt::MouseEventSource source{}; Qt::MouseEventSource source{};
}; };
QVector<Event> m_events; QVector<Event> m_events;
}; };
@ -396,9 +397,9 @@ public:
, touchPoints(event->touchPoints()) , touchPoints(event->touchPoints())
{ {
} }
const QEvent::Type type{}; QEvent::Type type{};
const Qt::TouchPointStates touchPointStates{}; Qt::TouchPointStates touchPointStates{};
const QList<QTouchEvent::TouchPoint> touchPoints; QList<QTouchEvent::TouchPoint> touchPoints;
}; };
QVector<Event> m_events; QVector<Event> m_events;
}; };
@ -586,5 +587,37 @@ void tst_seatv5::multiTouchUpAndMotionFrame()
QVERIFY(window.m_events.empty()); 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) QCOMPOSITOR_TEST_MAIN(tst_seatv5)
#include "tst_seatv5.moc" #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 class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor
{ {
Q_OBJECT Q_OBJECT
@ -171,7 +181,20 @@ public:
explicit SubCompositor(CoreCompositor *compositor, int version = 1) explicit SubCompositor(CoreCompositor *compositor, int version = 1)
: QtWaylandServer::wl_subcompositor(compositor->m_display, version) : 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 { struct OutputMode {

View File

@ -52,19 +52,16 @@ DefaultCompositor::DefaultCompositor()
// Pretend we made a copy of the buffer and just release it immediately // Pretend we made a copy of the buffer and just release it immediately
surface->m_committed.buffer->send_release(); 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>()); surface->sendEnter(get<Output>());
wl_display_flush_clients(m_display); wl_display_flush_clients(m_display);
}); });
}); });
QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, [&] (XdgToplevel *toplevel) { QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, get<XdgWmBase>(), [&] (XdgToplevel *toplevel) {
// Needed because lambdas don't support Qt::DirectConnection if (m_config.autoConfigure)
exec([&]{ toplevel->sendCompleteConfigure();
if (m_config.autoConfigure) }, Qt::DirectConnection);
toplevel->sendCompleteConfigure();
});
});
} }
Q_ASSERT(isClean()); Q_ASSERT(isClean());
} }

View File

@ -56,6 +56,7 @@ public:
// Convenience functions // Convenience functions
Output *output(int i = 0) { return getAll<Output>().value(i, nullptr); } Output *output(int i = 0) { return getAll<Output>().value(i, nullptr); }
Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.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); } XdgSurface *xdgSurface(int i = 0) { return get<XdgWmBase>()->m_xdgSurfaces.value(i, nullptr); }
XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); } XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); }
XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(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 CONFIG += testcase wayland-scanner
QMAKE_USE += wayland-server 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_pending.buffer);
QVERIFY(!surface->m_committed.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::attach, this, &XdgSurface::verifyConfigured);
connect(surface, &Surface::commit, this, [this] { connect(surface, &Surface::commit, this, [this] {
m_committed = m_pending; m_committed = m_pending;

View File

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

View File

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

View File

@ -29,7 +29,7 @@
#include "mockcompositor.h" #include "mockcompositor.h"
#include <QtGui/QRasterWindow> #include <QtGui/QRasterWindow>
#if QT_CONFIG(opengl) #if QT_CONFIG(opengl)
#include <QtGui/QOpenGLWindow> #include <QtOpenGL/QOpenGLWindow>
#endif #endif
using namespace MockCompositor; using namespace MockCompositor;
@ -45,6 +45,10 @@ private slots:
void waitForFrameCallbackGl(); void waitForFrameCallbackGl();
#endif #endif
void negotiateShmFormat(); void negotiateShmFormat();
// Subsurfaces
void createSubsurface();
void createSubsurfaceForHiddenParent();
}; };
void tst_surface::createDestroySurface() 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) QCOMPOSITOR_TEST_MAIN(tst_surface)
#include "tst_surface.moc" #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 <qwayland-server-xdg-decoration-unstable-v1.h>
#include <QtOpenGL/QOpenGLWindow>
#include <QtGui/QRasterWindow> #include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h> #include <QtCore/private/qcore_unix_p.h>

View File

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

View File

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

View File

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