diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 2dcbc5024..21fdfe361 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -96,6 +96,8 @@ static bool backend_start(struct wlr_backend *wlr_backend) { #if WLR_HAS_UDEV backend->libinput_context = libinput_udev_create_context(&libinput_impl, backend, backend->session->dev->udev); +#elif WLR_HAS_DEMI + backend->libinput_context = libinput_create_context(&libinput_impl, backend); #elif defined(__linux__) backend->libinput_context = libinput_netlink_create_context(&libinput_impl, backend, NETLINK_BITMASK); @@ -110,6 +112,8 @@ static bool backend_start(struct wlr_backend *wlr_backend) { #if WLR_HAS_UDEV if (libinput_udev_assign_seat(backend->libinput_context, backend->session->seat) != 0) { +#elif WLR_HAS_DEMI + if (libinput_assign_seat(backend->libinput_context, backend->session->seat) != 0) { #elif defined(__linux__) if (libinput_netlink_assign_seat(backend->libinput_context, backend->session->seat) != 0) { diff --git a/backend/session/dev_demi.c b/backend/session/dev_demi.c new file mode 100644 index 000000000..a10b0243d --- /dev/null +++ b/backend/session/dev_demi.c @@ -0,0 +1,148 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend/session/session.h" +#include "backend/session/dev.h" +#include "backend/session/dev_demi.h" +#include "util/signal.h" + +static bool is_drm_card(const char *devname) { + const char prefix[] = DRM_PRIMARY_MINOR_NAME; + const char *name = strrchr(devname); + name = name ? name + 1 : devname; + if (strncmp(name, prefix, strlen(prefix)) != 0) { + return false; + } + for (size_t i = strlen(prefix); name[i] != '\0'; i++) { + if (name[i] < '0' || name[i] > '9') { + return false; + } + } + return true; +} + +static int handle_event(int fd, uint32_t mask, void *data) { + struct wlr_session *session = data; + + struct demi_event event; + if (demi_read(fd, &event) == -1) { + // TODO log + return 1; + } + + const char *devname = event.de_devname; + enum demi_event_type event_type = event.de_type; + + if (event_type == DEMI_UNKNOWN) { + // TODO log + return 1; + } + + if (!is_drm_card(devname)) { + // TODO log + return 1; + } + + char devnode[sizeof("/dev/") + sizeof(event.de_devname)]; + snprintf(devnode, sizeof(devnode), "/dev/%s", devname); + + wlr_log(WLR_DEBUG, "kernel event for %s (code %d)", devnode, event_type); + + // TODO https://todo.sr.ht/~kennylevinsen/seatd/1 + const char *seat = "seat0"; + if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { + return 1; + } + + if (event_type == DEMI_ATTACH) { + wlr_log(WLR_DEBUG, "DRM device %s added", devnode); + struct wlr_session_add_event event = { + .path = devnode, + }; + wlr_signal_emit_safe(&session->events.add_drm_card, &event); + } else if (event_type == DEMI_CHANGE || event_type == DEMI_DETACH) { + struct stat st; + // FIXME stat will fail on DEMI_DETACH + if (stat(devnode, &st) == -1) { + // FIXME fallback to comparing devnode + return 1; + } + + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev != st.st_rdev) { + continue; + } + + if (event_type == DEMI_CHANGE) { + wlr_log(WLR_DEBUG, "DRM device %s changed", devnode); + // TODO + // struct wlr_device_change_event event = {0}; + // read_udev_change_event(&event, udev_dev); + wlr_signal_emit_safe(&dev->events.change, NULL); + } else if (event_type == DEMI_DETACH) { + wlr_log(WLR_DEBUG, "DRM device %s removed", devnode); + wlr_signal_emit_safe(&dev->events.remove, NULL); + } else { + abort(); + } + break; + } + } + + return 1; +} + +int dev_init(struct wlr_session *session, struct wl_display *disp) { + struct dev *dev = calloc(1, sizeof(*dev)); + if (!dev) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return -1; + } + + dev->fd = demi_init(DEMI_CLOEXEC | DEMI_NONBLOCK); + if (dev->fd == -1) { + wlr_log_errno(WLR_ERROR, "Failed to subscribe to kernel events"); + goto error_dev; + } + + struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); + + dev->event = wl_event_loop_add_fd(event_loop, dev->fd, + WL_EVENT_READABLE, handle_event, session); + if (!dev->event) { + wlr_log_errno(WLR_ERROR, "Failed to create gpu hotplugging event source"); + goto error_fd; + } + + session->dev = dev; + return 0; + +error_fd: + close(dev->fd); +error_dev: + free(dev); + return -1; +} + +void dev_finish(struct wlr_session *session) { + if (!session) { + return; + } + + wl_event_source_remove(session->dev->event); + close(session->dev->fd); + free(session->dev); +} diff --git a/backend/session/meson.build b/backend/session/meson.build index f407a2131..d62784d2b 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -6,17 +6,26 @@ libseat = dependency('libseat', wlr_files += files('session.c') wlr_deps += libseat -# libudev +# libudev & libdemi if get_option('enum-backend') == 'auto' udev = dependency('libudev', required: false) + demi = dependency('demi', required: false) if udev.found() wlr_files += files('dev_udev.c') wlr_deps += udev features += { 'udev': true } + elif demi.found() + wlr_files += files('dev_demi.c') + wlr_deps += demi + features += { 'demi': true } endif elif get_option('enum-backend') == 'udev' wlr_files += files('dev_udev.c') wlr_deps += dependency('libudev') features += { 'udev': true } +elif get_option('enum-backend') == 'demi' + wlr_files += files('dev_demi.c') + wlr_deps += dependency('demi') + features += { 'demi': true } endif diff --git a/backend/session/session.c b/backend/session/session.c index f2917fcad..cd3927fdc 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -145,7 +145,7 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { goto error_open; } -#if WLR_HAS_UDEV +#if WLR_HAS_UDEV || WLR_HAS_DEMI if (dev_init(session, disp) == -1) { wlr_log(WLR_ERROR, "Failed to initialize dev backend"); goto error_session; @@ -159,7 +159,7 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { return session; -#if WLR_HAS_UDEV +#if WLR_HAS_UDEV || WLR_HAS_DEMI error_session: libseat_session_finish(session); #endif @@ -176,7 +176,7 @@ void wlr_session_destroy(struct wlr_session *session) { wlr_signal_emit_safe(&session->events.destroy, session); wl_list_remove(&session->display_destroy.link); -#if WLR_HAS_UDEV +#if WLR_HAS_UDEV || WLR_HAS_DEMI dev_finish(session); #endif diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index e636bf5e0..73b81c1bd 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -12,5 +12,6 @@ #mesondefine WLR_HAS_XWAYLAND #mesondefine WLR_HAS_UDEV +#mesondefine WLR_HAS_DEMI #endif diff --git a/meson.build b/meson.build index ec3b734db..12df8e282 100644 --- a/meson.build +++ b/meson.build @@ -93,6 +93,7 @@ features = { 'gles2-renderer': false, 'vulkan-renderer': false, 'udev': false, + 'demi': false, } internal_features = { 'xcb-errors': false, diff --git a/meson_options.txt b/meson_options.txt index 01eca736e..491dd1e64 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,4 +4,4 @@ option('examples', type: 'boolean', value: true, description: 'Build example app option('icon_directory', description: 'Location used to look for cursors (default: ${datadir}/icons)', type: 'string', value: '') option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value: ['auto'], description: 'Select built-in renderers') option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends') -option('enum-backend', type: 'combo', choices: ['auto', 'disabled', 'udev'], value: 'udev', description: 'Select device enumeration backend') +option('enum-backend', type: 'combo', choices: ['auto', 'disabled', 'udev', 'demi'], value: 'udev', description: 'Select device enumeration backend')