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}