aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/tablet.c
diff options
context:
space:
mode:
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}