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}