main neuswc / libswc / seat.c
  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}