tablet_tool.c 12.2 KB
Newer Older
Markus Ongyerth's avatar
Markus Ongyerth committed
1
2
3
4
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include <string.h>
Drew DeVault's avatar
Drew DeVault committed
5
6
#include <assert.h>
#include <libinput.h>
emersion's avatar
emersion committed
7
#include <stdlib.h>
8
#include <wayland-util.h>
Scott Anderson's avatar
Scott Anderson committed
9
#include <wlr/backend/session.h>
10
#include <wlr/interfaces/wlr_tablet_tool.h>
emersion's avatar
emersion committed
11
#include <wlr/types/wlr_input_device.h>
Drew DeVault's avatar
Drew DeVault committed
12
#include <wlr/util/log.h>
Drew DeVault's avatar
Drew DeVault committed
13
#include "backend/libinput.h"
emersion's avatar
emersion committed
14
#include "util/signal.h"
Drew DeVault's avatar
Drew DeVault committed
15

16
17
18
19
20
21
static struct wlr_tablet_tool_impl tool_impl;

static bool tablet_tool_is_libinput(struct wlr_tablet_tool *tool) {
	return tool->impl == &tool_impl;
}

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
struct wlr_libinput_tablet_tool {
	struct wlr_tablet_tool_tool wlr_tool;

	struct libinput_tablet_tool *libinput_tool;

	bool unique;
	// Refcount for destroy + release
	size_t pad_refs;
};

// TODO: Maybe this should be a wlr_list? Do we keep it, or want to get rid of
// it?
struct tablet_tool_list_elem {
	struct wl_list link;

	struct wlr_libinput_tablet_tool *tool;
};

struct wlr_libinput_tablet {
	struct wlr_tablet_tool wlr_tool;

	struct wl_list tools; // tablet_tool_list_elem::link
};

static void destroy_tool_tool(struct wlr_libinput_tablet_tool *tool) {
	wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool);
	libinput_tablet_tool_ref(tool->libinput_tool);
	libinput_tablet_tool_set_user_data(tool->libinput_tool, NULL);
	free(tool);
}


54
55
static void destroy_tablet_tool(struct wlr_tablet_tool *tool) {
	assert(tablet_tool_is_libinput(tool));
56
	struct wlr_libinput_tablet *tablet =
57
		wl_container_of(tool, tablet, wlr_tool);
58
59
60
61
62

	struct tablet_tool_list_elem *pos;
	struct tablet_tool_list_elem *tmp;
	wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
		struct wlr_libinput_tablet_tool *tool = pos->tool;
63
64
		wl_list_remove(&pos->link);
		free(pos);
65
66
67
68
69

		if (--tool->pad_refs == 0) {
			destroy_tool_tool(tool);
		}
	}
70
71

	free(tablet);
72
73
}

74
static struct wlr_tablet_tool_impl tool_impl = {
75
	.destroy = destroy_tablet_tool,
76
77
};

Markus Ongyerth's avatar
Markus Ongyerth committed
78
struct wlr_tablet_tool *create_libinput_tablet_tool(
79
80
		struct libinput_device *libinput_dev) {
	assert(libinput_dev);
81
82
	struct wlr_libinput_tablet *libinput_tablet_tool =
		calloc(1, sizeof(struct wlr_libinput_tablet));
Markus Ongyerth's avatar
Markus Ongyerth committed
83
	if (!libinput_tablet_tool) {
84
		wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
85
86
		return NULL;
	}
Markus Ongyerth's avatar
Markus Ongyerth committed
87
	struct wlr_tablet_tool *wlr_tablet_tool = &libinput_tablet_tool->wlr_tool;
Markus Ongyerth's avatar
Markus Ongyerth committed
88

89
	wlr_list_init(&wlr_tablet_tool->paths);
Markus Ongyerth's avatar
Markus Ongyerth committed
90
	struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
91
	wlr_list_push(&wlr_tablet_tool->paths, strdup(udev_device_get_syspath(udev)));
Markus Ongyerth's avatar
Markus Ongyerth committed
92
	wlr_tablet_tool->name = strdup(libinput_device_get_name(libinput_dev));
93
	wl_list_init(&libinput_tablet_tool->tools);
Markus Ongyerth's avatar
Markus Ongyerth committed
94

95
	wlr_tablet_tool_init(wlr_tablet_tool, &tool_impl);
96
	return wlr_tablet_tool;
Drew DeVault's avatar
Drew DeVault committed
97
98
}

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
		enum libinput_tablet_tool_type value) {
	switch (value) {
	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
		return WLR_TABLET_TOOL_TYPE_PEN;
	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
		return WLR_TABLET_TOOL_TYPE_ERASER;
	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
		return WLR_TABLET_TOOL_TYPE_BRUSH;
	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
		return WLR_TABLET_TOOL_TYPE_PENCIL;
	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
		return WLR_TABLET_TOOL_TYPE_AIRBRUSH;
	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
		return WLR_TABLET_TOOL_TYPE_MOUSE;
	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
		return WLR_TABLET_TOOL_TYPE_LENS;
	}

	assert(false && "UNREACHABLE");
}

static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
		struct libinput_tablet_tool *tool) {
	struct wlr_libinput_tablet_tool *ret =
		libinput_tablet_tool_get_user_data(tool);

	if (ret) {
		return ret;
	}

	ret = calloc(1, sizeof(struct wlr_libinput_tablet_tool));
	if (!ret) {
		return NULL;
	}

	ret->libinput_tool = libinput_tablet_tool_ref(tool);
	ret->wlr_tool.pressure = libinput_tablet_tool_has_pressure(tool);
	ret->wlr_tool.distance = libinput_tablet_tool_has_distance(tool);
	ret->wlr_tool.tilt = libinput_tablet_tool_has_tilt(tool);
	ret->wlr_tool.rotation = libinput_tablet_tool_has_rotation(tool);
	ret->wlr_tool.slider = libinput_tablet_tool_has_slider(tool);
	ret->wlr_tool.wheel = libinput_tablet_tool_has_wheel(tool);

	ret->wlr_tool.hardware_serial = libinput_tablet_tool_get_serial(tool);
	ret->wlr_tool.hardware_wacom = libinput_tablet_tool_get_tool_id(tool);
	ret->wlr_tool.type = wlr_type_from_libinput_type(
		libinput_tablet_tool_get_type(tool));

	ret->unique = libinput_tablet_tool_is_unique(tool);

	wl_signal_init(&ret->wlr_tool.events.destroy);

	libinput_tablet_tool_set_user_data(tool, ret);
	return ret;
}

static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
		struct wlr_tablet_tool *wlr_dev) {
158
	assert(tablet_tool_is_libinput(wlr_dev));
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
	struct tablet_tool_list_elem *pos;
	struct wlr_libinput_tablet *tablet = wl_container_of(wlr_dev, tablet, wlr_tool);

	wl_list_for_each(pos, &tablet->tools, link) {
		if (pos->tool == tool) { // We already have a ref
			// XXX: We *could* optimize the tool to the front of
			// the list here, since we will probably get the next
			// couple of events from the same tool.
			// BUT the list should always be rather short (probably
			// single digit amount of tools) so it might be more
			// work than it saves
			return;
		}
	}

	struct tablet_tool_list_elem *new =
		calloc(1, sizeof(struct tablet_tool_list_elem));
	if (!new) {// TODO: Should we at least log?
		return;
	}

	new->tool = tool;
	wl_list_insert(&tablet->tools, &new->link);
	++tool->pad_refs;
}

Drew DeVault's avatar
Drew DeVault committed
185
void handle_tablet_tool_axis(struct libinput_event *event,
186
187
188
189
		struct libinput_device *libinput_dev) {
	struct wlr_input_device *wlr_dev =
		get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev);
	if (!wlr_dev) {
190
		wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?");
Drew DeVault's avatar
Drew DeVault committed
191
192
193
194
		return;
	}
	struct libinput_event_tablet_tool *tevent =
		libinput_event_get_tablet_tool_event(event);
195
	struct wlr_event_tablet_tool_axis wlr_event = { 0 };
196
197
198
199
	struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool(
		libinput_event_tablet_tool_get_tool(tevent));
	ensure_tool_reference(tool, wlr_dev->tablet_tool);

Tony Crisci's avatar
Tony Crisci committed
200
	wlr_event.device = wlr_dev;
201
	wlr_event.tool = &tool->wlr_tool;
Tony Crisci's avatar
Tony Crisci committed
202
203
	wlr_event.time_msec =
		usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
Drew DeVault's avatar
Drew DeVault committed
204
	if (libinput_event_tablet_tool_x_has_changed(tevent)) {
205
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_X;
206
		wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
Drew DeVault's avatar
Drew DeVault committed
207
208
	}
	if (libinput_event_tablet_tool_y_has_changed(tevent)) {
209
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_Y;
210
		wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
Drew DeVault's avatar
Drew DeVault committed
211
212
	}
	if (libinput_event_tablet_tool_pressure_has_changed(tevent)) {
213
214
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE;
		wlr_event.pressure = libinput_event_tablet_tool_get_pressure(tevent);
Drew DeVault's avatar
Drew DeVault committed
215
216
	}
	if (libinput_event_tablet_tool_distance_has_changed(tevent)) {
217
218
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE;
		wlr_event.distance = libinput_event_tablet_tool_get_distance(tevent);
Drew DeVault's avatar
Drew DeVault committed
219
220
	}
	if (libinput_event_tablet_tool_tilt_x_has_changed(tevent)) {
221
222
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X;
		wlr_event.tilt_x = libinput_event_tablet_tool_get_tilt_x(tevent);
Drew DeVault's avatar
Drew DeVault committed
223
224
	}
	if (libinput_event_tablet_tool_tilt_y_has_changed(tevent)) {
225
226
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y;
		wlr_event.tilt_y = libinput_event_tablet_tool_get_tilt_y(tevent);
Drew DeVault's avatar
Drew DeVault committed
227
228
	}
	if (libinput_event_tablet_tool_rotation_has_changed(tevent)) {
229
230
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION;
		wlr_event.rotation = libinput_event_tablet_tool_get_rotation(tevent);
Drew DeVault's avatar
Drew DeVault committed
231
232
	}
	if (libinput_event_tablet_tool_slider_has_changed(tevent)) {
233
234
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER;
		wlr_event.slider = libinput_event_tablet_tool_get_slider_position(tevent);
Drew DeVault's avatar
Drew DeVault committed
235
236
	}
	if (libinput_event_tablet_tool_wheel_has_changed(tevent)) {
237
238
		wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL;
		wlr_event.wheel_delta = libinput_event_tablet_tool_get_wheel_delta(tevent);
Drew DeVault's avatar
Drew DeVault committed
239
	}
emersion's avatar
emersion committed
240
	wlr_signal_emit_safe(&wlr_dev->tablet_tool->events.axis, &wlr_event);
Drew DeVault's avatar
Drew DeVault committed
241
242
243
}

void handle_tablet_tool_proximity(struct libinput_event *event,
244
245
246
247
		struct libinput_device *libinput_dev) {
	struct wlr_input_device *wlr_dev =
		get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev);
	if (!wlr_dev) {
248
		wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?");
Drew DeVault's avatar
Drew DeVault committed
249
250
251
252
		return;
	}
	struct libinput_event_tablet_tool *tevent =
		libinput_event_get_tablet_tool_event(event);
253
	struct wlr_event_tablet_tool_proximity wlr_event = { 0 };
254
255
256
257
258
	struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool(
		libinput_event_tablet_tool_get_tool(tevent));
	ensure_tool_reference(tool, wlr_dev->tablet_tool);

	wlr_event.tool = &tool->wlr_tool;
Tony Crisci's avatar
Tony Crisci committed
259
	wlr_event.device = wlr_dev;
Tony Crisci's avatar
Tony Crisci committed
260
261
	wlr_event.time_msec =
		usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
Drew DeVault's avatar
Drew DeVault committed
262
263
	switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
	case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
264
		wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
Drew DeVault's avatar
Drew DeVault committed
265
266
		break;
	case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN:
267
		wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_IN;
Drew DeVault's avatar
Drew DeVault committed
268
269
		break;
	}
emersion's avatar
emersion committed
270
	wlr_signal_emit_safe(&wlr_dev->tablet_tool->events.proximity, &wlr_event);
271

Markus Ongyerth's avatar
Markus Ongyerth committed
272
273
274
275
	if (libinput_event_tablet_tool_get_proximity_state(tevent) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) {
		handle_tablet_tool_axis(event, libinput_dev);
	}

276
277
	// If the tool is not unique, libinput will not find it again after the
	// proximity out, so we should destroy it
278
279
	if (!tool->unique &&
			libinput_event_tablet_tool_get_proximity_state(tevent) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
280
281
		// The tool isn't unique, it can't be on multiple tablets
		assert(tool->pad_refs == 1);
282
		assert(tablet_tool_is_libinput(wlr_dev->tablet_tool));
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
		struct wlr_libinput_tablet *tablet =
			wl_container_of(wlr_dev->tablet_tool, tablet, wlr_tool);
		struct tablet_tool_list_elem *pos;
		struct tablet_tool_list_elem *tmp;

		wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
			if (pos->tool == tool) {
				wl_list_remove(&pos->link);
				free(pos);
				break;
			}
		}

		destroy_tool_tool(tool);
	}
Drew DeVault's avatar
Drew DeVault committed
298
299
300
}

void handle_tablet_tool_tip(struct libinput_event *event,
301
302
303
304
		struct libinput_device *libinput_dev) {
	struct wlr_input_device *wlr_dev =
		get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev);
	if (!wlr_dev) {
305
		wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?");
Drew DeVault's avatar
Drew DeVault committed
306
307
		return;
	}
308
	handle_tablet_tool_axis(event, libinput_dev);
Drew DeVault's avatar
Drew DeVault committed
309
310
	struct libinput_event_tablet_tool *tevent =
		libinput_event_get_tablet_tool_event(event);
311
	struct wlr_event_tablet_tool_tip wlr_event = { 0 };
312
313
314
315
	struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool(
		libinput_event_tablet_tool_get_tool(tevent));
	ensure_tool_reference(tool, wlr_dev->tablet_tool);

Tony Crisci's avatar
Tony Crisci committed
316
	wlr_event.device = wlr_dev;
317
	wlr_event.tool = &tool->wlr_tool;
Tony Crisci's avatar
Tony Crisci committed
318
319
	wlr_event.time_msec =
		usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
Drew DeVault's avatar
Drew DeVault committed
320
321
	switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
	case LIBINPUT_TABLET_TOOL_TIP_UP:
322
		wlr_event.state = WLR_TABLET_TOOL_TIP_UP;
Drew DeVault's avatar
Drew DeVault committed
323
324
		break;
	case LIBINPUT_TABLET_TOOL_TIP_DOWN:
325
		wlr_event.state = WLR_TABLET_TOOL_TIP_DOWN;
Drew DeVault's avatar
Drew DeVault committed
326
327
		break;
	}
emersion's avatar
emersion committed
328
	wlr_signal_emit_safe(&wlr_dev->tablet_tool->events.tip, &wlr_event);
Drew DeVault's avatar
Drew DeVault committed
329
330
331
}

void handle_tablet_tool_button(struct libinput_event *event,
332
333
334
335
		struct libinput_device *libinput_dev) {
	struct wlr_input_device *wlr_dev =
		get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev);
	if (!wlr_dev) {
336
		wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?");
Drew DeVault's avatar
Drew DeVault committed
337
338
		return;
	}
339
	handle_tablet_tool_axis(event, libinput_dev);
Drew DeVault's avatar
Drew DeVault committed
340
341
	struct libinput_event_tablet_tool *tevent =
		libinput_event_get_tablet_tool_event(event);
342
	struct wlr_event_tablet_tool_button wlr_event = { 0 };
343
344
345
346
	struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool(
		libinput_event_tablet_tool_get_tool(tevent));
	ensure_tool_reference(tool, wlr_dev->tablet_tool);

Tony Crisci's avatar
Tony Crisci committed
347
	wlr_event.device = wlr_dev;
348
	wlr_event.tool = &tool->wlr_tool;
Tony Crisci's avatar
Tony Crisci committed
349
350
	wlr_event.time_msec =
		usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
351
	wlr_event.button = libinput_event_tablet_tool_get_button(tevent);
Drew DeVault's avatar
Drew DeVault committed
352
353
	switch (libinput_event_tablet_tool_get_button_state(tevent)) {
	case LIBINPUT_BUTTON_STATE_RELEASED:
354
		wlr_event.state = WLR_BUTTON_RELEASED;
Drew DeVault's avatar
Drew DeVault committed
355
356
		break;
	case LIBINPUT_BUTTON_STATE_PRESSED:
357
		wlr_event.state = WLR_BUTTON_PRESSED;
Drew DeVault's avatar
Drew DeVault committed
358
359
		break;
	}
emersion's avatar
emersion committed
360
	wlr_signal_emit_safe(&wlr_dev->tablet_tool->events.button, &wlr_event);
Drew DeVault's avatar
Drew DeVault committed
361
}