commit 62b60b6
shrub
·
2026-02-03 23:55:27 +0000 UTC
parent deb6d25
Add an evdev backend
7 files changed,
+671,
-128
+0,
-122
1@@ -1,122 +0,0 @@
2-# swc: Makefile
3-
4-.PHONY: all
5-all: build
6-
7-# Defaults for config.mk
8-PREFIX ?= /usr/local
9-BINDIR ?= $(PREFIX)/bin
10-LIBDIR ?= $(PREFIX)/lib
11-INCLUDEDIR ?= $(PREFIX)/include
12-DATADIR ?= $(PREFIX)/share
13-PKGCONFIGDIR ?= $(LIBDIR)/pkgconfig
14-
15-OBJCOPY ?= objcopy
16-PKG_CONFIG ?= pkg-config
17-WAYLAND_SCANNER ?= wayland-scanner
18-
19-VERSION_MAJOR := 0
20-VERSION_MINOR := 0
21-VERSION := $(VERSION_MAJOR).$(VERSION_MINOR)
22-
23-TARGETS := swc.pc
24-SUBDIRS := launch libswc protocol cursor example
25-CLEAN_FILES := $(TARGETS)
26-
27-include config.mk
28-
29-# Dependencies
30-PACKAGES := \
31- libdrm \
32- pixman-1 \
33- wayland-server \
34- wayland-protocols \
35- wld \
36- xkbcommon
37-
38-ifeq ($(ENABLE_XWAYLAND),1)
39-PACKAGES += \
40- xcb \
41- xcb-composite \
42- xcb-ewmh \
43- xcb-icccm
44-endif
45-
46-ifneq ($(shell uname),NetBSD)
47- PACKAGES += libinput
48- ifeq ($(ENABLE_LIBUDEV),1)
49- PACKAGES += libudev
50- endif
51-endif
52-
53-libinput_CONSTRAINTS := --atleast-version=0.4
54-wayland-server_CONSTRAINTS := --atleast-version=1.6.0
55-
56-define check
57- ifeq ($$(origin $(1)_EXISTS),undefined)
58- $(1)_EXISTS = $$(shell $$(PKG_CONFIG) --exists $$($(1)_CONSTRAINTS) $(1) && echo yes)
59- endif
60- ifneq ($$($(1)_EXISTS),yes)
61- $$(error Could not find package $(1) $$($(1)_CONSTRAINTS))
62- endif
63-endef
64-
65-$(foreach pkg,$(PACKAGES),$(eval $(call check,$(pkg))))
66-
67-FINAL_CFLAGS = $(CFLAGS) -fvisibility=hidden -std=c11
68-FINAL_CPPFLAGS = $(CPPFLAGS) -D_GNU_SOURCE # Required for mkostemp
69-
70-# Warning/error flags
71-FINAL_CFLAGS += -Werror=implicit-function-declaration -Werror=implicit-int \
72- -Werror=pointer-sign -Werror=pointer-arith \
73- -Wall -Wno-missing-braces
74-
75-ifeq ($(ENABLE_DEBUG),1)
76- FINAL_CPPFLAGS += -DENABLE_DEBUG=1
77- FINAL_CFLAGS += -g
78-endif
79-
80-ifeq ($(if $(V),$(V),0),0)
81- quiet = @echo ' $1 $@';
82-endif
83-
84-Q_AR = $(call quiet,AR )
85-Q_CC = $(call quiet,CC )
86-Q_CCLD = $(call quiet,CCLD )
87-Q_GEN = $(call quiet,GEN )
88-Q_OBJCOPY = $(call quiet,OBJCOPY)
89-Q_SYM = $(call quiet,SYM )
90-
91-compile = $(Q_CC)$(CC) $(FINAL_CPPFLAGS) $(FINAL_CFLAGS) -I . -c -o $@ $< \
92- -MMD -MP -MF .deps/$(basename $<).d -MT $(basename $@).o -MT $(basename $@).lo
93-link = $(Q_CCLD)$(CC) $(LDFLAGS) -o $@ $^
94-pkgconfig = $(foreach pkg,$(1),$(if $($(pkg)_$(3)),$($(pkg)_$(3)), \
95- $(shell $(PKG_CONFIG) --$(2) $(pkg))))
96-
97-include $(SUBDIRS:%=%/local.mk)
98-
99-$(foreach dir,BIN LIB INCLUDE PKGCONFIG,$(DESTDIR)$($(dir)DIR)) $(DESTDIR)$(DATADIR)/swc:
100- mkdir -p $@
101-
102-.PHONY: build
103-build: $(SUBDIRS:%=build-%) $(TARGETS)
104-
105-REQUIRES := wayland-server
106-REQUIRES_PRIVATE := $(filter-out $(REQUIRES),$(libswc_PACKAGES))
107-SWC_PC_VARS := VERSION PREFIX LIBDIR INCLUDEDIR DATADIR REQUIRES REQUIRES_PRIVATE
108-
109-swc.pc: swc.pc.in
110- $(Q_GEN)sed $(foreach var,$(SWC_PC_VARS),-e 's:@$(var)@:$($(var)):') $< >$@
111-
112-.PHONY: install-swc.pc
113-install-swc.pc: swc.pc | $(DESTDIR)$(PKGCONFIGDIR)
114- install -m 644 $< $(DESTDIR)$(PKGCONFIGDIR)
115-
116-.PHONY: install
117-install: $(SUBDIRS:%=install-%) $(TARGETS:%=install-%)
118-
119-.PHONY: clean
120-clean:
121- rm -f $(CLEAN_FILES)
122-
123--include .deps/*/*.d
M
Makefile
+22,
-3
1@@ -41,13 +41,21 @@ AR=ar
2
3 UNAME!= uname
4
5+.if !defined(INPUT_BACKEND)
6+. if ${UNAME} == "NetBSD"
7+INPUT_BACKEND=wscons
8+. else
9+INPUT_BACKEND=libinput
10+. endif
11+.endif
12+
13 VERSION_MAJOR=0
14 VERSION_MINOR=0
15 VERSION=${VERSION_MAJOR}.${VERSION_MINOR}
16
17 PACKAGES=libdrm pixman-1 wayland-server wayland-protocols wld xkbcommon
18
19-.if ${UNAME} != "NetBSD"
20+.if ${INPUT_BACKEND} == "libinput"
21 PACKAGES+= libinput
22 .if defined(ENABLE_LIBUDEV) && ${ENABLE_LIBUDEV} == 1
23 PACKAGES+= libudev
24@@ -78,6 +86,13 @@ CPPFLAGS+= -DENABLE_LIBUDEV
25 CPPFLAGS+= -DENABLE_XWAYLAND
26 .endif
27
28+.if defined(EVDEV_KBD_DEVICE)
29+CPPFLAGS+= -DEVDEV_KBD_DEVICE=\\\"${EVDEV_KBD_DEVICE}\\\"
30+.endif
31+.if defined(EVDEV_POINTER_DEVICE)
32+CPPFLAGS+= -DEVDEV_POINTER_DEVICE=\\\"${EVDEV_POINTER_DEVICE}\\\"
33+.endif
34+
35 PROTO_EXTENSIONS= \
36 protocol/server-decoration.xml \
37 protocol/swc.xml \
38@@ -200,10 +215,14 @@ SWC_SOURCES= \
39 protocol/xdg-decoration-unstable-v1-protocol.c \
40 protocol/xdg-shell-protocol.c
41
42-.if ${UNAME} == "NetBSD"
43+.if ${INPUT_BACKEND} == "wscons"
44 SWC_SOURCES+= libswc/seat-ws.c
45-.else
46+.elif ${INPUT_BACKEND} == "evdev"
47+SWC_SOURCES+= libswc/seat-evdev.c
48+.elif ${INPUT_BACKEND} == "libinput"
49 SWC_SOURCES+= libswc/seat.c
50+.else
51+.error Unknown INPUT_BACKEND '${INPUT_BACKEND}'. Use libinput, evdev, or wscons.
52 .endif
53
54 .if defined(ENABLE_XWAYLAND) && ${ENABLE_XWAYLAND} == 1
+2,
-0
1@@ -15,4 +15,6 @@ features
2 - fullscreen
3 - double window borders
4 - wallpapers
5+- screenshots
6+- evdev-only input backend
7 - probably more i forgot about
+5,
-1
1@@ -2,7 +2,7 @@
2
3 # The commented out options are defaults
4
5-# PREFIX = /usr/local
6+PREFIX = /usr
7 # BINDIR = $(PREFIX)/bin
8 # LIBDIR = $(PREFIX)/lib
9 # INCLUDEDIR = $(PREFIX)/include
10@@ -18,3 +18,7 @@ ENABLE_STATIC = 1
11 ENABLE_SHARED = 0
12 ENABLE_LIBUDEV = 1
13 ENABLE_XWAYLAND = 1
14+
15+#INPUT_BACKEND = libinput
16+# available: libinput, evdev, wscons
17+# default: libinput on linux, wscons on NetBSD
+20,
-2
1@@ -65,15 +65,33 @@ SWC_SOURCES = \
2 protocol/xdg-decoration-unstable-v1-protocol.c \
3 protocol/xdg-shell-protocol.c
4
5-ifeq ($(shell uname),NetBSD)
6+ifeq ($(strip $(INPUT_BACKEND)),)
7+ ifeq ($(shell uname),NetBSD)
8+ INPUT_BACKEND = wscons
9+ else
10+ INPUT_BACKEND = libinput
11+ endif
12+endif
13+
14+ifeq ($(INPUT_BACKEND),wscons)
15 SWC_SOURCES += libswc/seat-ws.c
16-else
17+else ifeq ($(INPUT_BACKEND),evdev)
18+ SWC_SOURCES += libswc/seat-evdev.c
19+ ifneq ($(EVDEV_KBD_DEVICE),)
20+ $(dir)_CFLAGS += -DEVDEV_KBD_DEVICE=\"$(EVDEV_KBD_DEVICE)\"
21+ endif
22+ ifneq ($(EVDEV_POINTER_DEVICE),)
23+ $(dir)_CFLAGS += -DEVDEV_POINTER_DEVICE=\"$(EVDEV_POINTER_DEVICE)\"
24+ endif
25+else ifeq ($(INPUT_BACKEND),libinput)
26 SWC_SOURCES += libswc/seat.c
27 $(dir)_PACKAGES += libinput
28 ifeq ($(ENABLE_LIBUDEV),1)
29 $(dir)_CFLAGS += -DENABLE_LIBUDEV
30 $(dir)_PACKAGES += libudev
31 endif
32+else
33+ $(error Unknown INPUT_BACKEND '$(INPUT_BACKEND)'. Use libinput, evdev, or wscons.)
34 endif
35
36 ifeq ($(ENABLE_XWAYLAND),1)
+621,
-0
1@@ -0,0 +1,621 @@
2+#include "seat.h"
3+#include "compositor.h"
4+#include "data_device.h"
5+#include "event.h"
6+#include "internal.h"
7+#include "keyboard.h"
8+#include "launch.h"
9+#include "pointer.h"
10+#include "screen.h"
11+#include "surface.h"
12+#include "util.h"
13+
14+#include <dirent.h>
15+#include <errno.h>
16+#include <fcntl.h>
17+#include <limits.h>
18+#include <stdbool.h>
19+#include <stdint.h>
20+#include <stdio.h>
21+#include <stdlib.h>
22+#include <string.h>
23+#include <unistd.h>
24+#include <ctype.h>
25+#include <sys/ioctl.h>
26+
27+#include <linux/input.h>
28+
29+#ifndef EVDEV_KBD_DEVICE
30+#define EVDEV_KBD_DEVICE "/dev/input/event0"
31+#endif
32+
33+#ifndef EVDEV_POINTER_DEVICE
34+#define EVDEV_POINTER_DEVICE "/dev/input/event1"
35+#endif
36+
37+struct seat {
38+ struct swc_seat base;
39+
40+ char *name;
41+ uint32_t capabilities;
42+
43+ int mouse_fd;
44+ int kbd_fd;
45+ bool ignore;
46+
47+ struct xkb_rule_names names;
48+
49+ struct wl_event_source *mouse_source;
50+ struct wl_event_source *kbd_source;
51+
52+ struct wl_listener swc_listener;
53+
54+ struct wl_listener keyboard_focus_listener;
55+ struct pointer pointer;
56+ struct wl_listener data_device_listener;
57+
58+ struct wl_global *global;
59+ struct wl_list resources;
60+
61+ wl_fixed_t abs_x;
62+ wl_fixed_t abs_y;
63+ bool abs_initialized;
64+
65+ bool shared_fd;
66+};
67+
68+static void
69+handle_keyboard_focus_event(struct wl_listener *listener, void *data)
70+{
71+ struct seat *seat = wl_container_of(listener, seat, keyboard_focus_listener);
72+ struct event *ev = data;
73+ struct input_focus_event_data *event_data = ev->data;
74+
75+ if (ev->type != INPUT_FOCUS_EVENT_CHANGED)
76+ return;
77+
78+ if (event_data->new) {
79+ struct wl_client *client = wl_resource_get_client(event_data->new->surface->resource);
80+
81+ /* offer the selection to the new focus */
82+ data_device_offer_selection(seat->base.data_device, client);
83+ }
84+}
85+
86+static void
87+handle_data_device_event(struct wl_listener *listener, void *data)
88+{
89+ struct seat *seat = wl_container_of(listener, seat, data_device_listener);
90+ struct event *ev = data;
91+
92+ if (ev->type != DATA_DEVICE_EVENT_SELECTION_CHANGED)
93+ return;
94+
95+ if (seat->base.keyboard->focus.client)
96+ data_device_offer_selection(seat->base.data_device, seat->base.keyboard->focus.client);
97+}
98+
99+static void
100+handle_swc_event(struct wl_listener *listener, void *data)
101+{
102+ struct seat *seat = wl_container_of(listener, seat, swc_listener);
103+ struct event *ev = data;
104+
105+ switch (ev->type) {
106+ case SWC_EVENT_DEACTIVATED:
107+ seat->ignore = true;
108+ keyboard_reset(seat->base.keyboard);
109+ break;
110+ case SWC_EVENT_ACTIVATED:
111+ seat->ignore = false;
112+ break;
113+ }
114+}
115+
116+/* da seat */
117+static void
118+get_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t id)
119+{
120+ struct seat *seat = wl_resource_get_user_data(resource);
121+
122+ pointer_bind(&seat->pointer, client, wl_resource_get_version(resource), id);
123+}
124+
125+static void
126+get_keyboard(struct wl_client *client, struct wl_resource *resource, uint32_t id)
127+{
128+ struct seat *seat = wl_resource_get_user_data(resource);
129+
130+ keyboard_bind(seat->base.keyboard, client, wl_resource_get_version(resource), id);
131+}
132+
133+static void
134+get_touch(struct wl_client *client, struct wl_resource *resource, uint32_t id)
135+{
136+}
137+
138+static struct wl_seat_interface seat_impl = {
139+ .get_pointer = get_pointer,
140+ .get_keyboard = get_keyboard,
141+ .get_touch = get_touch,
142+};
143+
144+static void
145+bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
146+{
147+ struct seat *seat = data;
148+ struct wl_resource *resource;
149+
150+ if (version > 4)
151+ version = 4;
152+
153+ resource = wl_resource_create(client, &wl_seat_interface, version, id);
154+ wl_resource_set_implementation(resource, &seat_impl, seat, &remove_resource);
155+ wl_list_insert(&seat->resources, wl_resource_get_link(resource));
156+
157+ if (version >= 2)
158+ wl_seat_send_name(resource, seat->name);
159+
160+ wl_seat_send_capabilities(resource, seat->capabilities);
161+}
162+
163+static uint32_t
164+event_time_ms(const struct input_event *ev)
165+{
166+ return (uint32_t)(ev->time.tv_sec * 1000 + ev->time.tv_usec / 1000);
167+}
168+
169+static void
170+handle_evdev_key(struct seat *seat, const struct input_event *ev)
171+{
172+ uint32_t state;
173+ uint32_t time = event_time_ms(ev);
174+
175+ if (ev->value == 2)
176+ return;
177+
178+ if (ev->code >= BTN_MISC)
179+ pointer_handle_button(seat->base.pointer, time, ev->code,
180+ ev->value ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED);
181+ else {
182+ if (ev->code > 255)
183+ return;
184+ state = (ev->value ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED);
185+ keyboard_handle_key(seat->base.keyboard, time, ev->code, state);
186+ }
187+}
188+
189+static void
190+handle_evdev_rel(struct seat *seat, const struct input_event *ev)
191+{
192+ uint32_t time = event_time_ms(ev);
193+ wl_fixed_t value;
194+
195+ switch (ev->code) {
196+ case REL_X:
197+ pointer_handle_relative_motion(seat->base.pointer, time, wl_fixed_from_int(ev->value), 0);
198+ break;
199+ case REL_Y:
200+ pointer_handle_relative_motion(seat->base.pointer, time, 0, wl_fixed_from_int(ev->value));
201+ break;
202+ case REL_WHEEL:
203+ value = wl_fixed_from_int(ev->value * 10);
204+ pointer_handle_axis(seat->base.pointer, time, WL_POINTER_AXIS_VERTICAL_SCROLL, WL_POINTER_AXIS_SOURCE_WHEEL, value, ev->value * 120);
205+ break;
206+ case REL_HWHEEL:
207+ value = wl_fixed_from_int(ev->value * 10);
208+ pointer_handle_axis(seat->base.pointer, time, WL_POINTER_AXIS_HORIZONTAL_SCROLL, WL_POINTER_AXIS_SOURCE_WHEEL, value, ev->value * 120);
209+ break;
210+ default:
211+ break;
212+ }
213+}
214+
215+static void
216+handle_evdev_abs(struct seat *seat, const struct input_event *ev)
217+{
218+ uint32_t time = event_time_ms(ev);
219+
220+ switch (ev->code) {
221+ case ABS_X:
222+ seat->abs_x = wl_fixed_from_int(ev->value);
223+ seat->abs_initialized = true;
224+ break;
225+ case ABS_Y:
226+ seat->abs_y = wl_fixed_from_int(ev->value);
227+ seat->abs_initialized = true;
228+ break;
229+ default:
230+ return;
231+ }
232+
233+ if (seat->abs_initialized)
234+ pointer_handle_absolute_motion(seat->base.pointer, time, seat->abs_x, seat->abs_y);
235+}
236+
237+static int
238+handle_evdev_data(int fd, uint32_t mask, void *data)
239+{
240+ struct seat *seat = data;
241+ struct input_event ev;
242+ ssize_t n;
243+
244+ while (!seat->ignore) {
245+ n = read(fd, &ev, sizeof(ev));
246+ if (n == -1) {
247+ if (errno == EAGAIN || errno == EINTR)
248+ break;
249+ return 0;
250+ }
251+ if (n != (ssize_t)sizeof(ev))
252+ break;
253+
254+ switch (ev.type) {
255+ case EV_KEY:
256+ handle_evdev_key(seat, &ev);
257+ break;
258+ case EV_REL:
259+ handle_evdev_rel(seat, &ev);
260+ break;
261+ case EV_ABS:
262+ handle_evdev_abs(seat, &ev);
263+ break;
264+ case EV_SYN:
265+ if (ev.code == SYN_REPORT)
266+ pointer_handle_frame(seat->base.pointer);
267+ break;
268+ default:
269+ break;
270+ }
271+ }
272+
273+ return 0;
274+}
275+
276+static bool
277+test_bit(const unsigned long *bits, size_t bit)
278+{
279+ return (bits[bit / (sizeof(unsigned long) * 8)] >> (bit % (sizeof(unsigned long) * 8))) & 1;
280+}
281+
282+static bool
283+contains_ci(const char *haystack, const char *needle)
284+{
285+ size_t nlen;
286+ const char *h;
287+
288+ if (!haystack || !needle || !*needle)
289+ return false;
290+
291+ nlen = strlen(needle);
292+ for (h = haystack; *h; ++h) {
293+ size_t i;
294+ for (i = 0; i < nlen; ++i) {
295+ unsigned char hc = (unsigned char)h[i];
296+ unsigned char nc = (unsigned char)needle[i];
297+ if (!h[i] || tolower(hc) != tolower(nc))
298+ break;
299+ }
300+ if (i == nlen)
301+ return true;
302+ }
303+ return false;
304+}
305+
306+static bool
307+is_keyboard_device(int fd)
308+{
309+ unsigned long ev_bits[(EV_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
310+ unsigned long key_bits[(KEY_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
311+
312+ memset(ev_bits, 0, sizeof(ev_bits));
313+ memset(key_bits, 0, sizeof(key_bits));
314+
315+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0)
316+ return false;
317+ if (!test_bit(ev_bits, EV_KEY))
318+ return false;
319+ if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0)
320+ return false;
321+
322+ return test_bit(key_bits, KEY_A) &&
323+ test_bit(key_bits, KEY_Z) &&
324+ test_bit(key_bits, KEY_ENTER) &&
325+ test_bit(key_bits, KEY_ESC) &&
326+ test_bit(key_bits, KEY_SPACE);
327+}
328+
329+static bool
330+is_pointer_device(int fd)
331+{
332+ unsigned long ev_bits[(EV_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
333+ unsigned long rel_bits[(REL_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
334+ unsigned long key_bits[(KEY_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
335+
336+ memset(ev_bits, 0, sizeof(ev_bits));
337+ memset(rel_bits, 0, sizeof(rel_bits));
338+ memset(key_bits, 0, sizeof(key_bits));
339+
340+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0)
341+ return false;
342+
343+ if (test_bit(ev_bits, EV_REL)) {
344+ if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits) < 0)
345+ return false;
346+ if (test_bit(rel_bits, REL_X) && test_bit(rel_bits, REL_Y))
347+ return true;
348+ }
349+
350+ if (test_bit(ev_bits, EV_KEY)) {
351+ if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0)
352+ return false;
353+ if (test_bit(key_bits, BTN_LEFT) && test_bit(key_bits, BTN_RIGHT))
354+ return true;
355+ }
356+
357+ return false;
358+}
359+
360+static bool
361+get_ev_bits(int fd, unsigned long *ev_bits, size_t ev_bits_len)
362+{
363+ memset(ev_bits, 0, ev_bits_len);
364+ return ioctl(fd, EVIOCGBIT(0, ev_bits_len), ev_bits) >= 0;
365+}
366+
367+static int
368+score_candidate(int fd, bool want_keyboard, const char *id_name)
369+{
370+ char name[256];
371+ unsigned long ev_bits[(EV_MAX + 8 * sizeof(unsigned long) - 1) / (8 * sizeof(unsigned long))];
372+ bool is_kbd;
373+ bool is_ptr;
374+ int score = 10;
375+
376+ is_kbd = is_keyboard_device(fd);
377+ is_ptr = is_pointer_device(fd);
378+
379+ if (want_keyboard && !is_kbd)
380+ return -1;
381+ if (!want_keyboard && !is_ptr)
382+ return -1;
383+
384+ if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
385+ name[0] = '\0';
386+
387+ if (!get_ev_bits(fd, ev_bits, sizeof(ev_bits)))
388+ memset(ev_bits, 0, sizeof(ev_bits));
389+
390+ if (want_keyboard) {
391+ if (is_ptr)
392+ score -= 6;
393+ if (contains_ci(id_name, "mouse") || contains_ci(name, "mouse"))
394+ score -= 12;
395+ if (contains_ci(id_name, "kbd") || contains_ci(id_name, "keyboard"))
396+ score += 4;
397+ if (contains_ci(name, "keyboard"))
398+ score += 2;
399+ if (test_bit(ev_bits, EV_LED))
400+ score += 3;
401+ if (test_bit(ev_bits, EV_REP))
402+ score += 1;
403+ } else {
404+ if (contains_ci(id_name, "mouse") || contains_ci(name, "mouse"))
405+ score += 4;
406+ if (contains_ci(id_name, "kbd") || contains_ci(id_name, "keyboard"))
407+ score -= 6;
408+ if (contains_ci(name, "keyboard"))
409+ score -= 4;
410+ }
411+
412+ return score;
413+}
414+
415+static bool
416+pick_best_device(const char *dir_path, const char *name_prefix, const char *name_substr,
417+ bool want_keyboard, char *out, size_t out_len)
418+{
419+ DIR *dir;
420+ struct dirent *ent;
421+ bool found = false;
422+ int best_score = -1;
423+ size_t prefix_len = name_prefix ? strlen(name_prefix) : 0;
424+
425+ dir = opendir(dir_path);
426+ if (!dir)
427+ return false;
428+
429+ while ((ent = readdir(dir)) != NULL) {
430+ char path[PATH_MAX];
431+ int fd;
432+ int score;
433+
434+ if (ent->d_name[0] == '.')
435+ continue;
436+ if (name_prefix && strncmp(ent->d_name, name_prefix, prefix_len) != 0)
437+ continue;
438+ if (name_substr && !strstr(ent->d_name, name_substr))
439+ continue;
440+
441+ snprintf(path, sizeof(path), "%s/%s", dir_path, ent->d_name);
442+ fd = launch_open_device(path, O_RDONLY | O_NONBLOCK);
443+ if (fd == -1)
444+ continue;
445+
446+ score = score_candidate(fd, want_keyboard, ent->d_name);
447+ if (score < 0) {
448+ close(fd);
449+ continue;
450+ }
451+
452+ if (score > best_score) {
453+ snprintf(out, out_len, "%s", path);
454+ best_score = score;
455+ found = true;
456+ }
457+
458+ close(fd);
459+ }
460+
461+ closedir(dir);
462+ return found;
463+}
464+
465+static bool
466+initialize_evdev(struct seat *seat)
467+{
468+ char kbd_path[PATH_MAX];
469+ char mouse_path[PATH_MAX];
470+ const char *kbd_dev = EVDEV_KBD_DEVICE;
471+ const char *mouse_dev = EVDEV_POINTER_DEVICE;
472+
473+ if (pick_best_device("/dev/input/by-id", NULL, "event-kbd", true, kbd_path, sizeof(kbd_path)))
474+ kbd_dev = kbd_path;
475+ else if (pick_best_device("/dev/input/by-path", NULL, "event-kbd", true, kbd_path, sizeof(kbd_path)))
476+ kbd_dev = kbd_path;
477+ else if (pick_best_device("/dev/input", "event", NULL, true, kbd_path, sizeof(kbd_path)))
478+ kbd_dev = kbd_path;
479+
480+ if (pick_best_device("/dev/input/by-id", NULL, "event-mouse", false, mouse_path, sizeof(mouse_path)))
481+ mouse_dev = mouse_path;
482+ else if (pick_best_device("/dev/input/by-path", NULL, "event-mouse", false, mouse_path, sizeof(mouse_path)))
483+ mouse_dev = mouse_path;
484+ else if (pick_best_device("/dev/input", "event", NULL, false, mouse_path, sizeof(mouse_path)))
485+ mouse_dev = mouse_path;
486+
487+ DEBUG("evdev devices: keyboard=%s pointer=%s\n", kbd_dev, mouse_dev);
488+
489+ seat->kbd_fd = launch_open_device(kbd_dev, O_RDONLY | O_NONBLOCK);
490+ if (seat->kbd_fd == -1) {
491+ ERROR("Could not open evdev keyboard device %s\n", kbd_dev);
492+ goto error0;
493+ }
494+
495+ if (strcmp(kbd_dev, mouse_dev) == 0) {
496+ seat->mouse_fd = seat->kbd_fd;
497+ seat->shared_fd = true;
498+ return true;
499+ }
500+
501+ seat->mouse_fd = launch_open_device(mouse_dev, O_RDONLY | O_NONBLOCK);
502+ if (seat->mouse_fd == -1) {
503+ ERROR("Could not open evdev pointer device %s\n", mouse_dev);
504+ goto error1;
505+ }
506+
507+ return true;
508+
509+error1:
510+ close(seat->kbd_fd);
511+error0:
512+ return false;
513+}
514+
515+struct swc_seat *
516+seat_create(struct wl_display *display, const char *seat_name)
517+{
518+ struct seat *seat;
519+
520+ seat = malloc(sizeof(*seat));
521+ if (!seat)
522+ goto error0;
523+
524+ memset(&seat->names, 0, sizeof(seat->names));
525+ seat->names.rules = "base";
526+ seat->names.model = "pc105";
527+ seat->names.layout = "us";
528+ seat->names.variant = "basic";
529+ seat->shared_fd = false;
530+
531+ seat->name = strdup(seat_name);
532+ if (!seat->name) {
533+ ERROR("Could not allocate seat name string\n");
534+ goto error1;
535+ }
536+
537+ if (!initialize_evdev(seat))
538+ goto error2;
539+
540+ seat->global = wl_global_create(display, &wl_seat_interface, 4, seat, &bind_seat);
541+ if (!seat->global)
542+ goto error2;
543+ seat->capabilities = WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER;
544+ wl_list_init(&seat->resources);
545+
546+ seat->swc_listener.notify = &handle_swc_event;
547+ wl_signal_add(&swc.event_signal, &seat->swc_listener);
548+
549+ seat->base.data_device = data_device_create();
550+ if (!seat->base.data_device) {
551+ ERROR("could not initialize data device\n");
552+ goto error3;
553+ }
554+ seat->data_device_listener.notify = &handle_data_device_event;
555+ wl_signal_add(&seat->base.data_device->event_signal, &seat->data_device_listener);
556+
557+ seat->base.keyboard = keyboard_create(&seat->names);
558+ if (!seat->base.keyboard) {
559+ ERROR("could not initialize keyboard\n");
560+ goto error4;
561+ }
562+ seat->keyboard_focus_listener.notify = handle_keyboard_focus_event;
563+ wl_signal_add(&seat->base.keyboard->focus.event_signal, &seat->keyboard_focus_listener);
564+
565+ if (!pointer_initialize(&seat->pointer)) {
566+ ERROR("Could not initialize pointer\n");
567+ goto error5;
568+ }
569+ seat->base.pointer = &seat->pointer;
570+
571+ seat->kbd_source = wl_event_loop_add_fd
572+ (swc.event_loop, seat->kbd_fd, WL_EVENT_READABLE,
573+ &handle_evdev_data, seat);
574+ if (!seat->shared_fd) {
575+ seat->mouse_source = wl_event_loop_add_fd
576+ (swc.event_loop, seat->mouse_fd, WL_EVENT_READABLE,
577+ &handle_evdev_data, seat);
578+ } else {
579+ seat->mouse_source = NULL;
580+ }
581+
582+ seat->abs_initialized = false;
583+
584+ return &seat->base;
585+
586+error5:
587+ keyboard_destroy(seat->base.keyboard);
588+error4:
589+ data_device_destroy(seat->base.data_device);
590+error3:
591+ wl_global_destroy(seat->global);
592+error2:
593+ free(seat->name);
594+error1:
595+ free(seat);
596+error0:
597+ return NULL;
598+}
599+
600+void
601+seat_destroy(struct swc_seat *seat_base)
602+{
603+ struct seat *seat = wl_container_of(seat_base, seat, base);
604+
605+ if (seat->mouse_source)
606+ wl_event_source_remove(seat->mouse_source);
607+ wl_event_source_remove(seat->kbd_source);
608+ if (seat->mouse_source) {
609+ close(seat->mouse_fd);
610+ seat->mouse_fd = -1;
611+ }
612+ close(seat->kbd_fd);
613+ seat->kbd_fd = -1;
614+
615+ pointer_finalize(&seat->pointer);
616+ keyboard_destroy(seat->base.keyboard);
617+ data_device_destroy(seat->base.data_device);
618+
619+ wl_global_destroy(seat->global);
620+ free(seat->name);
621+ free(seat);
622+}
+1,
-0
1@@ -361,6 +361,7 @@ seat_create(struct wl_display *display, const char *seat_name)
2 if (!seat)
3 goto error0;
4
5+ memset(&seat->names, 0, sizeof(seat->names));
6 seat->names.rules = "base";
7 seat->names.model = "pc105";
8 seat->names.layout = "us";