main 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};
181
182static void
183bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
184{
185	struct seat *seat = data;
186	struct wl_resource *resource;
187
188	if (version > 4) {
189		version = 4;
190	}
191
192	resource = wl_resource_create(client, &wl_seat_interface, version, id);
193	wl_resource_set_implementation(resource, &seat_impl, seat,
194	                               &remove_resource);
195	wl_list_insert(&seat->resources, wl_resource_get_link(resource));
196
197	if (version >= 2) {
198		wl_seat_send_name(resource, seat->name);
199	}
200
201	wl_seat_send_capabilities(resource, seat->capabilities);
202}
203
204static int
205ws_to_xkb(unsigned type, int key)
206{
207	switch (type) {
208	case WSKBD_TYPE_PC_XT:
209	case WSKBD_TYPE_PC_AT:
210		return wsXtMap[key];
211	case WSKBD_TYPE_USB:
212#ifdef WSKBD_TYPE_MAPLE
213	case WSKBD_TYPE_MAPLE:
214		return wsUsbMap[key];
215#endif
216		return wsUsbMap[key];
217	default:
218		fprintf(stderr, "Unknown wskbd type %d\n", type);
219		return key;
220	}
221}
222
223static int
224wsmouse_to_evdev(int button)
225{
226	/* The right and middle mouse buttons must be swapped. */
227	switch (button) {
228	case 1: /* Middle */
229		return 0x112;
230	case 2: /* Right */
231		return 0x111;
232	default:
233		return button + 0x110;
234	}
235}
236
237static int
238handle_ws_data(int fd, uint32_t mask, void *data)
239{
240	struct seat *seat = data;
241	struct wscons_event ev;
242
243	while (!seat->ignore && (read(fd, &ev, sizeof(ev))) != -1) {
244		uint32_t state, time;
245		int key;
246		wl_fixed_t pos;
247
248		time = ev.time.tv_sec + (ev.time.tv_nsec / 1000000L);
249		switch (ev.type) {
250		case WSCONS_EVENT_KEY_UP:
251			state = WL_KEYBOARD_KEY_STATE_RELEASED;
252			key = ws_to_xkb(seat->kbd_type, ev.value);
253			keyboard_handle_key(seat->base.keyboard, time, key, state);
254			break;
255		case WSCONS_EVENT_KEY_DOWN:
256			state = WL_KEYBOARD_KEY_STATE_PRESSED;
257			key = ws_to_xkb(seat->kbd_type, ev.value);
258			keyboard_handle_key(seat->base.keyboard, time, key, state);
259			break;
260		case WSCONS_EVENT_ALL_KEYS_UP:
261			break;
262		case WSCONS_EVENT_MOUSE_UP:
263			state = WL_POINTER_BUTTON_STATE_RELEASED;
264			key = wsmouse_to_evdev(ev.value);
265			pointer_handle_button(seat->base.pointer, time, key, state);
266			break;
267		case WSCONS_EVENT_MOUSE_DOWN:
268			state = WL_POINTER_BUTTON_STATE_PRESSED;
269			key = wsmouse_to_evdev(ev.value);
270			pointer_handle_button(seat->base.pointer, time, key, state);
271			break;
272		case WSCONS_EVENT_MOUSE_DELTA_X:
273			pos = wl_fixed_from_int(ev.value);
274			pointer_handle_relative_motion(seat->base.pointer, time, pos, 0);
275			break;
276		case WSCONS_EVENT_MOUSE_DELTA_Y:
277			pos = wl_fixed_from_int(-ev.value);
278			pointer_handle_relative_motion(seat->base.pointer, time, 0, pos);
279			break;
280		case WSCONS_EVENT_MOUSE_DELTA_Z:
281			pos = wl_fixed_from_int(ev.value * 10);
282			pointer_handle_axis(seat->base.pointer, time,
283				WL_POINTER_AXIS_VERTICAL_SCROLL,
284				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
285			break;
286		case WSCONS_EVENT_MOUSE_DELTA_W:
287			pos = wl_fixed_from_int(ev.value * 10);
288			pointer_handle_axis(seat->base.pointer, time,
289				WL_POINTER_AXIS_HORIZONTAL_SCROLL,
290				WL_POINTER_AXIS_SOURCE_WHEEL, pos, pos * 12);
291			break;
292		case WSCONS_EVENT_MOUSE_ABSOLUTE_X:
293			pos = wl_fixed_from_int(ev.value);
294			pointer_handle_absolute_motion(seat->base.pointer, time, pos, 0);
295			break;
296		case WSCONS_EVENT_MOUSE_ABSOLUTE_Y:
297			pos = wl_fixed_from_int(-ev.value);
298			pointer_handle_absolute_motion(seat->base.pointer, time, 0, pos);
299			break;
300		}
301	}
302	return 0;
303}
304
305static bool
306initialize_wscons(struct seat *seat)
307{
308#ifdef WSMOUSE_EVENT_VERSION
309	int mouse_ver = WSMOUSE_EVENT_VERSION;
310#endif
311#ifdef WSKBDIO_EVENT_VERSION
312	int kbd_ver = WSKBDIO_EVENT_VERSION;
313#endif
314	int encoding_layout;
315	kbd_t encoding;
316	unsigned i;
317
318	if ((seat->mouse_fd =
319	         launch_open_device("/dev/wsmouse", O_RDWR | O_NONBLOCK)) == -1) {
320		ERROR("Could not open mouse device\n");
321		goto error0;
322	}
323	if ((seat->kbd_fd =
324	         launch_open_device("/dev/wskbd", O_RDWR | O_NONBLOCK)) == -1) {
325		ERROR("Could not open keyboard device\n");
326		goto error1;
327	}
328
329#ifdef WSMOUSEIO_SETVERSION
330	(void)ioctl(seat->mouse_fd, WSMOUSEIO_SETVERSION, &mouse_ver);
331#endif
332#ifdef WSKBDIO_SETVERSION
333	(void)ioctl(seat->kbd_fd, WSKBDIO_SETVERSION, &kbd_ver);
334#endif
335
336	/* set devices to nativemode to receive events */
337#ifdef WSMOUSEIO_SETMODE
338	{
339		int mode = WSMOUSE_COMPAT; /* use compat mode; it sends events */
340		if (ioctl(seat->mouse_fd, WSMOUSEIO_SETMODE, &mode) == -1) {
341			fprintf(stderr, "wscons: WSMOUSEIO_SETMODE failed: %s\n",
342			        strerror(errno));
343		}
344	}
345#endif /* WSMOUSEIO_SETMODE */
346#ifdef WSKBDIO_SETMODE
347	{
348		int mode = WSKBD_TRANSLATED; /* use translated mode for key events */
349		if (ioctl(seat->kbd_fd, WSKBDIO_SETMODE, &mode) == -1) {
350			fprintf(stderr, "wscons: WSKBDIO_SETMODE failed: %s\n",
351			        strerror(errno));
352		}
353	}
354#endif /* WSKBDIO_SETMODE */
355
356	if (ioctl(seat->kbd_fd, WSKBDIO_GTYPE, &seat->kbd_type) == -1) {
357		ERROR("Could not get keyboard type\n");
358		goto error2;
359	}
360
361	if (ioctl(seat->kbd_fd, WSKBDIO_GETENCODING, &encoding) != -1) {
362		encoding_layout = KB_ENCODING(encoding);
363		for (i = 0; ws_xkb_encodings[i].xkb != NULL; ++i) {
364			if (ws_xkb_encodings[i].ws == encoding_layout) {
365				seat->names.layout = ws_xkb_encodings[i].xkb;
366				break;
367			}
368		}
369		switch (KB_VARIANT(encoding)) {
370		case KB_NODEAD:
371			seat->names.variant = "nodeadkeys";
372			break;
373		case KB_SWAPCTRLCAPS:
374			seat->names.options = "ctrl:swapcaps";
375			break;
376		case KB_DVORAK:
377			seat->names.variant = "dvorak";
378			break;
379		case KB_COLEMAK:
380			seat->names.variant = "colemak";
381			break;
382		}
383	}
384
385	return true;
386error2:
387	close(seat->kbd_fd);
388error1:
389	close(seat->mouse_fd);
390error0:
391	return false;
392}
393
394struct swc_seat *
395seat_create(struct wl_display *display, const char *seat_name)
396{
397	struct seat *seat;
398
399	seat = malloc(sizeof(*seat));
400	if (!seat) {
401		goto error0;
402	}
403
404	seat->ignore = false;
405	memset(&seat->names, 0, sizeof(seat->names));
406	seat->names.rules = "base";
407	seat->names.model = "pc105";
408	seat->names.layout = "us";
409	seat->names.variant = "basic";
410
411	seat->name = strdup(seat_name);
412	if (!seat->name) {
413		ERROR("Could not allocate seat name string\n");
414		goto error1;
415	}
416
417	if (!initialize_wscons(seat)) {
418		goto error2;
419	}
420
421	seat->global =
422	    wl_global_create(display, &wl_seat_interface, 4, seat, &bind_seat);
423	if (!seat->global) {
424		goto error2;
425	}
426	seat->capabilities =
427	    WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER;
428	wl_list_init(&seat->resources);
429
430	seat->swc_listener.notify = &handle_swc_event;
431	wl_signal_add(&swc.event_signal, &seat->swc_listener);
432
433	seat->base.data_device = data_device_create();
434	if (!seat->base.data_device) {
435		ERROR("Could not initialize data device\n");
436		goto error3;
437	}
438	seat->data_device_listener.notify = &handle_data_device_event;
439	wl_signal_add(&seat->base.data_device->event_signal,
440	              &seat->data_device_listener);
441
442	seat->base.keyboard = keyboard_create(&seat->names);
443	if (!seat->base.keyboard) {
444		ERROR("Could not initialize keyboard\n");
445		goto error4;
446	}
447	seat->keyboard_focus_listener.notify = handle_keyboard_focus_event;
448	wl_signal_add(&seat->base.keyboard->focus.event_signal,
449	              &seat->keyboard_focus_listener);
450
451	if (!pointer_initialize(&seat->pointer)) {
452		ERROR("Could not initialize pointer\n");
453		goto error5;
454	}
455	seat->base.pointer = &seat->pointer;
456
457	seat->kbd_source = wl_event_loop_add_fd(
458	    swc.event_loop, seat->kbd_fd, WL_EVENT_READABLE, &handle_ws_data, seat);
459	seat->mouse_source =
460	    wl_event_loop_add_fd(swc.event_loop, seat->mouse_fd, WL_EVENT_READABLE,
461	                         &handle_ws_data, seat);
462
463	return &seat->base;
464
465error5:
466	keyboard_destroy(seat->base.keyboard);
467error4:
468	data_device_destroy(seat->base.data_device);
469error3:
470	wl_global_destroy(seat->global);
471error2:
472	free(seat->name);
473error1:
474	free(seat);
475error0:
476	return NULL;
477}
478
479void
480seat_destroy(struct swc_seat *seat_base)
481{
482	struct seat *seat = wl_container_of(seat_base, seat, base);
483
484	wl_event_source_remove(seat->mouse_source);
485	wl_event_source_remove(seat->kbd_source);
486	close(seat->mouse_fd);
487	seat->mouse_fd = -1;
488	close(seat->kbd_fd);
489	seat->kbd_fd = -1;
490
491	pointer_finalize(&seat->pointer);
492	keyboard_destroy(seat->base.keyboard);
493	data_device_destroy(seat->base.data_device);
494
495	wl_global_destroy(seat->global);
496	free(seat->name);
497	free(seat);
498}