main shrub/neuswc / libswc / seat_ws.c
  1/* swc: libswc/seat_ws.c
  2 *
  3 * Copyright (c) 2013, 2014 Michael Forney
  4 * Copyright (c) 2019 Nia Alarie
  5 *
  6 * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 * of this software and associated documentation files (the "Software"), to deal
  8 * in the Software without restriction, including without limitation the rights
  9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 * copies of the Software, and to permit persons to whom the Software is
 11 * furnished to do so, subject to the following conditions:
 12 *
 13 * The above copyright notice and this permission notice shall be included in
 14 * all copies or substantial portions of the Software.
 15 *
 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 * SOFTWARE.
 23 */
 24
 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 "seat.h"
 34#include "surface.h"
 35#include "util.h"
 36#include "wscons/atKeynames.h"
 37#include "wscons/bsd_KbdMap.h"
 38
 39#include <errno.h>
 40#include <fcntl.h>
 41#include <stdbool.h>
 42#include <stdio.h>
 43#include <stdlib.h>
 44#include <string.h>
 45#include <unistd.h>
 46
 47#include <dev/wscons/wsconsio.h>
 48#include <dev/wscons/wsksymdef.h>
 49#include <sys/ioctl.h>
 50#include <time.h>
 51
 52/* Map wscons encodings to libxkbcommon layout names. */
 53struct ws_xkb_map {
 54	const int ws;
 55	const char *const xkb;
 56};
 57
 58static const struct ws_xkb_map ws_xkb_encodings[] = {
 59    {KB_UK, "gb"}, {KB_BE, "be"},
 60#ifdef KB_CZ
 61    {KB_CZ, "cz"},
 62#endif
 63    {KB_DK, "dk"}, {KB_NL, "nl"}, {KB_DE, "de"},
 64#ifdef KB_GR
 65    {KB_GR, "gr"},
 66#endif
 67    {KB_HU, "hu"}, {KB_IT, "it"}, {KB_JP, "jp"}, {KB_NO, "no"}, {KB_PL, "pl"},
 68    {KB_PT, "pt"}, {KB_RU, "ru"}, {KB_ES, "es"}, {KB_SV, "sv"}, {KB_SG, "sg"},
 69    {KB_TR, "tr"}, {KB_UA, "ua"}, {-1, NULL}};
 70
 71struct seat {
 72	struct swc_seat base;
 73
 74	char *name;
 75	uint32_t capabilities;
 76
 77	int mouse_fd;
 78	int kbd_fd;
 79	bool ignore;
 80
 81	unsigned kbd_type;
 82
 83	struct xkb_rule_names names;
 84
 85	struct wl_event_source *mouse_source;
 86	struct wl_event_source *kbd_source;
 87
 88	struct wl_listener swc_listener;
 89
 90	struct wl_listener keyboard_focus_listener;
 91	struct pointer pointer;
 92	struct wl_listener data_device_listener;
 93
 94	struct wl_global *global;
 95	struct wl_list resources;
 96};
 97
 98static void
 99handle_keyboard_focus_event(struct wl_listener *listener, void *data)
100{
101	struct seat *seat =
102	    wl_container_of(listener, seat, keyboard_focus_listener);
103	struct event *ev = data;
104	struct input_focus_event_data *event_data = ev->data;
105
106	if (ev->type != INPUT_FOCUS_EVENT_CHANGED) {
107		return;
108	}
109
110	if (event_data->new) {
111		struct wl_client *client =
112		    wl_resource_get_client(event_data->new->surface->resource);
113
114		/* Offer the selection to the new focus. */
115		data_device_offer_selection(seat->base.data_device, client);
116	}
117}
118
119static void
120handle_data_device_event(struct wl_listener *listener, void *data)
121{
122	struct seat *seat = wl_container_of(listener, seat, data_device_listener);
123	struct event *ev = data;
124
125	if (ev->type != DATA_DEVICE_EVENT_SELECTION_CHANGED) {
126		return;
127	}
128
129	if (seat->base.keyboard->focus.client) {
130		data_device_offer_selection(seat->base.data_device,
131		                            seat->base.keyboard->focus.client);
132	}
133}
134
135static void
136handle_swc_event(struct wl_listener *listener, void *data)
137{
138	struct seat *seat = wl_container_of(listener, seat, swc_listener);
139	struct event *ev = data;
140
141	switch (ev->type) {
142	case SWC_EVENT_DEACTIVATED:
143		seat->ignore = true;
144		keyboard_reset(seat->base.keyboard);
145		break;
146	case SWC_EVENT_ACTIVATED:
147		seat->ignore = false;
148		break;
149	}
150}
151
152/* Wayland Seat Interface */
153static void
154get_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t id)
155{
156	struct seat *seat = wl_resource_get_user_data(resource);
157
158	pointer_bind(&seat->pointer, client, wl_resource_get_version(resource), id);
159}
160
161static void
162get_keyboard(struct wl_client *client, struct wl_resource *resource,
163             uint32_t id)
164{
165	struct seat *seat = wl_resource_get_user_data(resource);
166
167	keyboard_bind(seat->base.keyboard, client,
168	              wl_resource_get_version(resource), id);
169}
170
171static void
172get_touch(struct wl_client *client, struct wl_resource *resource, uint32_t id)
173{
174	/* XXX: Implement */
175}
176
177static struct wl_seat_interface seat_impl = {
178    .get_pointer = get_pointer,
179    .get_keyboard = get_keyboard,
180    .get_touch = get_touch,
181    .release = destroy_resource,
182};
183
184static void
185bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
186{
187	struct seat *seat = data;
188	struct wl_resource *resource;
189
190	if (version > 5) {
191		version = 5;
192	}
193
194	resource = wl_resource_create(client, &wl_seat_interface, version, id);
195	wl_resource_set_implementation(resource, &seat_impl, seat,
196	                               &remove_resource);
197	wl_list_insert(&seat->resources, wl_resource_get_link(resource));
198
199	if (version >= 2) {
200		wl_seat_send_name(resource, seat->name);
201	}
202
203	wl_seat_send_capabilities(resource, seat->capabilities);
204}
205
206static int
207ws_to_xkb(unsigned type, int key)
208{
209	const uint8_t *map;
210	size_t count;
211
212	switch (type) {
213	case WSKBD_TYPE_PC_XT:
214	case WSKBD_TYPE_PC_AT:
215		map = wsXtMap;
216		count = sizeof(wsXtMap);
217		break;
218	case WSKBD_TYPE_USB:
219#ifdef WSKBD_TYPE_MAPLE
220	case WSKBD_TYPE_MAPLE:
221#endif
222		map = wsUsbMap;
223		count = sizeof(wsUsbMap);
224		break;
225	default:
226		return -1;
227	}
228
229	if (key < 0 || (size_t)key >= count || map[key] == KEY_NOTUSED)
230		return -1;
231	return map[key];
232}
233
234static int
235wsmouse_to_evdev(int button)
236{
237	/* The right and middle mouse buttons must be swapped. */
238	switch (button) {
239	case 1: /* Middle */
240		return 0x112;
241	case 2: /* Right */
242		return 0x111;
243	default:
244		return button + 0x110;
245	}
246}
247
248static int
249handle_ws_data(int fd, uint32_t mask, void *data)
250{
251	struct seat *seat = data;
252	struct wscons_event ev;
253
254	while (!seat->ignore && read(fd, &ev, sizeof(ev)) == sizeof(ev)) {
255		uint32_t state, time;
256		int key;
257		wl_fixed_t pos;
258
259		time = (uint32_t)(ev.time.tv_sec * 1000ULL +
260		                    ev.time.tv_nsec / 1000000L);
261		switch (ev.type) {
262		case WSCONS_EVENT_KEY_UP:
263		case WSCONS_EVENT_KEY_DOWN:
264			state = ev.type == WSCONS_EVENT_KEY_DOWN ?
265			    WL_KEYBOARD_KEY_STATE_PRESSED :
266			    WL_KEYBOARD_KEY_STATE_RELEASED;
267			key = ws_to_xkb(seat->kbd_type, ev.value);
268			if (key >= 0)
269				keyboard_handle_key(seat->base.keyboard, time, key, state);
270			break;
271		case WSCONS_EVENT_ALL_KEYS_UP:
272			keyboard_reset(seat->base.keyboard);
273			break;
274		case WSCONS_EVENT_MOUSE_UP:
275			state = WL_POINTER_BUTTON_STATE_RELEASED;
276			key = wsmouse_to_evdev(ev.value);
277			pointer_handle_button(seat->base.pointer, time, key, state);
278			break;
279		case WSCONS_EVENT_MOUSE_DOWN:
280			state = WL_POINTER_BUTTON_STATE_PRESSED;
281			key = wsmouse_to_evdev(ev.value);
282			pointer_handle_button(seat->base.pointer, time, key, state);
283			break;
284		case WSCONS_EVENT_MOUSE_DELTA_X:
285			pos = wl_fixed_from_int(ev.value);
286			pointer_handle_relative_motion(seat->base.pointer, time, pos, 0);
287			break;
288		case WSCONS_EVENT_MOUSE_DELTA_Y:
289			pos = wl_fixed_from_int(-ev.value);
290			pointer_handle_relative_motion(seat->base.pointer, time, 0, pos);
291			break;
292		case WSCONS_EVENT_MOUSE_DELTA_Z:
293			pos = wl_fixed_from_int(ev.value * 10);
294			pointer_handle_axis(seat->base.pointer, time,
295				WL_POINTER_AXIS_VERTICAL_SCROLL,
296				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
297			break;
298		case WSCONS_EVENT_MOUSE_DELTA_W:
299			pos = wl_fixed_from_int(ev.value * 10);
300			pointer_handle_axis(seat->base.pointer, time,
301				WL_POINTER_AXIS_HORIZONTAL_SCROLL,
302				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
303			break;
304		case WSCONS_EVENT_MOUSE_ABSOLUTE_X:
305			pos = wl_fixed_from_int(ev.value);
306			pointer_handle_absolute_motion(seat->base.pointer, time, pos, 0);
307			break;
308		case WSCONS_EVENT_MOUSE_ABSOLUTE_Y:
309			pos = wl_fixed_from_int(-ev.value);
310			pointer_handle_absolute_motion(seat->base.pointer, time, 0, pos);
311			break;
312		}
313	}
314	return 0;
315}
316
317static bool
318initialize_wscons(struct seat *seat)
319{
320#ifdef WSMOUSE_EVENT_VERSION
321	int mouse_ver = WSMOUSE_EVENT_VERSION;
322#endif
323#ifdef WSKBDIO_EVENT_VERSION
324	int kbd_ver = WSKBDIO_EVENT_VERSION;
325#endif
326	int encoding_layout;
327	kbd_t encoding;
328	unsigned i;
329
330	if ((seat->mouse_fd =
331	         launch_open_device("/dev/wsmouse", O_RDWR | O_NONBLOCK)) == -1) {
332		ERROR("Could not open mouse device\n");
333		goto error0;
334	}
335	if ((seat->kbd_fd =
336	         launch_open_device("/dev/wskbd", O_RDWR | O_NONBLOCK)) == -1) {
337		ERROR("Could not open keyboard device\n");
338		goto error1;
339	}
340
341#ifdef WSMOUSEIO_SETVERSION
342	(void)ioctl(seat->mouse_fd, WSMOUSEIO_SETVERSION, &mouse_ver);
343#endif
344#ifdef WSKBDIO_SETVERSION
345	(void)ioctl(seat->kbd_fd, WSKBDIO_SETVERSION, &kbd_ver);
346#endif
347
348	/* set devices to nativemode to receive events */
349#ifdef WSMOUSEIO_SETMODE
350	{
351		int mode = WSMOUSE_COMPAT; /* use compat mode; it sends events */
352		if (ioctl(seat->mouse_fd, WSMOUSEIO_SETMODE, &mode) == -1) {
353			fprintf(stderr, "wscons: WSMOUSEIO_SETMODE failed: %s\n",
354			        strerror(errno));
355		}
356	}
357#endif /* WSMOUSEIO_SETMODE */
358#ifdef WSKBDIO_SETMODE
359	{
360		int mode = WSKBD_TRANSLATED; /* use translated mode for key events */
361		if (ioctl(seat->kbd_fd, WSKBDIO_SETMODE, &mode) == -1) {
362			fprintf(stderr, "wscons: WSKBDIO_SETMODE failed: %s\n",
363			        strerror(errno));
364		}
365	}
366#endif /* WSKBDIO_SETMODE */
367
368	if (ioctl(seat->kbd_fd, WSKBDIO_GTYPE, &seat->kbd_type) == -1) {
369		ERROR("Could not get keyboard type\n");
370		goto error2;
371	}
372	/* TODO handle mixed keyboard types with wsmux?
373	   see: https://www.mail-archive.com/ports%40openbsd.org/msg134646.html */
374
375	if (ioctl(seat->kbd_fd, WSKBDIO_GETENCODING, &encoding) != -1) {
376		encoding_layout = KB_ENCODING(encoding);
377		for (i = 0; ws_xkb_encodings[i].xkb != NULL; ++i) {
378			if (ws_xkb_encodings[i].ws == encoding_layout) {
379				seat->names.layout = ws_xkb_encodings[i].xkb;
380				break;
381			}
382		}
383		switch (KB_VARIANT(encoding)) {
384		case KB_NODEAD:
385			seat->names.variant = "nodeadkeys";
386			break;
387		case KB_SWAPCTRLCAPS:
388			seat->names.options = "ctrl:swapcaps";
389			break;
390		case KB_DVORAK:
391			seat->names.variant = "dvorak";
392			break;
393		case KB_COLEMAK:
394			seat->names.variant = "colemak";
395			break;
396		}
397	}
398
399	return true;
400error2:
401	close(seat->kbd_fd);
402error1:
403	close(seat->mouse_fd);
404error0:
405	return false;
406}
407
408struct swc_seat *
409seat_create(struct wl_display *display, const char *seat_name)
410{
411	struct seat *seat;
412
413	seat = malloc(sizeof(*seat));
414	if (!seat) {
415		goto error0;
416	}
417
418	seat->ignore = false;
419	memset(&seat->names, 0, sizeof(seat->names));
420	seat->names.rules = "base";
421	seat->names.model = "pc105";
422	seat->names.layout = "us";
423	seat->names.variant = "basic";
424
425	seat->name = strdup(seat_name);
426	if (!seat->name) {
427		ERROR("Could not allocate seat name string\n");
428		goto error1;
429	}
430
431	if (!initialize_wscons(seat)) {
432		goto error2;
433	}
434
435	/* TODO other way */
436	{
437		const char *s;
438		if ((s = getenv("XKB_DEFAULT_RULES")) && *s)
439			seat->names.rules = s;
440		if ((s = getenv("XKB_DEFAULT_MODEL")) && *s)
441			seat->names.model = s;
442		if ((s = getenv("XKB_DEFAULT_LAYOUT")) && *s)
443			seat->names.layout = s;
444		if ((s = getenv("XKB_DEFAULT_VARIANT")) && *s)
445			seat->names.variant = s;
446		if ((s = getenv("XKB_DEFAULT_OPTIONS")) && *s)
447			seat->names.options = s;
448	}
449
450	seat->global =
451	    wl_global_create(display, &wl_seat_interface, 5, seat, &bind_seat);
452	if (!seat->global) {
453		goto error2;
454	}
455	seat->capabilities =
456	    WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER;
457	wl_list_init(&seat->resources);
458
459	seat->swc_listener.notify = &handle_swc_event;
460	wl_signal_add(&swc.event_signal, &seat->swc_listener);
461
462	seat->base.data_device = data_device_create();
463	if (!seat->base.data_device) {
464		ERROR("Could not initialize data device\n");
465		goto error3;
466	}
467	seat->data_device_listener.notify = &handle_data_device_event;
468	wl_signal_add(&seat->base.data_device->event_signal,
469	              &seat->data_device_listener);
470
471	seat->base.keyboard = keyboard_create(&seat->names);
472	if (!seat->base.keyboard) {
473		ERROR("Could not initialize keyboard\n");
474		goto error4;
475	}
476	seat->keyboard_focus_listener.notify = handle_keyboard_focus_event;
477	wl_signal_add(&seat->base.keyboard->focus.event_signal,
478	              &seat->keyboard_focus_listener);
479
480	if (!pointer_initialize(&seat->pointer)) {
481		ERROR("Could not initialize pointer\n");
482		goto error5;
483	}
484	seat->base.pointer = &seat->pointer;
485
486	seat->kbd_source = wl_event_loop_add_fd(
487	    swc.event_loop, seat->kbd_fd, WL_EVENT_READABLE, &handle_ws_data, seat);
488	seat->mouse_source =
489	    wl_event_loop_add_fd(swc.event_loop, seat->mouse_fd, WL_EVENT_READABLE,
490	                         &handle_ws_data, seat);
491
492	return &seat->base;
493
494error5:
495	keyboard_destroy(seat->base.keyboard);
496error4:
497	data_device_destroy(seat->base.data_device);
498error3:
499	wl_global_destroy(seat->global);
500error2:
501	free(seat->name);
502error1:
503	free(seat);
504error0:
505	return NULL;
506}
507
508void
509seat_destroy(struct swc_seat *seat_base)
510{
511	struct seat *seat = wl_container_of(seat_base, seat, base);
512
513	wl_event_source_remove(seat->mouse_source);
514	wl_event_source_remove(seat->kbd_source);
515	close(seat->mouse_fd);
516	seat->mouse_fd = -1;
517	close(seat->kbd_fd);
518	seat->kbd_fd = -1;
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}