Skip to content

Commit 202b117

Browse files
committed
Merge pull request #106251 from ArchercatNEO/wayland-cursor-shape
[Wayland] Implement the cursor-shape-v1 protocol
2 parents 13aaf5e + 3cd7b5b commit 202b117

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

platform/linuxbsd/wayland/SCsub

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ env.NoCache(
4646
"protocol/viewporter.gen.h", "#thirdparty/wayland-protocols/stable/viewporter/viewporter.xml"
4747
),
4848
env.WAYLAND_API_CODE("protocol/viewporter.gen.c", "#thirdparty/wayland-protocols/stable/viewporter/viewporter.xml"),
49+
env.WAYLAND_API_HEADER(
50+
"protocol/cursor_shape.gen.h",
51+
"#thirdparty/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml",
52+
),
53+
env.WAYLAND_API_CODE(
54+
"protocol/cursor_shape.gen.c",
55+
"#thirdparty/wayland-protocols/staging/cursor-shape/cursor-shape-v1.xml",
56+
),
4957
env.WAYLAND_API_HEADER(
5058
"protocol/fractional_scale.gen.h",
5159
"#thirdparty/wayland-protocols/staging/fractional-scale/fractional-scale-v1.xml",
@@ -158,6 +166,7 @@ env.NoCache(
158166
source_files = [
159167
"protocol/wayland.gen.c",
160168
"protocol/viewporter.gen.c",
169+
"protocol/cursor_shape.gen.c",
161170
"protocol/fractional_scale.gen.c",
162171
"protocol/xdg_shell.gen.c",
163172
"protocol/xdg_system_bell.gen.c",

platform/linuxbsd/wayland/wayland_thread.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
547547
registry->wp_viewporter_name = name;
548548
}
549549

550+
if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
551+
registry->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_cursor_shape_manager_v1_interface, 1);
552+
registry->wp_cursor_shape_manager_name = name;
553+
return;
554+
}
555+
550556
if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
551557
registry->wp_fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1);
552558
registry->wp_fractional_scale_manager_name = name;
@@ -753,6 +759,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
753759
return;
754760
}
755761

762+
if (name == registry->wp_cursor_shape_manager_name) {
763+
if (registry->wp_cursor_shape_manager) {
764+
wp_cursor_shape_manager_v1_destroy(registry->wp_cursor_shape_manager);
765+
registry->wp_cursor_shape_manager = nullptr;
766+
}
767+
768+
registry->wp_cursor_shape_manager_name = 0;
769+
770+
for (struct wl_seat *wl_seat : registry->wl_seats) {
771+
SeatState *ss = wl_seat_get_seat_state(wl_seat);
772+
ERR_FAIL_NULL(ss);
773+
774+
if (ss->wp_cursor_shape_device) {
775+
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
776+
ss->wp_cursor_shape_device = nullptr;
777+
}
778+
}
779+
}
780+
756781
if (name == registry->wp_fractional_scale_manager_name) {
757782
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : registry->wayland_thread->windows) {
758783
WindowState ws = pair.value;
@@ -1454,6 +1479,10 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat
14541479
ss->wl_pointer = wl_seat_get_pointer(wl_seat);
14551480
wl_pointer_add_listener(ss->wl_pointer, &wl_pointer_listener, ss);
14561481

1482+
if (ss->registry->wp_cursor_shape_manager) {
1483+
ss->wp_cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(ss->registry->wp_cursor_shape_manager, ss->wl_pointer);
1484+
}
1485+
14571486
if (ss->registry->wp_relative_pointer_manager) {
14581487
ss->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(ss->registry->wp_relative_pointer_manager, ss->wl_pointer);
14591488
zwp_relative_pointer_v1_add_listener(ss->wp_relative_pointer, &wp_relative_pointer_listener, ss);
@@ -1485,6 +1514,11 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat
14851514
ss->wl_pointer = nullptr;
14861515
}
14871516

1517+
if (ss->wp_cursor_shape_device) {
1518+
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
1519+
ss->wp_cursor_shape_device = nullptr;
1520+
}
1521+
14881522
if (ss->wp_relative_pointer) {
14891523
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
14901524
ss->wp_relative_pointer = nullptr;
@@ -3330,6 +3364,12 @@ void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
33303364
// We can't really reasonably scale custom cursors, so we'll let the
33313365
// compositor do it for us (badly).
33323366
scale = 1;
3367+
} else if (thread->registry.wp_cursor_shape_manager) {
3368+
wp_cursor_shape_device_v1_shape wp_shape = thread->standard_cursors[shape];
3369+
wp_cursor_shape_device_v1_set_shape(p_ss->wp_cursor_shape_device, p_ss->pointer_enter_serial, wp_shape);
3370+
3371+
// We should avoid calling the `wl_pointer_set_cursor` at the end of this method.
3372+
return;
33333373
} else {
33343374
struct wl_cursor *wl_cursor = thread->wl_cursors[shape];
33353375

@@ -4892,6 +4932,10 @@ void WaylandThread::destroy() {
48924932
wl_data_device_destroy(ss->wl_data_device);
48934933
}
48944934

4935+
if (ss->wp_cursor_shape_device) {
4936+
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
4937+
}
4938+
48954939
if (ss->wp_relative_pointer) {
48964940
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
48974941
}
@@ -4959,6 +5003,10 @@ void WaylandThread::destroy() {
49595003
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
49605004
}
49615005

5006+
if (registry.wp_cursor_shape_manager) {
5007+
wp_cursor_shape_manager_v1_destroy(registry.wp_cursor_shape_manager);
5008+
}
5009+
49625010
if (registry.wp_fractional_scale_manager) {
49635011
wp_fractional_scale_manager_v1_destroy(registry.wp_fractional_scale_manager);
49645012
}

platform/linuxbsd/wayland/wayland_thread.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@
5151
// These must go after the Wayland client include to work properly.
5252
#include "wayland/protocol/idle_inhibit.gen.h"
5353
#include "wayland/protocol/primary_selection.gen.h"
54-
// These three protocol headers name wl_pointer method arguments as `pointer`,
54+
// These four protocol headers name wl_pointer method arguments as `pointer`,
5555
// which is the same name as X11's pointer typedef. This trips some very
5656
// annoying shadowing warnings. A `#define` works around this issue.
5757
#define pointer wl_pointer
58+
#include "wayland/protocol/cursor_shape.gen.h"
5859
#include "wayland/protocol/pointer_constraints.gen.h"
5960
#include "wayland/protocol/pointer_gestures.gen.h"
6061
#include "wayland/protocol/relative_pointer.gen.h"
@@ -187,6 +188,9 @@ class WaylandThread {
187188
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
188189
uint32_t wp_fractional_scale_manager_name = 0;
189190

191+
struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager = nullptr;
192+
uint32_t wp_cursor_shape_manager_name = 0;
193+
190194
struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
191195
uint32_t xdg_decoration_manager_name = 0;
192196

@@ -416,6 +420,8 @@ class WaylandThread {
416420

417421
uint32_t pointer_enter_serial = 0;
418422

423+
struct wp_cursor_shape_device_v1 *wp_cursor_shape_device = nullptr;
424+
419425
struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
420426
struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
421427
struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
@@ -547,9 +553,32 @@ class WaylandThread {
547553
// especially as usually screen scales don't change continuously.
548554
int cursor_scale = 1;
549555

556+
// Use cursor-shape-v1 protocol if the compositor supports it.
557+
wp_cursor_shape_device_v1_shape standard_cursors[DisplayServer::CURSOR_MAX] = {
558+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT, //CURSOR_ARROW
559+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT, //CURSOR_IBEAM
560+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER, //CURSOR_POINTING_HAND
561+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR, //CURSOR_CROSS
562+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT, //CURSOR_WAIT
563+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS, //CURSOR_BUSY
564+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB, //CURSOR_DRAG
565+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING, //CURSOR_CAN_DROP
566+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP, //CURSOR_FORBIDDEN
567+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE, //CURSOR_VSIZE
568+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE, //CURSOR_HSIZE
569+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE, //CURSOR_BDIAGSIZE
570+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE, //CURSOR_FDIAGSIZE
571+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE, //CURSOR_MOVE
572+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE, //CURSOR_VSPLIT
573+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE, //CURSOR_HSPLIT
574+
wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP, //CURSOR_HELP
575+
};
576+
577+
// Fallback to reading $XCURSOR and system themes if the compositor does not.
550578
struct wl_cursor_theme *wl_cursor_theme = nullptr;
551579
struct wl_cursor *wl_cursors[DisplayServer::CURSOR_MAX] = {};
552580

581+
// User-defined cursor overrides. Take precedence over standard and wl cursors.
553582
HashMap<DisplayServer::CursorShape, CustomCursor> custom_cursors;
554583

555584
DisplayServer::CursorShape cursor_shape = DisplayServer::CURSOR_ARROW;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cursor-shape protocol
2+
3+
Maintainers:
4+
Simon Ser <[email protected]> (@emersion)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<protocol name="cursor_shape_v1">
3+
<copyright>
4+
Copyright 2018 The Chromium Authors
5+
Copyright 2023 Simon Ser
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a
8+
copy of this software and associated documentation files (the "Software"),
9+
to deal in the Software without restriction, including without limitation
10+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
11+
and/or sell copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following conditions:
13+
The above copyright notice and this permission notice (including the next
14+
paragraph) shall be included in all copies or substantial portions of the
15+
Software.
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+
DEALINGS IN THE SOFTWARE.
23+
</copyright>
24+
25+
<interface name="wp_cursor_shape_manager_v1" version="2">
26+
<description summary="cursor shape manager">
27+
This global offers an alternative, optional way to set cursor images. This
28+
new way uses enumerated cursors instead of a wl_surface like
29+
wl_pointer.set_cursor does.
30+
31+
Warning! The protocol described in this file is currently in the testing
32+
phase. Backward compatible changes may be added together with the
33+
corresponding interface version bump. Backward incompatible changes can
34+
only be done by creating a new major version of the extension.
35+
</description>
36+
37+
<request name="destroy" type="destructor">
38+
<description summary="destroy the manager">
39+
Destroy the cursor shape manager.
40+
</description>
41+
</request>
42+
43+
<request name="get_pointer">
44+
<description summary="manage the cursor shape of a pointer device">
45+
Obtain a wp_cursor_shape_device_v1 for a wl_pointer object.
46+
47+
When the pointer capability is removed from the wl_seat, the
48+
wp_cursor_shape_device_v1 object becomes inert.
49+
</description>
50+
<arg name="cursor_shape_device" type="new_id" interface="wp_cursor_shape_device_v1"/>
51+
<arg name="pointer" type="object" interface="wl_pointer"/>
52+
</request>
53+
54+
<request name="get_tablet_tool_v2">
55+
<description summary="manage the cursor shape of a tablet tool device">
56+
Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 object.
57+
58+
When the zwp_tablet_tool_v2 is removed, the wp_cursor_shape_device_v1
59+
object becomes inert.
60+
</description>
61+
<arg name="cursor_shape_device" type="new_id" interface="wp_cursor_shape_device_v1"/>
62+
<arg name="tablet_tool" type="object" interface="zwp_tablet_tool_v2"/>
63+
</request>
64+
</interface>
65+
66+
<interface name="wp_cursor_shape_device_v1" version="2">
67+
<description summary="cursor shape for a device">
68+
This interface allows clients to set the cursor shape.
69+
</description>
70+
71+
<enum name="shape">
72+
<description summary="cursor shapes">
73+
This enum describes cursor shapes.
74+
75+
The names are taken from the CSS W3C specification:
76+
https://w3c.github.io/csswg-drafts/css-ui/#cursor
77+
with a few additions.
78+
79+
Note that there are some groups of cursor shapes that are related:
80+
The first group is drag-and-drop cursors which are used to indicate
81+
the selected action during dnd operations. The second group is resize
82+
cursors which are used to indicate resizing and moving possibilities
83+
on window borders. It is recommended that the shapes in these groups
84+
should use visually compatible images and metaphors.
85+
</description>
86+
<entry name="default" value="1" summary="default cursor"/>
87+
<entry name="context_menu" value="2" summary="a context menu is available for the object under the cursor"/>
88+
<entry name="help" value="3" summary="help is available for the object under the cursor"/>
89+
<entry name="pointer" value="4" summary="pointer that indicates a link or another interactive element"/>
90+
<entry name="progress" value="5" summary="progress indicator"/>
91+
<entry name="wait" value="6" summary="program is busy, user should wait"/>
92+
<entry name="cell" value="7" summary="a cell or set of cells may be selected"/>
93+
<entry name="crosshair" value="8" summary="simple crosshair"/>
94+
<entry name="text" value="9" summary="text may be selected"/>
95+
<entry name="vertical_text" value="10" summary="vertical text may be selected"/>
96+
<entry name="alias" value="11" summary="drag-and-drop: alias of/shortcut to something is to be created"/>
97+
<entry name="copy" value="12" summary="drag-and-drop: something is to be copied"/>
98+
<entry name="move" value="13" summary="drag-and-drop: something is to be moved"/>
99+
<entry name="no_drop" value="14" summary="drag-and-drop: the dragged item cannot be dropped at the current cursor location"/>
100+
<entry name="not_allowed" value="15" summary="drag-and-drop: the requested action will not be carried out"/>
101+
<entry name="grab" value="16" summary="drag-and-drop: something can be grabbed"/>
102+
<entry name="grabbing" value="17" summary="drag-and-drop: something is being grabbed"/>
103+
<entry name="e_resize" value="18" summary="resizing: the east border is to be moved"/>
104+
<entry name="n_resize" value="19" summary="resizing: the north border is to be moved"/>
105+
<entry name="ne_resize" value="20" summary="resizing: the north-east corner is to be moved"/>
106+
<entry name="nw_resize" value="21" summary="resizing: the north-west corner is to be moved"/>
107+
<entry name="s_resize" value="22" summary="resizing: the south border is to be moved"/>
108+
<entry name="se_resize" value="23" summary="resizing: the south-east corner is to be moved"/>
109+
<entry name="sw_resize" value="24" summary="resizing: the south-west corner is to be moved"/>
110+
<entry name="w_resize" value="25" summary="resizing: the west border is to be moved"/>
111+
<entry name="ew_resize" value="26" summary="resizing: the east and west borders are to be moved"/>
112+
<entry name="ns_resize" value="27" summary="resizing: the north and south borders are to be moved"/>
113+
<entry name="nesw_resize" value="28" summary="resizing: the north-east and south-west corners are to be moved"/>
114+
<entry name="nwse_resize" value="29" summary="resizing: the north-west and south-east corners are to be moved"/>
115+
<entry name="col_resize" value="30" summary="resizing: that the item/column can be resized horizontally"/>
116+
<entry name="row_resize" value="31" summary="resizing: that the item/row can be resized vertically"/>
117+
<entry name="all_scroll" value="32" summary="something can be scrolled in any direction"/>
118+
<entry name="zoom_in" value="33" summary="something can be zoomed in"/>
119+
<entry name="zoom_out" value="34" summary="something can be zoomed out"/>
120+
<entry name="dnd_ask" value="35" summary="drag-and-drop: the user will select which action will be carried out (non-css value)" since="2"/>
121+
<entry name="all_resize" value="36" summary="resizing: something can be moved or resized in any direction (non-css value)" since="2"/>
122+
</enum>
123+
124+
<enum name="error">
125+
<entry name="invalid_shape" value="1"
126+
summary="the specified shape value is invalid"/>
127+
</enum>
128+
129+
<request name="destroy" type="destructor">
130+
<description summary="destroy the cursor shape device">
131+
Destroy the cursor shape device.
132+
133+
The device cursor shape remains unchanged.
134+
</description>
135+
</request>
136+
137+
<request name="set_shape">
138+
<description summary="set device cursor to the shape">
139+
Sets the device cursor to the specified shape. The compositor will
140+
change the cursor image based on the specified shape.
141+
142+
The cursor actually changes only if the input device focus is one of
143+
the requesting client's surfaces. If any, the previous cursor image
144+
(surface or shape) is replaced.
145+
146+
The "shape" argument must be a valid enum entry, otherwise the
147+
invalid_shape protocol error is raised.
148+
149+
This is similar to the wl_pointer.set_cursor and
150+
zwp_tablet_tool_v2.set_cursor requests, but this request accepts a
151+
shape instead of contents in the form of a surface. Clients can mix
152+
set_cursor and set_shape requests.
153+
154+
The serial parameter must match the latest wl_pointer.enter or
155+
zwp_tablet_tool_v2.proximity_in serial number sent to the client.
156+
Otherwise the request will be ignored.
157+
</description>
158+
<arg name="serial" type="uint" summary="serial number of the enter event"/>
159+
<arg name="shape" type="uint" enum="shape"/>
160+
</request>
161+
</interface>
162+
</protocol>

0 commit comments

Comments
 (0)