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
 51/* Map wscons encodings to libxkbcommon layout names. */
 52struct ws_xkb_map {
 53	const int ws;
 54	const char *const xkb;
 55};
 56
 57static const struct ws_xkb_map ws_xkb_encodings[] = {
 58    {KB_UK, "gb"}, {KB_BE, "be"},
 59#ifdef KB_CZ
 60    {KB_CZ, "cz"},
 61#endif
 62    {KB_DK, "dk"}, {KB_NL, "nl"}, {KB_DE, "de"},
 63#ifdef KB_GR
 64    {KB_GR, "gr"},
 65#endif
 66    {KB_HU, "hu"}, {KB_IT, "it"}, {KB_JP, "jp"}, {KB_NO, "no"}, {KB_PL, "pl"},
 67    {KB_PT, "pt"}, {KB_RU, "ru"}, {KB_ES, "es"}, {KB_SV, "sv"}, {KB_SG, "sg"},
 68    {KB_TR, "tr"}, {KB_UA, "ua"}, {-1, NULL}};
 69
 70struct seat {
 71	struct swc_seat base;
 72
 73	char *name;
 74	uint32_t capabilities;
 75
 76	int mouse_fd;
 77	int kbd_fd;
 78	bool ignore;
 79
 80	unsigned kbd_type;
 81
 82	struct xkb_rule_names names;
 83
 84	struct wl_event_source *mouse_source;
 85	struct wl_event_source *kbd_source;
 86
 87	struct wl_listener swc_listener;
 88
 89	struct wl_listener keyboard_focus_listener;
 90	struct pointer pointer;
 91	struct wl_listener data_device_listener;
 92
 93	struct wl_global *global;
 94	struct wl_list resources;
 95};
 96
 97static void
 98handle_keyboard_focus_event(struct wl_listener *listener, void *data)
 99{
100	struct seat *seat =
101	    wl_container_of(listener, seat, keyboard_focus_listener);
102	struct event *ev = data;
103	struct input_focus_event_data *event_data = ev->data;
104
105	if (ev->type != INPUT_FOCUS_EVENT_CHANGED) {
106		return;
107	}
108
109	if (event_data->new) {
110		struct wl_client *client =
111		    wl_resource_get_client(event_data->new->surface->resource);
112
113		/* Offer the selection to the new focus. */
114		data_device_offer_selection(seat->base.data_device, client);
115	}
116}
117
118static void
119handle_data_device_event(struct wl_listener *listener, void *data)
120{
121	struct seat *seat = wl_container_of(listener, seat, data_device_listener);
122	struct event *ev = data;
123
124	if (ev->type != DATA_DEVICE_EVENT_SELECTION_CHANGED) {
125		return;
126	}
127
128	if (seat->base.keyboard->focus.client) {
129		data_device_offer_selection(seat->base.data_device,
130		                            seat->base.keyboard->focus.client);
131	}
132}
133
134static void
135handle_swc_event(struct wl_listener *listener, void *data)
136{
137	struct seat *seat = wl_container_of(listener, seat, swc_listener);
138	struct event *ev = data;
139
140	switch (ev->type) {
141	case SWC_EVENT_DEACTIVATED:
142		seat->ignore = true;
143		keyboard_reset(seat->base.keyboard);
144		break;
145	case SWC_EVENT_ACTIVATED:
146		seat->ignore = false;
147		break;
148	}
149}
150
151/* Wayland Seat Interface */
152static void
153get_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t id)
154{
155	struct seat *seat = wl_resource_get_user_data(resource);
156
157	pointer_bind(&seat->pointer, client, wl_resource_get_version(resource), id);
158}
159
160static void
161get_keyboard(struct wl_client *client, struct wl_resource *resource,
162             uint32_t id)
163{
164	struct seat *seat = wl_resource_get_user_data(resource);
165
166	keyboard_bind(seat->base.keyboard, client,
167	              wl_resource_get_version(resource), id);
168}
169
170static void
171get_touch(struct wl_client *client, struct wl_resource *resource, uint32_t id)
172{
173	/* XXX: Implement */
174}
175
176static struct wl_seat_interface seat_impl = {
177    .get_pointer = get_pointer,
178    .get_keyboard = get_keyboard,
179    .get_touch = get_touch,
180    .release = destroy_resource,
181};
182
183static void
184bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
185{
186	struct seat *seat = data;
187	struct wl_resource *resource;
188
189	if (version > 5) {
190		version = 5;
191	}
192
193	resource = wl_resource_create(client, &wl_seat_interface, version, id);
194	wl_resource_set_implementation(resource, &seat_impl, seat,
195	                               &remove_resource);
196	wl_list_insert(&seat->resources, wl_resource_get_link(resource));
197
198	if (version >= 2) {
199		wl_seat_send_name(resource, seat->name);
200	}
201
202	wl_seat_send_capabilities(resource, seat->capabilities);
203}
204
205static int
206ws_to_xkb(unsigned type, int key)
207{
208	switch (type) {
209	case WSKBD_TYPE_PC_XT:
210	case WSKBD_TYPE_PC_AT:
211		return wsXtMap[key];
212	case WSKBD_TYPE_USB:
213#ifdef WSKBD_TYPE_MAPLE
214	case WSKBD_TYPE_MAPLE:
215		return wsUsbMap[key];
216#endif
217		return wsUsbMap[key];
218	default:
219		fprintf(stderr, "Unknown wskbd type %d\n", type);
220		return key;
221	}
222}
223
224static int
225wsmouse_to_evdev(int button)
226{
227	/* The right and middle mouse buttons must be swapped. */
228	switch (button) {
229	case 1: /* Middle */
230		return 0x112;
231	case 2: /* Right */
232		return 0x111;
233	default:
234		return button + 0x110;
235	}
236}
237
238static int
239handle_ws_data(int fd, uint32_t mask, void *data)
240{
241	struct seat *seat = data;
242	struct wscons_event ev;
243
244	while (!seat->ignore && (read(fd, &ev, sizeof(ev))) != -1) {
245		uint32_t state, time;
246		int key;
247		wl_fixed_t pos;
248
249		time = ev.time.tv_sec + (ev.time.tv_nsec / 1000000L);
250		switch (ev.type) {
251		case WSCONS_EVENT_KEY_UP:
252			state = WL_KEYBOARD_KEY_STATE_RELEASED;
253			key = ws_to_xkb(seat->kbd_type, ev.value);
254			keyboard_handle_key(seat->base.keyboard, time, key, state);
255			break;
256		case WSCONS_EVENT_KEY_DOWN:
257			state = WL_KEYBOARD_KEY_STATE_PRESSED;
258			key = ws_to_xkb(seat->kbd_type, ev.value);
259			keyboard_handle_key(seat->base.keyboard, time, key, state);
260			break;
261		case WSCONS_EVENT_ALL_KEYS_UP:
262			break;
263		case WSCONS_EVENT_MOUSE_UP:
264			state = WL_POINTER_BUTTON_STATE_RELEASED;
265			key = wsmouse_to_evdev(ev.value);
266			pointer_handle_button(seat->base.pointer, time, key, state);
267			break;
268		case WSCONS_EVENT_MOUSE_DOWN:
269			state = WL_POINTER_BUTTON_STATE_PRESSED;
270			key = wsmouse_to_evdev(ev.value);
271			pointer_handle_button(seat->base.pointer, time, key, state);
272			break;
273		case WSCONS_EVENT_MOUSE_DELTA_X:
274			pos = wl_fixed_from_int(ev.value);
275			pointer_handle_relative_motion(seat->base.pointer, time, pos, 0);
276			break;
277		case WSCONS_EVENT_MOUSE_DELTA_Y:
278			pos = wl_fixed_from_int(-ev.value);
279			pointer_handle_relative_motion(seat->base.pointer, time, 0, pos);
280			break;
281		case WSCONS_EVENT_MOUSE_DELTA_Z:
282			pos = wl_fixed_from_int(ev.value * 10);
283			pointer_handle_axis(seat->base.pointer, time,
284				WL_POINTER_AXIS_VERTICAL_SCROLL,
285				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
286			break;
287		case WSCONS_EVENT_MOUSE_DELTA_W:
288			pos = wl_fixed_from_int(ev.value * 10);
289			pointer_handle_axis(seat->base.pointer, time,
290				WL_POINTER_AXIS_HORIZONTAL_SCROLL,
291				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
292			break;
293		case WSCONS_EVENT_MOUSE_ABSOLUTE_X:
294			pos = wl_fixed_from_int(ev.value);
295			pointer_handle_absolute_motion(seat->base.pointer, time, pos, 0);
296			break;
297		case WSCONS_EVENT_MOUSE_ABSOLUTE_Y:
298			pos = wl_fixed_from_int(-ev.value);
299			pointer_handle_absolute_motion(seat->base.pointer, time, 0, pos);
300			break;
301		}
302	}
303	return 0;
304}
305
306static bool
307initialize_wscons(struct seat *seat)
308{
309#ifdef WSMOUSE_EVENT_VERSION
310	int mouse_ver = WSMOUSE_EVENT_VERSION;
311#endif
312#ifdef WSKBDIO_EVENT_VERSION
313	int kbd_ver = WSKBDIO_EVENT_VERSION;
314#endif
315	int encoding_layout;
316	kbd_t encoding;
317	unsigned i;
318
319	if ((seat->mouse_fd =
320	         launch_open_device("/dev/wsmouse", O_RDWR | O_NONBLOCK)) == -1) {
321		ERROR("Could not open mouse device\n");
322		goto error0;
323	}
324	if ((seat->kbd_fd =
325	         launch_open_device("/dev/wskbd", O_RDWR | O_NONBLOCK)) == -1) {
326		ERROR("Could not open keyboard device\n");
327		goto error1;
328	}
329
330#ifdef WSMOUSEIO_SETVERSION
331	(void)ioctl(seat->mouse_fd, WSMOUSEIO_SETVERSION, &mouse_ver);
332#endif
333#ifdef WSKBDIO_SETVERSION
334	(void)ioctl(seat->kbd_fd, WSKBDIO_SETVERSION, &kbd_ver);
335#endif
336
337	/* set devices to nativemode to receive events */
338#ifdef WSMOUSEIO_SETMODE
339	{
340		int mode = WSMOUSE_COMPAT; /* use compat mode; it sends events */
341		if (ioctl(seat->mouse_fd, WSMOUSEIO_SETMODE, &mode) == -1) {
342			fprintf(stderr, "wscons: WSMOUSEIO_SETMODE failed: %s\n",
343			        strerror(errno));
344		}
345	}
346#endif /* WSMOUSEIO_SETMODE */
347#ifdef WSKBDIO_SETMODE
348	{
349		int mode = WSKBD_TRANSLATED; /* use translated mode for key events */
350		if (ioctl(seat->kbd_fd, WSKBDIO_SETMODE, &mode) == -1) {
351			fprintf(stderr, "wscons: WSKBDIO_SETMODE failed: %s\n",
352			        strerror(errno));
353		}
354	}
355#endif /* WSKBDIO_SETMODE */
356
357	if (ioctl(seat->kbd_fd, WSKBDIO_GTYPE, &seat->kbd_type) == -1) {
358		ERROR("Could not get keyboard type\n");
359		goto error2;
360	}
361
362	if (ioctl(seat->kbd_fd, WSKBDIO_GETENCODING, &encoding) != -1) {
363		encoding_layout = KB_ENCODING(encoding);
364		for (i = 0; ws_xkb_encodings[i].xkb != NULL; ++i) {
365			if (ws_xkb_encodings[i].ws == encoding_layout) {
366				seat->names.layout = ws_xkb_encodings[i].xkb;
367				break;
368			}
369		}
370		switch (KB_VARIANT(encoding)) {
371		case KB_NODEAD:
372			seat->names.variant = "nodeadkeys";
373			break;
374		case KB_SWAPCTRLCAPS:
375			seat->names.options = "ctrl:swapcaps";
376			break;
377		case KB_DVORAK:
378			seat->names.variant = "dvorak";
379			break;
380		case KB_COLEMAK:
381			seat->names.variant = "colemak";
382			break;
383		}
384	}
385
386	return true;
387error2:
388	close(seat->kbd_fd);
389error1:
390	close(seat->mouse_fd);
391error0:
392	return false;
393}
394
395struct swc_seat *
396seat_create(struct wl_display *display, const char *seat_name)
397{
398	struct seat *seat;
399
400	seat = malloc(sizeof(*seat));
401	if (!seat) {
402		goto error0;
403	}
404
405	seat->ignore = false;
406	memset(&seat->names, 0, sizeof(seat->names));
407	seat->names.rules = "base";
408	seat->names.model = "pc105";
409	seat->names.layout = "us";
410	seat->names.variant = "basic";
411
412	seat->name = strdup(seat_name);
413	if (!seat->name) {
414		ERROR("Could not allocate seat name string\n");
415		goto error1;
416	}
417
418	if (!initialize_wscons(seat)) {
419		goto error2;
420	}
421
422	seat->global =
423	    wl_global_create(display, &wl_seat_interface, 5, seat, &bind_seat);
424	if (!seat->global) {
425		goto error2;
426	}
427	seat->capabilities =
428	    WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER;
429	wl_list_init(&seat->resources);
430
431	seat->swc_listener.notify = &handle_swc_event;
432	wl_signal_add(&swc.event_signal, &seat->swc_listener);
433
434	seat->base.data_device = data_device_create();
435	if (!seat->base.data_device) {
436		ERROR("Could not initialize data device\n");
437		goto error3;
438	}
439	seat->data_device_listener.notify = &handle_data_device_event;
440	wl_signal_add(&seat->base.data_device->event_signal,
441	              &seat->data_device_listener);
442
443	seat->base.keyboard = keyboard_create(&seat->names);
444	if (!seat->base.keyboard) {
445		ERROR("Could not initialize keyboard\n");
446		goto error4;
447	}
448	seat->keyboard_focus_listener.notify = handle_keyboard_focus_event;
449	wl_signal_add(&seat->base.keyboard->focus.event_signal,
450	              &seat->keyboard_focus_listener);
451
452	if (!pointer_initialize(&seat->pointer)) {
453		ERROR("Could not initialize pointer\n");
454		goto error5;
455	}
456	seat->base.pointer = &seat->pointer;
457
458	seat->kbd_source = wl_event_loop_add_fd(
459	    swc.event_loop, seat->kbd_fd, WL_EVENT_READABLE, &handle_ws_data, seat);
460	seat->mouse_source =
461	    wl_event_loop_add_fd(swc.event_loop, seat->mouse_fd, WL_EVENT_READABLE,
462	                         &handle_ws_data, seat);
463
464	return &seat->base;
465
466error5:
467	keyboard_destroy(seat->base.keyboard);
468error4:
469	data_device_destroy(seat->base.data_device);
470error3:
471	wl_global_destroy(seat->global);
472error2:
473	free(seat->name);
474error1:
475	free(seat);
476error0:
477	return NULL;
478}
479
480void
481seat_destroy(struct swc_seat *seat_base)
482{
483	struct seat *seat = wl_container_of(seat_base, seat, base);
484
485	wl_event_source_remove(seat->mouse_source);
486	wl_event_source_remove(seat->kbd_source);
487	close(seat->mouse_fd);
488	seat->mouse_fd = -1;
489	close(seat->kbd_fd);
490	seat->kbd_fd = -1;
491
492	pointer_finalize(&seat->pointer);
493	keyboard_destroy(seat->base.keyboard);
494	data_device_destroy(seat->base.data_device);
495
496	wl_global_destroy(seat->global);
497	free(seat->name);
498	free(seat);
499}