aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/tablet.c
diff options
context:
space:
mode:
authorLibravatar John Chadwick <johnwchadwick@gmail.com>2019-09-17 21:46:29 -0700
committerLibravatar Drew DeVault <sir@cmpwn.com>2019-09-25 23:10:33 -0400
commit7e420cb6e4a334dea7296060820de12a768b76da (patch)
tree143678e6ff0a4b4223e2dfe30086eb5e2d2ab174 /sway/input/tablet.c
parentAdd support for fullscreen view direct scan-out (diff)
downloadsway-7e420cb6e4a334dea7296060820de12a768b76da.tar.gz
sway-7e420cb6e4a334dea7296060820de12a768b76da.tar.zst
sway-7e420cb6e4a334dea7296060820de12a768b76da.zip
input: Add support for tablet protocol.
Sway has basic support for drawing tablets, but does not expose properties such as pressure sensitivity. This implements the wlr tablet v2 protocol, providing tablet events to Wayland clients.
Diffstat (limited to 'sway/input/tablet.c')
-rw-r--r--sway/input/tablet.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
new file mode 100644
index 00000000..b0d4d0c6
--- /dev/null
+++ b/sway/input/tablet.c
@@ -0,0 +1,345 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h>
3#include <wlr/backend/libinput.h>
4#include <wlr/types/wlr_tablet_v2.h>
5#include "log.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/input/tablet.h"
9
10static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) {
11 struct sway_tablet_pad *pad =
12 wl_container_of(listener, pad, tablet_destroy);
13
14 pad->tablet = NULL;
15
16 wl_list_remove(&pad->tablet_destroy.link);
17 wl_list_init(&pad->tablet_destroy.link);
18}
19
20static void attach_tablet_pad(struct sway_tablet_pad *tablet_pad,
21 struct sway_tablet *tablet) {
22 sway_log(SWAY_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"",
23 tablet_pad->seat_device->input_device->wlr_device->name,
24 tablet->seat_device->input_device->wlr_device->name);
25
26 tablet_pad->tablet = tablet;
27
28 wl_list_remove(&tablet_pad->tablet_destroy.link);
29 tablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy;
30 wl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy,
31 &tablet_pad->tablet_destroy);
32}
33
34struct sway_tablet *sway_tablet_create(struct sway_seat *seat,
35 struct sway_seat_device *device) {
36 struct sway_tablet *tablet =
37 calloc(1, sizeof(struct sway_tablet));
38 if (!sway_assert(tablet, "could not allocate sway tablet for seat")) {
39 return NULL;
40 }
41
42 wl_list_insert(&seat->cursor->tablets, &tablet->link);
43
44 device->tablet = tablet;
45 tablet->seat_device = device;
46
47 return tablet;
48}
49
50void sway_configure_tablet(struct sway_tablet *tablet) {
51 struct wlr_input_device *device =
52 tablet->seat_device->input_device->wlr_device;
53 struct sway_seat *seat = tablet->seat_device->sway_seat;
54
55 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) {
56 seat_configure_xcursor(seat);
57 }
58
59 tablet->tablet_v2 =
60 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device);
61
62 /* Search for a sibling tablet pad */
63 if (!wlr_input_device_is_libinput(device)) {
64 /* We can only do this on libinput devices */
65 return;
66 }
67
68 struct libinput_device_group *group =
69 libinput_device_get_device_group(wlr_libinput_get_device_handle(device));
70 struct sway_tablet_pad *tablet_pad;
71 wl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) {
72 struct wlr_input_device *pad_device =
73 tablet_pad->seat_device->input_device->wlr_device;
74 if (!wlr_input_device_is_libinput(pad_device)) {
75 continue;
76 }
77
78 struct libinput_device_group *pad_group =
79 libinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device));
80
81 if (pad_group == group) {
82 attach_tablet_pad(tablet_pad, tablet);
83 break;
84 }
85 }
86}
87
88void sway_tablet_destroy(struct sway_tablet *tablet) {
89 if (!tablet) {
90 return;
91 }
92 wl_list_remove(&tablet->link);
93 free(tablet);
94}
95
96static void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) {
97 struct sway_tablet_tool *tool =
98 wl_container_of(listener, tool, set_cursor);
99 struct wlr_tablet_v2_event_cursor *event = data;
100
101 struct sway_cursor *cursor = tool->seat->cursor;
102 if (!seatop_allows_set_cursor(cursor->seat)) {
103 return;
104 }
105
106 struct wl_client *focused_client = NULL;
107 struct wlr_surface *focused_surface =
108 cursor->seat->wlr_seat->pointer_state.focused_surface;
109 if (focused_surface != NULL) {
110 focused_client = wl_resource_get_client(focused_surface->resource);
111 }
112
113 // TODO: check cursor mode
114 if (focused_client == NULL ||
115 event->seat_client->client != focused_client) {
116 sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client");
117 return;
118 }
119
120 cursor_set_image_surface(cursor, event->surface, event->hotspot_x,
121 event->hotspot_y, focused_client);
122}
123
124static void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) {
125 struct sway_tablet_tool *tool =
126 wl_container_of(listener, tool, tool_destroy);
127
128 wl_list_remove(&tool->tool_destroy.link);
129 wl_list_remove(&tool->set_cursor.link);
130
131 free(tool);
132}
133
134void sway_tablet_tool_configure(struct sway_tablet *tablet,
135 struct wlr_tablet_tool *wlr_tool) {
136 struct sway_tablet_tool *tool =
137 calloc(1, sizeof(struct sway_tablet_tool));
138 if (!sway_assert(tool, "could not allocate sway tablet tool for tablet")) {
139 return;
140 }
141
142 tool->seat = tablet->seat_device->sway_seat;
143 tool->tablet = tablet;
144 tool->tablet_v2_tool =
145 wlr_tablet_tool_create(server.tablet_v2,
146 tablet->seat_device->sway_seat->wlr_seat, wlr_tool);
147
148 tool->tool_destroy.notify = handle_tablet_tool_destroy;
149 wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy);
150
151 tool->set_cursor.notify = handle_tablet_tool_set_cursor;
152 wl_signal_add(&tool->tablet_v2_tool->events.set_cursor,
153 &tool->set_cursor);
154
155 wlr_tool->data = tool;
156}
157
158static void handle_tablet_pad_attach(struct wl_listener *listener,
159 void *data) {
160 struct sway_tablet_pad *pad = wl_container_of(listener, pad, attach);
161 struct wlr_tablet_tool *wlr_tool = data;
162 struct sway_tablet_tool *tool = wlr_tool->data;
163
164 if (!tool) {
165 return;
166 }
167
168 attach_tablet_pad(pad, tool->tablet);
169}
170
171static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
172 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
173 struct wlr_event_tablet_pad_ring *event = data;
174
175 if (!pad->current_surface) {
176 return;
177 }
178
179 wlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad,
180 event->ring, event->position,
181 event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
182 event->time_msec);
183}
184
185static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
186 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
187 struct wlr_event_tablet_pad_strip *event = data;
188
189 if (!pad->current_surface) {
190 return;
191 }
192
193 wlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad,
194 event->strip, event->position,
195 event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
196 event->time_msec);
197}
198
199static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
200 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
201 struct wlr_event_tablet_pad_button *event = data;
202
203 if (!pad->current_surface) {
204 return;
205 }
206
207 wlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad,
208 event->group, event->mode, event->time_msec);
209
210 wlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad,
211 event->button, event->time_msec,
212 (enum zwp_tablet_pad_v2_button_state)event->state);
213}
214
215struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
216 struct sway_seat_device *device) {
217 struct sway_tablet_pad *tablet_pad =
218 calloc(1, sizeof(struct sway_tablet_pad));
219 if (!sway_assert(tablet_pad, "could not allocate sway tablet")) {
220 return NULL;
221 }
222
223 tablet_pad->seat_device = device;
224 wl_list_init(&tablet_pad->attach.link);
225 wl_list_init(&tablet_pad->button.link);
226 wl_list_init(&tablet_pad->strip.link);
227 wl_list_init(&tablet_pad->ring.link);
228 wl_list_init(&tablet_pad->surface_destroy.link);
229 wl_list_init(&tablet_pad->tablet_destroy.link);
230
231 wl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link);
232
233 return tablet_pad;
234}
235
236void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
237 struct wlr_input_device *device =
238 tablet_pad->seat_device->input_device->wlr_device;
239 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
240
241 tablet_pad->tablet_v2_pad =
242 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device);
243
244 wl_list_remove(&tablet_pad->attach.link);
245 tablet_pad->attach.notify = handle_tablet_pad_attach;
246 wl_signal_add(&device->tablet_pad->events.attach_tablet,
247 &tablet_pad->attach);
248
249 wl_list_remove(&tablet_pad->button.link);
250 tablet_pad->button.notify = handle_tablet_pad_button;
251 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button);
252
253 wl_list_remove(&tablet_pad->strip.link);
254 tablet_pad->strip.notify = handle_tablet_pad_strip;
255 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip);
256
257 wl_list_remove(&tablet_pad->ring.link);
258 tablet_pad->ring.notify = handle_tablet_pad_ring;
259 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring);
260
261 /* Search for a sibling tablet */
262 if (!wlr_input_device_is_libinput(device)) {
263 /* We can only do this on libinput devices */
264 return;
265 }
266
267 struct libinput_device_group *group =
268 libinput_device_get_device_group(wlr_libinput_get_device_handle(device));
269 struct sway_tablet *tool;
270 wl_list_for_each(tool, &seat->cursor->tablets, link) {
271 struct wlr_input_device *tablet =
272 tool->seat_device->input_device->wlr_device;
273 if (!wlr_input_device_is_libinput(tablet)) {
274 continue;
275 }
276
277 struct libinput_device_group *tablet_group =
278 libinput_device_get_device_group(wlr_libinput_get_device_handle(tablet));
279
280 if (tablet_group == group) {
281 attach_tablet_pad(tablet_pad, tool);
282 break;
283 }
284 }
285}
286
287void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
288 if (!tablet_pad) {
289 return;
290 }
291
292 wl_list_remove(&tablet_pad->link);
293 wl_list_remove(&tablet_pad->attach.link);
294 wl_list_remove(&tablet_pad->button.link);
295 wl_list_remove(&tablet_pad->strip.link);
296 wl_list_remove(&tablet_pad->ring.link);
297 wl_list_remove(&tablet_pad->surface_destroy.link);
298 wl_list_remove(&tablet_pad->tablet_destroy.link);
299
300 free(tablet_pad);
301}
302
303static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
304 void *data) {
305 struct sway_tablet_pad *tablet_pad =
306 wl_container_of(listener, tablet_pad, surface_destroy);
307
308 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad,
309 tablet_pad->current_surface);
310 wl_list_remove(&tablet_pad->surface_destroy.link);
311 wl_list_init(&tablet_pad->surface_destroy.link);
312 tablet_pad->current_surface = NULL;
313}
314
315void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
316 struct wlr_surface *surface) {
317 if (!tablet_pad || !tablet_pad->tablet) {
318 return;
319 }
320
321 if (surface == tablet_pad->current_surface) {
322 return;
323 }
324
325 /* Leave current surface */
326 if (tablet_pad->current_surface) {
327 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad,
328 tablet_pad->current_surface);
329 wl_list_remove(&tablet_pad->surface_destroy.link);
330 wl_list_init(&tablet_pad->surface_destroy.link);
331 tablet_pad->current_surface = NULL;
332 }
333
334 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
335 return;
336 }
337
338 wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad,
339 tablet_pad->tablet->tablet_v2, surface);
340
341 tablet_pad->current_surface = surface;
342 wl_list_remove(&tablet_pad->surface_destroy.link);
343 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
344 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
345}