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}