1 #ifndef _POSIX_C_SOURCE
2 #define _POSIX_C_SOURCE 200809L
3 #endif
4 
5 #include "tablet-unstable-v2-protocol.h"
6 #include "util/array.h"
7 #include "util/time.h"
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <types/wlr_tablet_v2.h>
11 #include <wayland-util.h>
12 #include <wlr/types/wlr_tablet_tool.h>
13 #include <wlr/types/wlr_tablet_v2.h>
14 #include <wlr/util/log.h>
15 
16 static const struct wlr_tablet_tool_v2_grab_interface default_tool_grab_interface;
17 
18 static const struct wlr_surface_role tablet_tool_cursor_surface_role = {
19 	.name = "wp_tablet_tool-cursor",
20 };
21 
handle_tablet_tool_v2_set_cursor(struct wl_client * client,struct wl_resource * resource,uint32_t serial,struct wl_resource * surface_resource,int32_t hotspot_x,int32_t hotspot_y)22 static void handle_tablet_tool_v2_set_cursor(struct wl_client *client,
23 		struct wl_resource *resource, uint32_t serial,
24 		struct wl_resource *surface_resource,
25 		int32_t hotspot_x, int32_t hotspot_y) {
26 	struct wlr_tablet_tool_client_v2 *tool = tablet_tool_client_from_resource(resource);
27 	if (!tool || !tool->tool) {
28 		return;
29 	}
30 
31 	struct wlr_surface *surface = NULL;
32 	if (surface_resource != NULL) {
33 		surface = wlr_surface_from_resource(surface_resource);
34 		if (!wlr_surface_set_role(surface, &tablet_tool_cursor_surface_role, NULL,
35 				surface_resource, ZWP_TABLET_TOOL_V2_ERROR_ROLE)) {
36 			return;
37 		}
38 	}
39 
40 	struct wlr_tablet_v2_event_cursor evt = {
41 		.surface = surface,
42 		.serial = serial,
43 		.hotspot_x = hotspot_x,
44 		.hotspot_y = hotspot_y,
45 		.seat_client = tool->seat->seat_client,
46 	};
47 
48 	wl_signal_emit(&tool->tool->events.set_cursor, &evt);
49 }
50 
handle_tablet_tool_v2_destroy(struct wl_client * client,struct wl_resource * resource)51 static void handle_tablet_tool_v2_destroy(struct wl_client *client,
52 		struct wl_resource *resource) {
53 	wl_resource_destroy(resource);
54 }
55 static struct zwp_tablet_tool_v2_interface tablet_tool_impl = {
56 	.set_cursor = handle_tablet_tool_v2_set_cursor,
57 	.destroy = handle_tablet_tool_v2_destroy,
58 };
59 
tablet_type_from_wlr_type(enum wlr_tablet_tool_type wlr_type)60 static enum zwp_tablet_tool_v2_type tablet_type_from_wlr_type(
61 		enum wlr_tablet_tool_type wlr_type) {
62 	switch(wlr_type) {
63 	case WLR_TABLET_TOOL_TYPE_PEN:
64 		return ZWP_TABLET_TOOL_V2_TYPE_PEN;
65 	case WLR_TABLET_TOOL_TYPE_ERASER:
66 		return ZWP_TABLET_TOOL_V2_TYPE_ERASER;
67 	case WLR_TABLET_TOOL_TYPE_BRUSH:
68 		return ZWP_TABLET_TOOL_V2_TYPE_BRUSH;
69 	case WLR_TABLET_TOOL_TYPE_PENCIL:
70 		return ZWP_TABLET_TOOL_V2_TYPE_PENCIL;
71 	case WLR_TABLET_TOOL_TYPE_AIRBRUSH:
72 		return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH;
73 	case WLR_TABLET_TOOL_TYPE_MOUSE:
74 		return ZWP_TABLET_TOOL_V2_TYPE_MOUSE;
75 	case WLR_TABLET_TOOL_TYPE_LENS:
76 		return ZWP_TABLET_TOOL_V2_TYPE_LENS;
77 	case WLR_TABLET_TOOL_TYPE_TOTEM:
78 		// missing, see:
79 		// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/19
80 		abort();
81 	}
82 	abort(); // unreachable
83 }
84 
destroy_tablet_tool_v2(struct wl_resource * resource)85 void destroy_tablet_tool_v2(struct wl_resource *resource) {
86 	struct wlr_tablet_tool_client_v2 *client =
87 		tablet_tool_client_from_resource(resource);
88 
89 	if (!client) {
90 		return;
91 	}
92 
93 	if (client->frame_source) {
94 		wl_event_source_remove(client->frame_source);
95 	}
96 
97 	if (client->tool && client->tool->current_client == client) {
98 		client->tool->current_client = NULL;
99 	}
100 
101 	wl_list_remove(&client->seat_link);
102 	wl_list_remove(&client->tool_link);
103 	free(client);
104 
105 	wl_resource_set_user_data(resource, NULL);
106 }
107 
add_tablet_tool_client(struct wlr_tablet_seat_client_v2 * seat,struct wlr_tablet_v2_tablet_tool * tool)108 void add_tablet_tool_client(struct wlr_tablet_seat_client_v2 *seat,
109 		struct wlr_tablet_v2_tablet_tool *tool) {
110 	struct wlr_tablet_tool_client_v2 *client =
111 		calloc(1, sizeof(struct wlr_tablet_tool_client_v2));
112 	if (!client) {
113 		return;
114 	}
115 	client->tool = tool;
116 	client->seat = seat;
117 
118 	client->resource =
119 		wl_resource_create(seat->wl_client, &zwp_tablet_tool_v2_interface, 1, 0);
120 	if (!client->resource) {
121 		free(client);
122 		return;
123 	}
124 	wl_resource_set_implementation(client->resource, &tablet_tool_impl,
125 		client, destroy_tablet_tool_v2);
126 	zwp_tablet_seat_v2_send_tool_added(seat->resource, client->resource);
127 
128 	// Send the expected events
129 	if (tool->wlr_tool->hardware_serial) {
130 			zwp_tablet_tool_v2_send_hardware_serial(
131 			client->resource,
132 			tool->wlr_tool->hardware_serial >> 32,
133 			tool->wlr_tool->hardware_serial & 0xFFFFFFFF);
134 	}
135 	if (tool->wlr_tool->hardware_wacom) {
136 			zwp_tablet_tool_v2_send_hardware_id_wacom(
137 			client->resource,
138 			tool->wlr_tool->hardware_wacom >> 32,
139 			tool->wlr_tool->hardware_wacom & 0xFFFFFFFF);
140 	}
141 	zwp_tablet_tool_v2_send_type(client->resource,
142 		tablet_type_from_wlr_type(tool->wlr_tool->type));
143 
144 	if (tool->wlr_tool->tilt) {
145 		zwp_tablet_tool_v2_send_capability(client->resource,
146 			ZWP_TABLET_TOOL_V2_CAPABILITY_TILT);
147 	}
148 
149 	if (tool->wlr_tool->pressure) {
150 		zwp_tablet_tool_v2_send_capability(client->resource,
151 			ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE);
152 	}
153 
154 	if (tool->wlr_tool->distance) {
155 		zwp_tablet_tool_v2_send_capability(client->resource,
156 			ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE);
157 	}
158 
159 	if (tool->wlr_tool->rotation) {
160 		zwp_tablet_tool_v2_send_capability(client->resource,
161 			ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION);
162 	}
163 
164 	if (tool->wlr_tool->slider) {
165 		zwp_tablet_tool_v2_send_capability(client->resource,
166 			ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER);
167 	}
168 
169 	if (tool->wlr_tool->wheel) {
170 		zwp_tablet_tool_v2_send_capability(client->resource,
171 			ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL);
172 	}
173 
174 	zwp_tablet_tool_v2_send_done(client->resource);
175 
176 	client->client = seat->wl_client;
177 	wl_list_insert(&seat->tools, &client->seat_link);
178 	wl_list_insert(&tool->clients, &client->tool_link);
179 }
180 
handle_wlr_tablet_tool_destroy(struct wl_listener * listener,void * data)181 static void handle_wlr_tablet_tool_destroy(struct wl_listener *listener, void *data) {
182 	struct wlr_tablet_v2_tablet_tool *tool =
183 		wl_container_of(listener, tool, tool_destroy);
184 
185 	struct wlr_tablet_tool_client_v2 *pos;
186 	struct wlr_tablet_tool_client_v2 *tmp;
187 	wl_list_for_each_safe(pos, tmp, &tool->clients, tool_link) {
188 		zwp_tablet_tool_v2_send_removed(pos->resource);
189 		pos->tool = NULL;
190 	}
191 
192 	wl_list_remove(&tool->clients);
193 	wl_list_remove(&tool->link);
194 	wl_list_remove(&tool->tool_destroy.link);
195 	wl_list_remove(&tool->events.set_cursor.listener_list);
196 	wl_list_remove(&tool->surface_destroy.link);
197 	free(tool);
198 }
199 
wlr_tablet_tool_create(struct wlr_tablet_manager_v2 * manager,struct wlr_seat * wlr_seat,struct wlr_tablet_tool * wlr_tool)200 struct wlr_tablet_v2_tablet_tool *wlr_tablet_tool_create(
201 		struct wlr_tablet_manager_v2 *manager,
202 		struct wlr_seat *wlr_seat,
203 		struct wlr_tablet_tool *wlr_tool) {
204 	switch (wlr_tool->type) {
205 	case WLR_TABLET_TOOL_TYPE_PEN:
206 	case WLR_TABLET_TOOL_TYPE_ERASER:
207 	case WLR_TABLET_TOOL_TYPE_BRUSH:
208 	case WLR_TABLET_TOOL_TYPE_PENCIL:
209 	case WLR_TABLET_TOOL_TYPE_AIRBRUSH:
210 	case WLR_TABLET_TOOL_TYPE_MOUSE:
211 	case WLR_TABLET_TOOL_TYPE_LENS:
212 		/* supported */
213 		break;
214 	default:
215 		/* Unsupported */
216 		return NULL;
217 	}
218 
219 	struct wlr_tablet_seat_v2 *seat = get_or_create_tablet_seat(manager, wlr_seat);
220 	if (!seat) {
221 		return NULL;
222 	}
223 	struct wlr_tablet_v2_tablet_tool *tool =
224 		calloc(1, sizeof(struct wlr_tablet_v2_tablet_tool));
225 	if (!tool) {
226 		return NULL;
227 	}
228 
229 	tool->wlr_tool = wlr_tool;
230 	wl_list_init(&tool->clients);
231 	wl_list_init(&tool->surface_destroy.link);
232 	tool->default_grab.tool = tool;
233 	tool->default_grab.interface = &default_tool_grab_interface;
234 	tool->grab = &tool->default_grab;
235 
236 	tool->tool_destroy.notify = handle_wlr_tablet_tool_destroy;
237 	wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy);
238 	wl_list_insert(&seat->tools, &tool->link);
239 
240 	// We need to create a tablet client for all clients on the seat
241 	struct wlr_tablet_seat_client_v2 *pos;
242 	wl_list_for_each(pos, &seat->clients, seat_link) {
243 		// Tell the clients about the new tool
244 		add_tablet_tool_client(pos, tool);
245 	}
246 
247 	wl_signal_init(&tool->events.set_cursor);
248 
249 	return tool;
250 }
251 
tablet_tool_client_from_resource(struct wl_resource * resource)252 struct wlr_tablet_tool_client_v2 *tablet_tool_client_from_resource(struct wl_resource *resource) {
253 	assert(wl_resource_instance_of(resource, &zwp_tablet_tool_v2_interface,
254 		&tablet_tool_impl));
255 	return wl_resource_get_user_data(resource);
256 }
257 
258 
259 /* Actual protocol foo */
260 
261 // Button 0 is KEY_RESERVED in input-event-codes on linux (and freebsd)
tablet_tool_button_update(struct wlr_tablet_v2_tablet_tool * tool,uint32_t button,enum zwp_tablet_pad_v2_button_state state)262 static ssize_t tablet_tool_button_update(struct wlr_tablet_v2_tablet_tool *tool,
263 		uint32_t button, enum zwp_tablet_pad_v2_button_state state) {
264 	bool found = false;
265 	size_t i = 0;
266 	for (; i < tool->num_buttons; ++i) {
267 		if (tool->pressed_buttons[i] == button) {
268 			found = true;
269 			wlr_log(WLR_DEBUG, "Found the button \\o/: %u", button);
270 			break;
271 
272 		}
273 	}
274 
275 	if (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED && found) {
276 		/* Already have the button saved, durr */
277 		return -1;
278 	}
279 
280 	if (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED && !found) {
281 		if (tool->num_buttons < WLR_TABLET_V2_TOOL_BUTTONS_CAP) {
282 			i = tool->num_buttons++;
283 			tool->pressed_buttons[i] = button;
284 			tool->pressed_serials[i] = -1;
285 		} else {
286 			i = -1;
287 			wlr_log(WLR_ERROR, "You pressed more than %d tablet tool buttons. "
288 				"This is currently not supported by wlroots. Please report this "
289 				"with a description of your tablet, since this is either a "
290 				"bug, or fancy hardware", WLR_TABLET_V2_TOOL_BUTTONS_CAP);
291 		}
292 	}
293 	if (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED && found) {
294 		wlr_log(WLR_DEBUG, "Removed the button \\o/: %u", button);
295 		tool->pressed_buttons[i] = 0;
296 		tool->pressed_serials[i] = 0;
297 		tool->num_buttons = push_zeroes_to_end(tool->pressed_buttons, WLR_TABLET_V2_TOOL_BUTTONS_CAP);
298 		tool->num_buttons = push_zeroes_to_end(tool->pressed_serials, WLR_TABLET_V2_TOOL_BUTTONS_CAP);
299 	}
300 
301 	assert(tool->num_buttons <= WLR_TABLET_V2_TOOL_BUTTONS_CAP);
302 	return i;
303 }
304 
send_tool_frame(void * data)305 static void send_tool_frame(void *data) {
306 	struct wlr_tablet_tool_client_v2 *tool = data;
307 
308 	zwp_tablet_tool_v2_send_frame(tool->resource, get_current_time_msec());
309 	tool->frame_source = NULL;
310 }
311 
queue_tool_frame(struct wlr_tablet_tool_client_v2 * tool)312 static void queue_tool_frame(struct wlr_tablet_tool_client_v2 *tool) {
313 	struct wl_display *display = wl_client_get_display(tool->client);
314 	struct wl_event_loop *loop = wl_display_get_event_loop(display);
315 	if (!tool->frame_source) {
316 		tool->frame_source =
317 			wl_event_loop_add_idle(loop, send_tool_frame, tool);
318 	}
319 }
320 
handle_tablet_tool_surface_destroy(struct wl_listener * listener,void * data)321 static void handle_tablet_tool_surface_destroy(struct wl_listener *listener,
322 		void *data) {
323 	struct wlr_tablet_v2_tablet_tool *tool =
324 		wl_container_of(listener, tool, surface_destroy);
325 	wlr_send_tablet_v2_tablet_tool_proximity_out(tool);
326 }
327 
wlr_send_tablet_v2_tablet_tool_proximity_in(struct wlr_tablet_v2_tablet_tool * tool,struct wlr_tablet_v2_tablet * tablet,struct wlr_surface * surface)328 void wlr_send_tablet_v2_tablet_tool_proximity_in(
329 		struct wlr_tablet_v2_tablet_tool *tool,
330 		struct wlr_tablet_v2_tablet *tablet,
331 		struct wlr_surface *surface) {
332 	struct wl_client *client = wl_resource_get_client(surface->resource);
333 
334 	if (tool->focused_surface == surface) {
335 		return;
336 	}
337 
338 	wlr_send_tablet_v2_tablet_tool_proximity_out(tool);
339 
340 	struct wlr_tablet_client_v2 *tablet_tmp;
341 	struct wlr_tablet_client_v2 *tablet_client = NULL;
342 	wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) {
343 		if (tablet_tmp->client == client) {
344 			tablet_client = tablet_tmp;
345 			break;
346 		}
347 	}
348 
349 	// Couldn't find the client binding for the surface's client. Either
350 	// the client didn't bind tablet_v2 at all, or not for the relevant
351 	// seat
352 	if (!tablet_client) {
353 		return;
354 	}
355 
356 	struct wlr_tablet_tool_client_v2 *tool_tmp = NULL;
357 	struct wlr_tablet_tool_client_v2 *tool_client = NULL;
358 	wl_list_for_each(tool_tmp, &tool->clients, tool_link) {
359 		if (tool_tmp->client == client) {
360 			tool_client = tool_tmp;
361 			break;
362 		}
363 	}
364 
365 	// Couldn't find the client binding for the surface's client. Either
366 	// the client didn't bind tablet_v2 at all, or not for the relevant
367 	// seat
368 	if (!tool_client) {
369 		return;
370 	}
371 
372 	// Reinitialize the focus destroy events
373 	wl_list_remove(&tool->surface_destroy.link);
374 	wl_signal_add(&surface->events.destroy, &tool->surface_destroy);
375 	tool->surface_destroy.notify = handle_tablet_tool_surface_destroy;
376 
377 	tool->current_client = tool_client;
378 
379 	uint32_t serial = wlr_seat_client_next_serial(tool_client->seat->seat_client);
380 	tool->focused_surface = surface;
381 	tool->proximity_serial = serial;
382 
383 	zwp_tablet_tool_v2_send_proximity_in(tool_client->resource, serial,
384 		tablet_client->resource, surface->resource);
385 	/* Send all the pressed buttons */
386 	for (size_t i = 0; i < tool->num_buttons; ++i) {
387 		wlr_send_tablet_v2_tablet_tool_button(tool,
388 			tool->pressed_buttons[i],
389 			ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED);
390 	}
391 	if (tool->is_down) {
392 		wlr_send_tablet_v2_tablet_tool_down(tool);
393 	}
394 
395 	queue_tool_frame(tool_client);
396 }
397 
wlr_send_tablet_v2_tablet_tool_motion(struct wlr_tablet_v2_tablet_tool * tool,double x,double y)398 void wlr_send_tablet_v2_tablet_tool_motion(
399 		struct wlr_tablet_v2_tablet_tool *tool, double x, double y) {
400 	if (!tool->current_client) {
401 		return;
402 	}
403 
404 	zwp_tablet_tool_v2_send_motion(tool->current_client->resource,
405 		wl_fixed_from_double(x), wl_fixed_from_double(y));
406 
407 	queue_tool_frame(tool->current_client);
408 }
409 
wlr_send_tablet_v2_tablet_tool_proximity_out(struct wlr_tablet_v2_tablet_tool * tool)410 void wlr_send_tablet_v2_tablet_tool_proximity_out(
411 		struct wlr_tablet_v2_tablet_tool *tool) {
412 	if (tool->current_client) {
413 		for (size_t i = 0; i < tool->num_buttons; ++i) {
414 			zwp_tablet_tool_v2_send_button(tool->current_client->resource,
415 				tool->pressed_serials[i],
416 				tool->pressed_buttons[i],
417 				ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED);
418 		}
419 		if (tool->is_down) {
420 			zwp_tablet_tool_v2_send_up(tool->current_client->resource);
421 		}
422 		if (tool->current_client->frame_source) {
423 			wl_event_source_remove(tool->current_client->frame_source);
424 			send_tool_frame(tool->current_client);
425 		}
426 		zwp_tablet_tool_v2_send_proximity_out(tool->current_client->resource);
427 		send_tool_frame(tool->current_client);
428 
429 		wl_list_remove(&tool->surface_destroy.link);
430 		wl_list_init(&tool->surface_destroy.link);
431 		tool->current_client = NULL;
432 		tool->focused_surface = NULL;
433 	}
434 }
435 
wlr_send_tablet_v2_tablet_tool_pressure(struct wlr_tablet_v2_tablet_tool * tool,double pressure)436 void wlr_send_tablet_v2_tablet_tool_pressure(
437 		struct wlr_tablet_v2_tablet_tool *tool, double pressure) {
438 	if (tool->current_client) {
439 		zwp_tablet_tool_v2_send_pressure(tool->current_client->resource,
440 			pressure * 65535);
441 
442 		queue_tool_frame(tool->current_client);
443 	}
444 }
445 
wlr_send_tablet_v2_tablet_tool_distance(struct wlr_tablet_v2_tablet_tool * tool,double distance)446 void wlr_send_tablet_v2_tablet_tool_distance(
447 		struct wlr_tablet_v2_tablet_tool *tool, double distance) {
448 	if (tool->current_client) {
449 		zwp_tablet_tool_v2_send_distance(tool->current_client->resource,
450 			distance * 65535);
451 
452 		queue_tool_frame(tool->current_client);
453 	}
454 }
455 
wlr_send_tablet_v2_tablet_tool_tilt(struct wlr_tablet_v2_tablet_tool * tool,double x,double y)456 void wlr_send_tablet_v2_tablet_tool_tilt(
457 		struct wlr_tablet_v2_tablet_tool *tool, double x, double y) {
458 	if (!tool->current_client) {
459 		return;
460 	}
461 
462 	zwp_tablet_tool_v2_send_tilt(tool->current_client->resource,
463 		wl_fixed_from_double(x), wl_fixed_from_double(y));
464 
465 	queue_tool_frame(tool->current_client);
466 }
467 
wlr_send_tablet_v2_tablet_tool_rotation(struct wlr_tablet_v2_tablet_tool * tool,double degrees)468 void wlr_send_tablet_v2_tablet_tool_rotation(
469 		struct wlr_tablet_v2_tablet_tool *tool, double degrees) {
470 	if (!tool->current_client) {
471 		return;
472 	}
473 
474 	zwp_tablet_tool_v2_send_rotation(tool->current_client->resource,
475 		wl_fixed_from_double(degrees));
476 
477 	queue_tool_frame(tool->current_client);
478 }
479 
wlr_send_tablet_v2_tablet_tool_slider(struct wlr_tablet_v2_tablet_tool * tool,double position)480 void wlr_send_tablet_v2_tablet_tool_slider(
481 		struct wlr_tablet_v2_tablet_tool *tool, double position) {
482 	if (!tool->current_client) {
483 		return;
484 	}
485 
486 	zwp_tablet_tool_v2_send_slider(tool->current_client->resource,
487 		position * 65535);
488 
489 	queue_tool_frame(tool->current_client);
490 }
491 
wlr_send_tablet_v2_tablet_tool_button(struct wlr_tablet_v2_tablet_tool * tool,uint32_t button,enum zwp_tablet_pad_v2_button_state state)492 void wlr_send_tablet_v2_tablet_tool_button(
493 		struct wlr_tablet_v2_tablet_tool *tool, uint32_t button,
494 		enum zwp_tablet_pad_v2_button_state state) {
495 	ssize_t index = tablet_tool_button_update(tool, button, state);
496 
497 	if (tool->current_client) {
498 		uint32_t serial = wlr_seat_client_next_serial(
499 			tool->current_client->seat->seat_client);
500 		if (index >= 0) {
501 			tool->pressed_serials[index] = serial;
502 		}
503 
504 		zwp_tablet_tool_v2_send_button(tool->current_client->resource,
505 			serial, button, state);
506 		queue_tool_frame(tool->current_client);
507 	}
508 }
509 
wlr_send_tablet_v2_tablet_tool_wheel(struct wlr_tablet_v2_tablet_tool * tool,double degrees,int32_t clicks)510 void wlr_send_tablet_v2_tablet_tool_wheel(
511 	struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks) {
512 	if (tool->current_client) {
513 		zwp_tablet_tool_v2_send_wheel(tool->current_client->resource,
514 			clicks, degrees);
515 
516 		queue_tool_frame(tool->current_client);
517 	}
518 }
519 
wlr_send_tablet_v2_tablet_tool_down(struct wlr_tablet_v2_tablet_tool * tool)520 void wlr_send_tablet_v2_tablet_tool_down(struct wlr_tablet_v2_tablet_tool *tool) {
521 	if (tool->is_down) {
522 		return;
523 	}
524 
525 	tool->is_down = true;
526 	if (tool->current_client) {
527 		uint32_t serial = wlr_seat_client_next_serial(
528 			tool->current_client->seat->seat_client);
529 
530 		zwp_tablet_tool_v2_send_down(tool->current_client->resource,
531 			serial);
532 		queue_tool_frame(tool->current_client);
533 
534 		tool->down_serial = serial;
535 	}
536 }
537 
wlr_send_tablet_v2_tablet_tool_up(struct wlr_tablet_v2_tablet_tool * tool)538 void wlr_send_tablet_v2_tablet_tool_up(struct wlr_tablet_v2_tablet_tool *tool) {
539 	if (!tool->is_down) {
540 		return;
541 	}
542 	tool->is_down = false;
543 	tool->down_serial = 0;
544 
545 	if (tool->current_client) {
546 		zwp_tablet_tool_v2_send_up(tool->current_client->resource);
547 		queue_tool_frame(tool->current_client);
548 	}
549 }
550 
551 
wlr_tablet_v2_tablet_tool_notify_proximity_in(struct wlr_tablet_v2_tablet_tool * tool,struct wlr_tablet_v2_tablet * tablet,struct wlr_surface * surface)552 void wlr_tablet_v2_tablet_tool_notify_proximity_in(
553 	struct wlr_tablet_v2_tablet_tool *tool,
554 	struct wlr_tablet_v2_tablet *tablet,
555 	struct wlr_surface *surface) {
556 	if (tool->grab->interface->proximity_in) {
557 		tool->grab->interface->proximity_in(tool->grab, tablet, surface);
558 	}
559 }
560 
wlr_tablet_v2_tablet_tool_notify_down(struct wlr_tablet_v2_tablet_tool * tool)561 void wlr_tablet_v2_tablet_tool_notify_down(struct wlr_tablet_v2_tablet_tool *tool) {
562 	if (tool->grab->interface->down) {
563 		tool->grab->interface->down(tool->grab);
564 	}
565 }
wlr_tablet_v2_tablet_tool_notify_up(struct wlr_tablet_v2_tablet_tool * tool)566 void wlr_tablet_v2_tablet_tool_notify_up(struct wlr_tablet_v2_tablet_tool *tool) {
567 	if (tool->grab->interface->up) {
568 		tool->grab->interface->up(tool->grab);
569 	}
570 }
571 
wlr_tablet_v2_tablet_tool_notify_motion(struct wlr_tablet_v2_tablet_tool * tool,double x,double y)572 void wlr_tablet_v2_tablet_tool_notify_motion(
573 	struct wlr_tablet_v2_tablet_tool *tool, double x, double y) {
574 	if (tool->grab->interface->motion) {
575 		tool->grab->interface->motion(tool->grab, x, y);
576 	}
577 }
578 
wlr_tablet_v2_tablet_tool_notify_pressure(struct wlr_tablet_v2_tablet_tool * tool,double pressure)579 void wlr_tablet_v2_tablet_tool_notify_pressure(
580 	struct wlr_tablet_v2_tablet_tool *tool, double pressure) {
581 	if (tool->grab->interface->pressure) {
582 		tool->grab->interface->pressure(tool->grab, pressure);
583 	}
584 }
585 
wlr_tablet_v2_tablet_tool_notify_distance(struct wlr_tablet_v2_tablet_tool * tool,double distance)586 void wlr_tablet_v2_tablet_tool_notify_distance(
587 	struct wlr_tablet_v2_tablet_tool *tool, double distance) {
588 	if (tool->grab->interface->distance) {
589 		tool->grab->interface->distance(tool->grab, distance);
590 	}
591 }
592 
wlr_tablet_v2_tablet_tool_notify_tilt(struct wlr_tablet_v2_tablet_tool * tool,double x,double y)593 void wlr_tablet_v2_tablet_tool_notify_tilt(
594 	struct wlr_tablet_v2_tablet_tool *tool, double x, double y) {
595 	if (tool->grab->interface->tilt) {
596 		tool->grab->interface->tilt(tool->grab, x, y);
597 	}
598 }
599 
wlr_tablet_v2_tablet_tool_notify_rotation(struct wlr_tablet_v2_tablet_tool * tool,double degrees)600 void wlr_tablet_v2_tablet_tool_notify_rotation(
601 	struct wlr_tablet_v2_tablet_tool *tool, double degrees) {
602 	if (tool->grab->interface->rotation) {
603 		tool->grab->interface->rotation(tool->grab, degrees);
604 	}
605 }
606 
wlr_tablet_v2_tablet_tool_notify_slider(struct wlr_tablet_v2_tablet_tool * tool,double position)607 void wlr_tablet_v2_tablet_tool_notify_slider(
608 	struct wlr_tablet_v2_tablet_tool *tool, double position) {
609 	if (tool->grab->interface->slider) {
610 		tool->grab->interface->slider(tool->grab, position);
611 	}
612 }
613 
wlr_tablet_v2_tablet_tool_notify_wheel(struct wlr_tablet_v2_tablet_tool * tool,double degrees,int32_t clicks)614 void wlr_tablet_v2_tablet_tool_notify_wheel(
615 	struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks) {
616 	if (tool->grab->interface->wheel) {
617 		tool->grab->interface->wheel(tool->grab, degrees, clicks);
618 	}
619 }
620 
wlr_tablet_v2_tablet_tool_notify_proximity_out(struct wlr_tablet_v2_tablet_tool * tool)621 void wlr_tablet_v2_tablet_tool_notify_proximity_out(
622 	struct wlr_tablet_v2_tablet_tool *tool) {
623 	if (tool->grab->interface->proximity_out) {
624 		tool->grab->interface->proximity_out(tool->grab);
625 	}
626 }
627 
wlr_tablet_v2_tablet_tool_notify_button(struct wlr_tablet_v2_tablet_tool * tool,uint32_t button,enum zwp_tablet_pad_v2_button_state state)628 void wlr_tablet_v2_tablet_tool_notify_button(
629 	struct wlr_tablet_v2_tablet_tool *tool, uint32_t button,
630 	enum zwp_tablet_pad_v2_button_state state) {
631 	if (tool->grab->interface->button) {
632 		tool->grab->interface->button(tool->grab, button, state);
633 	}
634 }
635 
wlr_tablet_tool_v2_start_grab(struct wlr_tablet_v2_tablet_tool * tool,struct wlr_tablet_tool_v2_grab * grab)636 void wlr_tablet_tool_v2_start_grab(struct wlr_tablet_v2_tablet_tool *tool,
637 		struct wlr_tablet_tool_v2_grab *grab) {
638 	wlr_tablet_tool_v2_end_grab(tool);
639 	tool->grab = grab;
640 }
641 
wlr_tablet_tool_v2_end_grab(struct wlr_tablet_v2_tablet_tool * tool)642 void wlr_tablet_tool_v2_end_grab(struct wlr_tablet_v2_tablet_tool *tool) {
643 	if (tool->grab->interface->cancel) {
644 		tool->grab->interface->cancel(tool->grab);
645 	}
646 	tool->grab = &tool->default_grab;
647 }
648 
649 
default_tool_proximity_in(struct wlr_tablet_tool_v2_grab * grab,struct wlr_tablet_v2_tablet * tablet,struct wlr_surface * surface)650 static void default_tool_proximity_in(
651 	struct wlr_tablet_tool_v2_grab *grab,
652 	struct wlr_tablet_v2_tablet *tablet,
653 	struct wlr_surface *surface) {
654 	wlr_send_tablet_v2_tablet_tool_proximity_in(grab->tool, tablet, surface);
655 }
656 
default_tool_down(struct wlr_tablet_tool_v2_grab * grab)657 static void default_tool_down(struct wlr_tablet_tool_v2_grab *grab) {
658 	wlr_send_tablet_v2_tablet_tool_down(grab->tool);
659 }
default_tool_up(struct wlr_tablet_tool_v2_grab * grab)660 static void default_tool_up(struct wlr_tablet_tool_v2_grab *grab) {
661 	wlr_send_tablet_v2_tablet_tool_up(grab->tool);
662 }
663 
default_tool_motion(struct wlr_tablet_tool_v2_grab * grab,double x,double y)664 static void default_tool_motion(
665 		struct wlr_tablet_tool_v2_grab *grab, double x, double y) {
666 	wlr_send_tablet_v2_tablet_tool_motion(grab->tool, x, y);
667 }
668 
default_tool_pressure(struct wlr_tablet_tool_v2_grab * grab,double pressure)669 static void default_tool_pressure(
670 	struct wlr_tablet_tool_v2_grab *grab, double pressure) {
671 	wlr_send_tablet_v2_tablet_tool_pressure(grab->tool, pressure);
672 }
673 
default_tool_distance(struct wlr_tablet_tool_v2_grab * grab,double distance)674 static void default_tool_distance(
675 	struct wlr_tablet_tool_v2_grab *grab, double distance) {
676 	wlr_send_tablet_v2_tablet_tool_distance(grab->tool, distance);
677 }
678 
default_tool_tilt(struct wlr_tablet_tool_v2_grab * grab,double x,double y)679 static void default_tool_tilt(
680 	struct wlr_tablet_tool_v2_grab *grab, double x, double y) {
681 	wlr_send_tablet_v2_tablet_tool_tilt(grab->tool, x, y);
682 }
683 
default_tool_rotation(struct wlr_tablet_tool_v2_grab * grab,double degrees)684 static void default_tool_rotation(
685 	struct wlr_tablet_tool_v2_grab *grab, double degrees) {
686 	wlr_send_tablet_v2_tablet_tool_rotation(grab->tool, degrees);
687 }
688 
default_tool_slider(struct wlr_tablet_tool_v2_grab * grab,double position)689 static void default_tool_slider(
690 	struct wlr_tablet_tool_v2_grab *grab, double position) {
691 	wlr_send_tablet_v2_tablet_tool_slider(grab->tool, position);
692 }
693 
default_tool_wheel(struct wlr_tablet_tool_v2_grab * grab,double degrees,int32_t clicks)694 static void default_tool_wheel(
695 	struct wlr_tablet_tool_v2_grab *grab, double degrees, int32_t clicks) {
696 	wlr_send_tablet_v2_tablet_tool_wheel(grab->tool, degrees, clicks);
697 }
698 
default_tool_proximity_out(struct wlr_tablet_tool_v2_grab * grab)699 static void default_tool_proximity_out(struct wlr_tablet_tool_v2_grab *grab) {
700 	wlr_send_tablet_v2_tablet_tool_proximity_out(grab->tool);
701 }
702 
default_tool_button(struct wlr_tablet_tool_v2_grab * grab,uint32_t button,enum zwp_tablet_pad_v2_button_state state)703 static void default_tool_button(
704 	struct wlr_tablet_tool_v2_grab *grab, uint32_t button,
705 	enum zwp_tablet_pad_v2_button_state state) {
706 	wlr_send_tablet_v2_tablet_tool_button(grab->tool, button, state);
707 }
708 
default_tool_cancel(struct wlr_tablet_tool_v2_grab * grab)709 static void default_tool_cancel(struct wlr_tablet_tool_v2_grab *grab) {
710 	/* Do nothing. Default grab can't be canceled */
711 }
712 
713 static const struct wlr_tablet_tool_v2_grab_interface
714 		default_tool_grab_interface = {
715 	.proximity_in = default_tool_proximity_in,
716 	.down = default_tool_down,
717 	.up = default_tool_up,
718 	.motion = default_tool_motion,
719 	.pressure = default_tool_pressure,
720 	.distance = default_tool_distance,
721 	.tilt = default_tool_tilt,
722 	.rotation = default_tool_rotation,
723 	.slider = default_tool_slider,
724 	.wheel = default_tool_wheel,
725 	.proximity_out = default_tool_proximity_out,
726 	.button = default_tool_button,
727 	.cancel = default_tool_cancel,
728 };
729 
730 struct implicit_grab_state {
731 	struct wlr_surface *original;
732 	bool released;
733 
734 	struct wlr_surface *focused;
735 	struct wlr_tablet_v2_tablet *tablet;
736 };
737 
check_and_release_implicit_grab(struct wlr_tablet_tool_v2_grab * grab)738 static void check_and_release_implicit_grab(struct wlr_tablet_tool_v2_grab *grab) {
739 	struct implicit_grab_state *state = grab->data;
740 	/* Still button or tip pressed. We should hold the grab */
741 	if (grab->tool->is_down || grab->tool->num_buttons > 0 || state->released) {
742 		return;
743 	}
744 
745 	state->released = true;
746 
747 	/* We should still focus the same surface. Do nothing */
748 	if (state->original == state->focused) {
749 		wlr_tablet_tool_v2_end_grab(grab->tool);
750 		return;
751 	}
752 
753 	wlr_send_tablet_v2_tablet_tool_proximity_out(grab->tool);
754 	if (state->focused) {
755 		wlr_send_tablet_v2_tablet_tool_proximity_in(grab->tool,
756 			state->tablet, state->focused);
757 	}
758 
759 	wlr_tablet_tool_v2_end_grab(grab->tool);
760 }
761 
implicit_tool_proximity_in(struct wlr_tablet_tool_v2_grab * grab,struct wlr_tablet_v2_tablet * tablet,struct wlr_surface * surface)762 static void implicit_tool_proximity_in(
763 	struct wlr_tablet_tool_v2_grab *grab,
764 	struct wlr_tablet_v2_tablet *tablet,
765 	struct wlr_surface *surface) {
766 
767 	/* As long as we got an implicit grab, proximity won't change
768 	 * But should track the currently focused surface to change to it when
769 	 * the grab is released.
770 	 */
771 	struct implicit_grab_state *state = grab->data;
772 	state->focused = surface;
773 	state->tablet = tablet;
774 }
775 
implicit_tool_proximity_out(struct wlr_tablet_tool_v2_grab * grab)776 static void implicit_tool_proximity_out(struct wlr_tablet_tool_v2_grab *grab) {
777 	struct implicit_grab_state *state = grab->data;
778 	state->focused = NULL;
779 }
780 
implicit_tool_down(struct wlr_tablet_tool_v2_grab * grab)781 static void implicit_tool_down(struct wlr_tablet_tool_v2_grab *grab) {
782 	wlr_send_tablet_v2_tablet_tool_down(grab->tool);
783 }
784 
implicit_tool_up(struct wlr_tablet_tool_v2_grab * grab)785 static void implicit_tool_up(struct wlr_tablet_tool_v2_grab *grab) {
786 	wlr_send_tablet_v2_tablet_tool_up(grab->tool);
787 	check_and_release_implicit_grab(grab);
788 }
789 
implicit_tool_button(struct wlr_tablet_tool_v2_grab * grab,uint32_t button,enum zwp_tablet_pad_v2_button_state state)790 static void implicit_tool_button(
791 	struct wlr_tablet_tool_v2_grab *grab, uint32_t button,
792 	enum zwp_tablet_pad_v2_button_state state) {
793 	wlr_send_tablet_v2_tablet_tool_button(grab->tool, button, state);
794 	check_and_release_implicit_grab(grab);
795 }
796 
implicit_tool_cancel(struct wlr_tablet_tool_v2_grab * grab)797 static void implicit_tool_cancel(struct wlr_tablet_tool_v2_grab *grab) {
798 	check_and_release_implicit_grab(grab);
799 	free(grab->data);
800 	free(grab);
801 }
802 
803 static const struct wlr_tablet_tool_v2_grab_interface
804 		implicit_tool_grab_interface = {
805 	.proximity_in = implicit_tool_proximity_in,
806 	.down = implicit_tool_down,
807 	.up = implicit_tool_up,
808 	.motion = default_tool_motion,
809 	.pressure = default_tool_pressure,
810 	.distance = default_tool_distance,
811 	.tilt = default_tool_tilt,
812 	.rotation = default_tool_rotation,
813 	.slider = default_tool_slider,
814 	.wheel = default_tool_wheel,
815 	.proximity_out = implicit_tool_proximity_out,
816 	.button = implicit_tool_button,
817 	.cancel = implicit_tool_cancel,
818 };
819 
wlr_tablet_tool_v2_has_implicit_grab(struct wlr_tablet_v2_tablet_tool * tool)820 bool wlr_tablet_tool_v2_has_implicit_grab(
821 		struct wlr_tablet_v2_tablet_tool *tool) {
822 	return tool->grab->interface == &implicit_tool_grab_interface;
823 }
824 
wlr_tablet_tool_v2_start_implicit_grab(struct wlr_tablet_v2_tablet_tool * tool)825 void wlr_tablet_tool_v2_start_implicit_grab(
826 		struct wlr_tablet_v2_tablet_tool *tool) {
827 	if (wlr_tablet_tool_v2_has_implicit_grab(tool) || !tool->focused_surface) {
828 		return;
829 	}
830 
831 	/* No current implicit grab */
832 	if (!(tool->is_down || tool->num_buttons > 0)) {
833 		return;
834 	}
835 
836 	struct wlr_tablet_tool_v2_grab *grab =
837 		calloc(1, sizeof(struct wlr_tablet_tool_v2_grab));
838 	if (!grab) {
839 		return;
840 	}
841 
842 	grab->interface = &implicit_tool_grab_interface;
843 	grab->tool = tool;
844 	struct implicit_grab_state *state = calloc(1, sizeof(struct implicit_grab_state));
845 	if (!state) {
846 		free(grab);
847 		return;
848 	}
849 
850 	state->original = tool->focused_surface;
851 	grab->data = state;
852 
853 	wlr_tablet_tool_v2_start_grab(tool, grab);
854 }
855