1/* swc: libswc/seat.c
2 *
3 * Copyright (c) 2013-2020 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "seat.h"
25#include "compositor.h"
26#include "data_device.h"
27#include "event.h"
28#include "internal.h"
29#include "keyboard.h"
30#include "launch.h"
31#include "pointer.h"
32#include "screen.h"
33#include "surface.h"
34#include "util.h"
35
36#include <dirent.h>
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <libinput.h>
44#if defined(__linux__)
45#include <linux/input.h>
46#elif defined(__FreeBSD__)
47#include <dev/evdev/input.h> /* mirrors linux/input.h functionality */
48#endif
49#ifdef ENABLE_LIBUDEV
50#include <libudev.h>
51#endif
52
53#ifndef NETLINK_MASK
54#define NETLINK_MASK 4
55#endif
56
57struct seat {
58 struct swc_seat base;
59
60 char *name;
61 uint32_t capabilities;
62
63 struct libinput *libinput;
64 struct wl_event_source *libinput_source;
65
66#ifdef ENABLE_LIBUDEV
67 struct udev *udev;
68#endif
69
70 struct wl_listener swc_listener;
71
72 struct wl_listener keyboard_focus_listener;
73 struct pointer pointer;
74 struct wl_listener data_device_listener;
75
76 struct wl_global *global;
77 struct wl_list resources;
78};
79
80static void
81handle_keyboard_focus_event(struct wl_listener *listener, void *data)
82{
83 struct seat *seat =
84 wl_container_of(listener, seat, keyboard_focus_listener);
85 struct event *ev = data;
86 struct input_focus_event_data *event_data = ev->data;
87
88 if (ev->type != INPUT_FOCUS_EVENT_CHANGED) {
89 return;
90 }
91
92 if (event_data->new) {
93 struct wl_client *client =
94 wl_resource_get_client(event_data->new->surface->resource);
95
96 /* Offer the selection to the new focus. */
97 data_device_offer_selection(seat->base.data_device, client);
98 }
99}
100
101static void
102handle_data_device_event(struct wl_listener *listener, void *data)
103{
104 struct seat *seat = wl_container_of(listener, seat, data_device_listener);
105 struct event *ev = data;
106
107 if (ev->type != DATA_DEVICE_EVENT_SELECTION_CHANGED) {
108 return;
109 }
110
111 if (seat->base.keyboard->focus.client) {
112 data_device_offer_selection(seat->base.data_device,
113 seat->base.keyboard->focus.client);
114 }
115}
116
117static void
118handle_swc_event(struct wl_listener *listener, void *data)
119{
120 struct seat *seat = wl_container_of(listener, seat, swc_listener);
121 struct event *ev = data;
122
123 switch (ev->type) {
124 case SWC_EVENT_DEACTIVATED:
125 libinput_suspend(seat->libinput);
126 keyboard_reset(seat->base.keyboard);
127 break;
128 case SWC_EVENT_ACTIVATED:
129 if (libinput_resume(seat->libinput) != 0) {
130 WARNING("Failed to resume libinput context\n");
131 }
132 break;
133 }
134}
135
136/* Wayland Seat Interface */
137static void
138get_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t id)
139{
140 struct seat *seat = wl_resource_get_user_data(resource);
141
142 if (!pointer_bind(&seat->pointer, client, wl_resource_get_version(resource),
143 id)) {
144 wl_resource_post_no_memory(resource);
145 }
146}
147
148static void
149get_keyboard(struct wl_client *client, struct wl_resource *resource,
150 uint32_t id)
151{
152 struct seat *seat = wl_resource_get_user_data(resource);
153
154 if (!keyboard_bind(seat->base.keyboard, client,
155 wl_resource_get_version(resource), id)) {
156 wl_resource_post_no_memory(resource);
157 }
158}
159
160static void
161get_touch(struct wl_client *client, struct wl_resource *resource, uint32_t id)
162{
163 /* XXX: Implement */
164}
165
166static const struct wl_seat_interface seat_impl = {
167 .get_pointer = get_pointer,
168 .get_keyboard = get_keyboard,
169 .get_touch = get_touch,
170 .release = destroy_resource,
171};
172
173static void
174bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
175{
176 struct seat *seat = data;
177 struct wl_resource *resource;
178
179 resource = wl_resource_create(client, &wl_seat_interface, version, id);
180 if (!resource) {
181 wl_client_post_no_memory(client);
182 return;
183 }
184 wl_resource_set_implementation(resource, &seat_impl, seat,
185 &remove_resource);
186 wl_list_insert(&seat->resources, wl_resource_get_link(resource));
187
188 if (version >= 2) {
189 wl_seat_send_name(resource, seat->name);
190 }
191
192 wl_seat_send_capabilities(resource, seat->capabilities);
193}
194
195static void
196update_capabilities(struct seat *seat, uint32_t capabilities)
197{
198 struct wl_resource *resource;
199
200 if (!(~seat->capabilities & capabilities)) {
201 return;
202 }
203
204 seat->capabilities |= capabilities;
205 wl_list_for_each(resource, &seat->resources, link)
206 wl_seat_send_capabilities(resource, seat->capabilities);
207}
208
209static int
210open_restricted(const char *path, int flags, void *user_data)
211{
212 return launch_open_device(path, flags);
213}
214
215static void
216close_restricted(int fd, void *user_data)
217{
218 close(fd);
219}
220
221const struct libinput_interface libinput_interface = {
222 .open_restricted = open_restricted,
223 .close_restricted = close_restricted,
224};
225
226static uint32_t
227device_capabilities(struct libinput_device *device)
228{
229 uint32_t capabilities = 0;
230
231 if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
232 capabilities |= WL_SEAT_CAPABILITY_KEYBOARD;
233 }
234 if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
235 capabilities |= WL_SEAT_CAPABILITY_POINTER;
236 }
237 /* TODO: Add touch device support
238 * if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
239 * capabilities |= WL_SEAT_CAPABILITY_TOUCH;
240 */
241
242 return capabilities;
243}
244
245static int
246handle_libinput_data(int fd, uint32_t mask, void *data)
247{
248 struct seat *seat = data;
249 struct screen *screen;
250 struct swc_rectangle *rect;
251 struct libinput_event *generic_event;
252 struct libinput_device *device;
253 union {
254 struct libinput_event_keyboard *k;
255 struct libinput_event_pointer *p;
256 } event;
257 wl_fixed_t x, y, value;
258 uint32_t time, key, state;
259 enum wl_pointer_axis_source source;
260 int value120;
261
262 if (libinput_dispatch(seat->libinput) != 0) {
263 WARNING("libinput_dispatch failed: %s\n", strerror(errno));
264 return 0;
265 }
266
267 while ((generic_event = libinput_get_event(seat->libinput))) {
268 switch (libinput_event_get_type(generic_event)) {
269 case LIBINPUT_EVENT_DEVICE_ADDED:
270 device = libinput_event_get_device(generic_event);
271 update_capabilities(seat, device_capabilities(device));
272 if (swc.manager->new_device) {
273 swc.manager->new_device(device);
274 }
275 break;
276 case LIBINPUT_EVENT_KEYBOARD_KEY:
277 event.k = libinput_event_get_keyboard_event(generic_event);
278 time = libinput_event_keyboard_get_time(event.k);
279 key = libinput_event_keyboard_get_key(event.k);
280 state = libinput_event_keyboard_get_key_state(event.k);
281 keyboard_handle_key(seat->base.keyboard, time, key, state);
282 break;
283 case LIBINPUT_EVENT_POINTER_MOTION:
284 event.p = libinput_event_get_pointer_event(generic_event);
285 time = libinput_event_pointer_get_time(event.p);
286 x = wl_fixed_from_double(libinput_event_pointer_get_dx(event.p));
287 y = wl_fixed_from_double(libinput_event_pointer_get_dy(event.p));
288 pointer_handle_relative_motion(&seat->pointer, time, x, y);
289 pointer_handle_frame(&seat->pointer);
290 break;
291 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
292 screen = wl_container_of(swc.screens.next, screen, link);
293 rect = &screen->base.geometry;
294 event.p = libinput_event_get_pointer_event(generic_event);
295 time = libinput_event_pointer_get_time(event.p);
296 x = wl_fixed_from_double(
297 libinput_event_pointer_get_absolute_x_transformed(event.p,
298 rect->width));
299 y = wl_fixed_from_double(
300 libinput_event_pointer_get_absolute_y_transformed(
301 event.p, rect->height));
302 pointer_handle_absolute_motion(&seat->pointer, time, x, y);
303 pointer_handle_frame(&seat->pointer);
304 break;
305 case LIBINPUT_EVENT_POINTER_BUTTON:
306 event.p = libinput_event_get_pointer_event(generic_event);
307 time = libinput_event_pointer_get_time(event.p);
308 key = libinput_event_pointer_get_button(event.p);
309 state = libinput_event_pointer_get_button_state(event.p);
310 pointer_handle_button(&seat->pointer, time, key, state);
311 if (state == LIBINPUT_BUTTON_STATE_PRESSED) {
312 /* qemu generates GEAR_UP/GEAR_DOWN events on scroll, so pass
313 * those through as axis events. */
314 source = WL_POINTER_AXIS_SOURCE_WHEEL;
315 switch (key) {
316 case BTN_GEAR_DOWN:
317 pointer_handle_axis(&seat->pointer, time,
318 WL_POINTER_AXIS_VERTICAL_SCROLL, source,
319 wl_fixed_from_int(10), 120);
320 break;
321 case BTN_GEAR_UP:
322 pointer_handle_axis(&seat->pointer, time,
323 WL_POINTER_AXIS_VERTICAL_SCROLL, source,
324 wl_fixed_from_int(-10), -120);
325 break;
326 }
327 }
328 pointer_handle_frame(&seat->pointer);
329 break;
330 case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
331 source = WL_POINTER_AXIS_SOURCE_WHEEL;
332 goto scroll;
333 case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
334 source = WL_POINTER_AXIS_SOURCE_FINGER;
335 goto scroll;
336 case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
337 source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
338 goto scroll;
339 scroll:
340 event.p = libinput_event_get_pointer_event(generic_event);
341 time = libinput_event_pointer_get_time(event.p);
342 value120 = 0;
343 if (libinput_event_pointer_has_axis(
344 event.p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
345 value = wl_fixed_from_double(
346 libinput_event_pointer_get_scroll_value(
347 event.p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL));
348 if (source == WL_POINTER_AXIS_SOURCE_WHEEL) {
349 value120 = libinput_event_pointer_get_scroll_value_v120(
350 event.p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
351 }
352 pointer_handle_axis(&seat->pointer, time,
353 WL_POINTER_AXIS_VERTICAL_SCROLL, source,
354 value, value120);
355 }
356 if (libinput_event_pointer_has_axis(
357 event.p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
358 value = wl_fixed_from_double(
359 libinput_event_pointer_get_scroll_value(
360 event.p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL));
361 if (source == WL_POINTER_AXIS_SOURCE_WHEEL) {
362 value120 = libinput_event_pointer_get_scroll_value_v120(
363 event.p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
364 }
365 pointer_handle_axis(&seat->pointer, time,
366 WL_POINTER_AXIS_HORIZONTAL_SCROLL, source,
367 value, value120);
368 }
369 pointer_handle_frame(&seat->pointer);
370 break;
371 default:
372 break;
373 }
374
375 libinput_event_destroy(generic_event);
376 }
377
378 return 0;
379}
380
381bool
382initialize_libinput(struct seat *seat)
383{
384#ifdef ENABLE_LIBUDEV
385 if (!(seat->udev = udev_new())) {
386 ERROR("Could not create udev context\n");
387 goto error0;
388 }
389
390 seat->libinput =
391 libinput_udev_create_context(&libinput_interface, NULL, seat->udev);
392#else
393 seat->libinput = libinput_netlink_create_context(&libinput_interface, NULL,
394 NETLINK_MASK);
395#endif
396
397 if (!seat->libinput) {
398 ERROR("Could not create libinput context\n");
399 goto error1;
400 }
401
402#ifdef ENABLE_LIBUDEV
403 if (libinput_udev_assign_seat(seat->libinput, seat->name) != 0) {
404 ERROR("Failed to assign seat to libinput context\n");
405 goto error2;
406 }
407#else
408 if (libinput_netlink_assign_seat(seat->libinput, seat->name) != 0) {
409 ERROR("Failed to assign seat to libinput context\n");
410 goto error2;
411 }
412#endif
413
414 seat->libinput_source =
415 wl_event_loop_add_fd(swc.event_loop, libinput_get_fd(seat->libinput),
416 WL_EVENT_READABLE, &handle_libinput_data, seat);
417 if (!seat->libinput_source) {
418 ERROR("Could not create event source for libinput\n");
419 goto error2;
420 }
421
422 if (!swc.active) {
423 libinput_suspend(seat->libinput);
424 }
425
426 return true;
427
428error2:
429 libinput_unref(seat->libinput);
430error1:
431#ifdef ENABLE_LIBUDEV
432 udev_unref(seat->udev);
433error0:
434#endif
435 return false;
436}
437
438struct swc_seat *
439seat_create(struct wl_display *display, const char *seat_name)
440{
441 struct seat *seat;
442
443 seat = malloc(sizeof(*seat));
444 if (!seat) {
445 goto error0;
446 }
447 seat->name = strdup(seat_name);
448 if (!seat->name) {
449 ERROR("Could not allocate seat name string\n");
450 goto error1;
451 }
452 seat->global =
453 wl_global_create(display, &wl_seat_interface, 8, seat, &bind_seat);
454 if (!seat->global) {
455 goto error2;
456 }
457 seat->capabilities = 0;
458 wl_list_init(&seat->resources);
459
460 seat->swc_listener.notify = &handle_swc_event;
461 wl_signal_add(&swc.event_signal, &seat->swc_listener);
462
463 seat->base.data_device = data_device_create();
464 if (!seat->base.data_device) {
465 ERROR("Could not initialize data device\n");
466 goto error3;
467 }
468 seat->data_device_listener.notify = &handle_data_device_event;
469 wl_signal_add(&seat->base.data_device->event_signal,
470 &seat->data_device_listener);
471
472 seat->base.keyboard = keyboard_create(NULL);
473 if (!seat->base.keyboard) {
474 ERROR("Could not initialize keyboard\n");
475 goto error4;
476 }
477 seat->keyboard_focus_listener.notify = handle_keyboard_focus_event;
478 wl_signal_add(&seat->base.keyboard->focus.event_signal,
479 &seat->keyboard_focus_listener);
480
481 if (!pointer_initialize(&seat->pointer)) {
482 ERROR("Could not initialize pointer\n");
483 goto error5;
484 }
485 seat->base.pointer = &seat->pointer;
486
487 if (!initialize_libinput(seat)) {
488 goto error6;
489 }
490
491 return &seat->base;
492
493error6:
494 pointer_finalize(&seat->pointer);
495error5:
496 keyboard_destroy(seat->base.keyboard);
497error4:
498 data_device_destroy(seat->base.data_device);
499error3:
500 wl_global_destroy(seat->global);
501error2:
502 free(seat->name);
503error1:
504 free(seat);
505error0:
506 return NULL;
507}
508
509void
510seat_destroy(struct swc_seat *seat_base)
511{
512 struct seat *seat = wl_container_of(seat_base, seat, base);
513
514 wl_event_source_remove(seat->libinput_source);
515 libinput_unref(seat->libinput);
516#ifdef ENABLE_LIBUDEV
517 udev_unref(seat->udev);
518#endif
519
520 pointer_finalize(&seat->pointer);
521 keyboard_destroy(seat->base.keyboard);
522 data_device_destroy(seat->base.data_device);
523
524 wl_global_destroy(seat->global);
525 free(seat->name);
526 free(seat);
527}