diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 60f456cc1..21fdfe361 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -5,9 +5,16 @@ #include #include #include +#include #include "backend/libinput.h" #include "util/signal.h" +#if WLR_HAS_UDEV +#include "backend/session/dev_udev.h" +#endif + +#define NETLINK_BITMASK 4 + static struct wlr_libinput_backend *get_libinput_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_libinput(wlr_backend)); @@ -86,15 +93,33 @@ static bool backend_start(struct wlr_backend *wlr_backend) { get_libinput_backend_from_backend(wlr_backend); wlr_log(WLR_DEBUG, "Starting libinput backend"); +#if WLR_HAS_UDEV backend->libinput_context = libinput_udev_create_context(&libinput_impl, - backend, backend->session->udev); + 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); +#else +#error Unsupported platform +#endif if (!backend->libinput_context) { wlr_log(WLR_ERROR, "Failed to create libinput context"); return false; } +#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) { +#else +#error Unsupported platform +#endif wlr_log(WLR_ERROR, "Failed to assign libinput seat"); return false; } diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index 083711ef0..580676ec4 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -5,9 +5,14 @@ #include #include #include +#include #include "backend/libinput.h" #include "util/signal.h" +#if WLR_HAS_UDEV +#include +#endif + const struct wlr_tablet_pad_impl libinput_tablet_pad_impl = { .name = "libinput-tablet-pad", }; @@ -106,9 +111,11 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { wlr_tablet_pad->strip_count = libinput_device_tablet_pad_get_num_strips(handle); +#if WLR_HAS_UDEV struct udev_device *udev = libinput_device_get_udev_device(handle); char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); +#endif int groups = libinput_device_tablet_pad_get_num_mode_groups(handle); for (int i = 0; i < groups; ++i) { diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index 31cd18b8c..5d0f6edba 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -5,9 +5,14 @@ #include #include #include +#include #include "backend/libinput.h" #include "util/signal.h" +#if WLR_HAS_UDEV +#include +#endif + struct tablet_tool { struct wlr_tablet_tool wlr_tool; struct libinput_tablet_tool *handle; @@ -28,9 +33,11 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) { libinput_device_get_size(dev->handle, &wlr_tablet->width_mm, &wlr_tablet->height_mm); +#if WLR_HAS_UDEV struct udev_device *udev = libinput_device_get_udev_device(dev->handle); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); +#endif wl_list_init(&dev->tablet_tools); } diff --git a/backend/session/dev.c b/backend/session/dev.c new file mode 100644 index 000000000..96236dcbb --- /dev/null +++ b/backend/session/dev.c @@ -0,0 +1,21 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include "backend/session/dev.h" + +// TODO move to util? +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; +} diff --git a/backend/session/dev_demi.c b/backend/session/dev_demi.c new file mode 100644 index 000000000..855d9f4cd --- /dev/null +++ b/backend/session/dev_demi.c @@ -0,0 +1,135 @@ +#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 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; + bool has_devnum = stat(devnode, &st) == 0; + + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (has_devnum) { + if (dev->dev != st.st_rdev) { + continue; + } + } else { + if (strcmp(dev->devnode, devnode) != 0) { + 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/dev_udev.c b/backend/session/dev_udev.c new file mode 100644 index 000000000..c1cf304ff --- /dev/null +++ b/backend/session/dev_udev.c @@ -0,0 +1,306 @@ +#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_udev.h" +#include "util/signal.h" + +#define WAIT_GPU_TIMEOUT 10000 // ms + +static void read_udev_change_event(struct wlr_device_change_event *event, + struct udev_device *udev_dev) { + const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); + if (hotplug != NULL && strcmp(hotplug, "1") == 0) { + event->type = WLR_DEVICE_HOTPLUG; + struct wlr_device_hotplug_event *hotplug = &event->hotplug; + + const char *connector = + udev_device_get_property_value(udev_dev, "CONNECTOR"); + if (connector != NULL) { + hotplug->connector_id = strtoul(connector, NULL, 10); + } + + const char *prop = + udev_device_get_property_value(udev_dev, "PROPERTY"); + if (prop != NULL) { + hotplug->prop_id = strtoul(prop, NULL, 10); + } + + return; + } + + const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); + if (lease != NULL && strcmp(lease, "1") == 0) { + event->type = WLR_DEVICE_LEASE; + return; + } +} + +static int handle_udev_event(int fd, uint32_t mask, void *data) { + struct wlr_session *session = data; + + struct udev_device *udev_dev = udev_monitor_receive_device(session->dev->mon); + if (!udev_dev) { + return 1; + } + + const char *sysname = udev_device_get_sysname(udev_dev); + const char *devnode = udev_device_get_devnode(udev_dev); + const char *action = udev_device_get_action(udev_dev); + wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); + + if (!is_drm_card(sysname) || !action || !devnode) { + goto out; + } + + const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); + if (!seat) { + seat = "seat0"; + } + if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { + goto out; + } + + if (strcmp(action, "add") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s added", sysname); + struct wlr_session_add_event event = { + .path = devnode, + }; + wlr_signal_emit_safe(&session->events.add_drm_card, &event); + } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { + dev_t devnum = udev_device_get_devnum(udev_dev); + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev != devnum) { + continue; + } + + if (strcmp(action, "change") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); + struct wlr_device_change_event event = {0}; + read_udev_change_event(&event, udev_dev); + wlr_signal_emit_safe(&dev->events.change, &event); + } else if (strcmp(action, "remove") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); + wlr_signal_emit_safe(&dev->events.remove, NULL); + } else { + assert(0); + } + break; + } + } + +out: + udev_device_unref(udev_dev); + 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->udev = udev_new(); + if (!dev->udev) { + wlr_log_errno(WLR_ERROR, "Failed to create udev context"); + goto error_dev; + } + + dev->mon = udev_monitor_new_from_netlink(dev->udev, "udev"); + if (!dev->mon) { + wlr_log_errno(WLR_ERROR, "Failed to create udev monitor"); + goto error_udev; + } + + udev_monitor_filter_add_match_subsystem_devtype(dev->mon, "drm", NULL); + udev_monitor_enable_receiving(dev->mon); + + struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); + int fd = udev_monitor_get_fd(dev->mon); + + dev->udev_event = wl_event_loop_add_fd(event_loop, fd, + WL_EVENT_READABLE, handle_udev_event, session); + if (!dev->udev_event) { + wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); + goto error_mon; + } + + session->dev = dev; + return 0; + +error_mon: + udev_monitor_unref(dev->mon); +error_udev: + udev_unref(dev->udev); +error_dev: + free(dev); + return -1; +} + +void dev_finish(struct wlr_session *session) { + if (!session) { + return; + } + + wl_event_source_remove(session->dev->udev_event); + udev_monitor_unref(session->dev->mon); + udev_unref(session->dev->udev); + free(session->dev); +} + +static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { + struct udev_enumerate *en = udev_enumerate_new(udev); + if (!en) { + wlr_log(WLR_ERROR, "udev_enumerate_new failed"); + return NULL; + } + + udev_enumerate_add_match_subsystem(en, "drm"); + udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*"); + + if (udev_enumerate_scan_devices(en) != 0) { + wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); + udev_enumerate_unref(en); + return NULL; + } + + return en; +} + +static uint64_t get_current_time_ms(void) { + struct timespec ts = {0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} + +struct find_gpus_add_handler { + bool added; + struct wl_listener listener; +}; + +static void find_gpus_handle_add(struct wl_listener *listener, void *data) { + struct find_gpus_add_handler *handler = + wl_container_of(listener, handler, listener); + handler->added = true; +} + +/* Tries to find the primary GPU by checking for the "boot_vga" attribute. + * If it's not found, it returns the first valid GPU it finds. + */ +ssize_t dev_find_gpus(struct wlr_session *session, + size_t ret_len, struct wlr_device **ret) { + struct udev_enumerate *en = enumerate_drm_cards(session->dev->udev); + if (!en) { + return -1; + } + + if (udev_enumerate_get_list_entry(en) == NULL) { + udev_enumerate_unref(en); + wlr_log(WLR_INFO, "Waiting for a DRM card device"); + + struct find_gpus_add_handler handler = {0}; + handler.listener.notify = find_gpus_handle_add; + wl_signal_add(&session->events.add_drm_card, &handler.listener); + + uint64_t started_at = get_current_time_ms(); + uint64_t timeout = WAIT_GPU_TIMEOUT; + struct wl_event_loop *event_loop = + wl_display_get_event_loop(session->display); + while (!handler.added) { + int ret = wl_event_loop_dispatch(event_loop, (int)timeout); + if (ret < 0) { + wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " + "wl_event_loop_dispatch failed"); + udev_enumerate_unref(en); + return -1; + } + + uint64_t now = get_current_time_ms(); + if (now >= started_at + WAIT_GPU_TIMEOUT) { + break; + } + timeout = started_at + WAIT_GPU_TIMEOUT - now; + } + + wl_list_remove(&handler.listener.link); + + en = enumerate_drm_cards(session->dev->udev); + if (!en) { + return -1; + } + } + + struct udev_list_entry *entry; + size_t i = 0; + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { + if (i == ret_len) { + break; + } + + bool is_boot_vga = false; + + const char *path = udev_list_entry_get_name(entry); + struct udev_device *dev = udev_device_new_from_syspath(session->dev->udev, path); + if (!dev) { + continue; + } + + const char *seat = udev_device_get_property_value(dev, "ID_SEAT"); + if (!seat) { + seat = "seat0"; + } + if (session->seat[0] && strcmp(session->seat, seat) != 0) { + udev_device_unref(dev); + continue; + } + + // This is owned by 'dev', so we don't need to free it + struct udev_device *pci = + udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); + + if (pci) { + const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); + if (id && strcmp(id, "1") == 0) { + is_boot_vga = true; + } + } + + struct wlr_device *wlr_dev = + session_open_if_kms(session, udev_device_get_devnode(dev)); + if (!wlr_dev) { + udev_device_unref(dev); + continue; + } + + udev_device_unref(dev); + + ret[i] = wlr_dev; + if (is_boot_vga) { + struct wlr_device *tmp = ret[0]; + ret[0] = ret[i]; + ret[i] = tmp; + } + + ++i; + } + + udev_enumerate_unref(en); + + return i; +} diff --git a/backend/session/meson.build b/backend/session/meson.build index 27915506b..39fd94125 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -3,5 +3,29 @@ libseat = dependency('libseat', fallback: 'seatd', default_options: ['server=disabled', 'man-pages=disabled'], ) -wlr_files += files('session.c') +wlr_files += files('session.c', 'dev.c') wlr_deps += libseat + +# 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 7d6d080da..36ac7cb81 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -1,8 +1,5 @@ #define _POSIX_C_SOURCE 200809L -#include -#include #include -#include #include #include #include @@ -17,12 +14,11 @@ #include #include #include "backend/session/session.h" +#include "backend/session/dev.h" #include "util/signal.h" #include -#define WAIT_GPU_TIMEOUT 10000 // ms - static void handle_enable_seat(struct libseat *seat, void *data) { struct wlr_session *session = data; session->active = true; @@ -126,107 +122,6 @@ static void libseat_session_finish(struct wlr_session *session) { session->libseat_event = NULL; } -static bool is_drm_card(const char *sysname) { - const char prefix[] = DRM_PRIMARY_MINOR_NAME; - if (strncmp(sysname, prefix, strlen(prefix)) != 0) { - return false; - } - for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) { - if (sysname[i] < '0' || sysname[i] > '9') { - return false; - } - } - return true; -} - -static void read_udev_change_event(struct wlr_device_change_event *event, - struct udev_device *udev_dev) { - const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); - if (hotplug != NULL && strcmp(hotplug, "1") == 0) { - event->type = WLR_DEVICE_HOTPLUG; - struct wlr_device_hotplug_event *hotplug = &event->hotplug; - - const char *connector = - udev_device_get_property_value(udev_dev, "CONNECTOR"); - if (connector != NULL) { - hotplug->connector_id = strtoul(connector, NULL, 10); - } - - const char *prop = - udev_device_get_property_value(udev_dev, "PROPERTY"); - if (prop != NULL) { - hotplug->prop_id = strtoul(prop, NULL, 10); - } - - return; - } - - const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); - if (lease != NULL && strcmp(lease, "1") == 0) { - event->type = WLR_DEVICE_LEASE; - return; - } -} - -static int handle_udev_event(int fd, uint32_t mask, void *data) { - struct wlr_session *session = data; - - struct udev_device *udev_dev = udev_monitor_receive_device(session->mon); - if (!udev_dev) { - return 1; - } - - const char *sysname = udev_device_get_sysname(udev_dev); - const char *devnode = udev_device_get_devnode(udev_dev); - const char *action = udev_device_get_action(udev_dev); - wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); - - if (!is_drm_card(sysname) || !action || !devnode) { - goto out; - } - - const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); - if (!seat) { - seat = "seat0"; - } - if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { - goto out; - } - - if (strcmp(action, "add") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s added", sysname); - struct wlr_session_add_event event = { - .path = devnode, - }; - wlr_signal_emit_safe(&session->events.add_drm_card, &event); - } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { - dev_t devnum = udev_device_get_devnum(udev_dev); - struct wlr_device *dev; - wl_list_for_each(dev, &session->devices, link) { - if (dev->dev != devnum) { - continue; - } - - if (strcmp(action, "change") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); - struct wlr_device_change_event event = {0}; - read_udev_change_event(&event, udev_dev); - wlr_signal_emit_safe(&dev->events.change, &event); - } else if (strcmp(action, "remove") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); - wlr_signal_emit_safe(&dev->events.remove, NULL); - } else { - assert(0); - } - break; - } - } - -out: - udev_device_unref(udev_dev); - return 1; -} - static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_session *session = wl_container_of(listener, session, display_destroy); @@ -250,30 +145,12 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { goto error_open; } - session->udev = udev_new(); - if (!session->udev) { - wlr_log_errno(WLR_ERROR, "Failed to create udev context"); +#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; } - - session->mon = udev_monitor_new_from_netlink(session->udev, "udev"); - if (!session->mon) { - wlr_log_errno(WLR_ERROR, "Failed to create udev monitor"); - goto error_udev; - } - - udev_monitor_filter_add_match_subsystem_devtype(session->mon, "drm", NULL); - udev_monitor_enable_receiving(session->mon); - - struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); - int fd = udev_monitor_get_fd(session->mon); - - session->udev_event = wl_event_loop_add_fd(event_loop, fd, - WL_EVENT_READABLE, handle_udev_event, session); - if (!session->udev_event) { - wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); - goto error_mon; - } +#endif session->display = disp; @@ -282,12 +159,10 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { return session; -error_mon: - udev_monitor_unref(session->mon); -error_udev: - udev_unref(session->udev); +#if WLR_HAS_UDEV || WLR_HAS_DEMI error_session: libseat_session_finish(session); +#endif error_open: free(session); return NULL; @@ -301,9 +176,9 @@ void wlr_session_destroy(struct wlr_session *session) { wlr_signal_emit_safe(&session->events.destroy, session); wl_list_remove(&session->display_destroy.link); - wl_event_source_remove(session->udev_event); - udev_monitor_unref(session->mon); - udev_unref(session->udev); +#if WLR_HAS_UDEV || WLR_HAS_DEMI + dev_finish(session); +#endif struct wlr_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) { @@ -335,6 +210,12 @@ struct wlr_device *wlr_session_open_file(struct wlr_session *session, goto error; } + dev->devnode = strdup(path); + if (!dev->devnode) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + goto error; + } + dev->fd = fd; dev->dev = st.st_rdev; dev->device_id = device_id; @@ -358,6 +239,7 @@ void wlr_session_close_file(struct wlr_session *session, } close(dev->fd); wl_list_remove(&dev->link); + free(dev->devnode); free(dev); } @@ -418,45 +300,6 @@ static ssize_t explicit_find_gpus(struct wlr_session *session, return i; } -static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { - struct udev_enumerate *en = udev_enumerate_new(udev); - if (!en) { - wlr_log(WLR_ERROR, "udev_enumerate_new failed"); - return NULL; - } - - udev_enumerate_add_match_subsystem(en, "drm"); - udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*"); - - if (udev_enumerate_scan_devices(en) != 0) { - wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); - udev_enumerate_unref(en); - return NULL; - } - - return en; -} - -static uint64_t get_current_time_ms(void) { - struct timespec ts = {0}; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; -} - -struct find_gpus_add_handler { - bool added; - struct wl_listener listener; -}; - -static void find_gpus_handle_add(struct wl_listener *listener, void *data) { - struct find_gpus_add_handler *handler = - wl_container_of(listener, handler, listener); - handler->added = true; -} - -/* Tries to find the primary GPU by checking for the "boot_vga" attribute. - * If it's not found, it returns the first valid GPU it finds. - */ ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); @@ -464,103 +307,61 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, return explicit_find_gpus(session, ret_len, ret, explicit); } - struct udev_enumerate *en = enumerate_drm_cards(session->udev); - if (!en) { +#if WLR_HAS_UDEV + return dev_find_gpus(session, ret_len, ret); +#endif + + int cnt = drmGetDevices2(0, NULL, 0); + if (cnt <= 0) { + return cnt == 0 ? 0 : -1; + } + + drmDevicePtr *devs = calloc(cnt, sizeof(*devs)); + if (!devs) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); return -1; } - if (udev_enumerate_get_list_entry(en) == NULL) { - udev_enumerate_unref(en); - wlr_log(WLR_INFO, "Waiting for a DRM card device"); - - struct find_gpus_add_handler handler = {0}; - handler.listener.notify = find_gpus_handle_add; - wl_signal_add(&session->events.add_drm_card, &handler.listener); - - uint64_t started_at = get_current_time_ms(); - uint64_t timeout = WAIT_GPU_TIMEOUT; - struct wl_event_loop *event_loop = - wl_display_get_event_loop(session->display); - while (!handler.added) { - int ret = wl_event_loop_dispatch(event_loop, (int)timeout); - if (ret < 0) { - wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " - "wl_event_loop_dispatch failed"); - udev_enumerate_unref(en); - return -1; - } - - uint64_t now = get_current_time_ms(); - if (now >= started_at + WAIT_GPU_TIMEOUT) { - break; - } - timeout = started_at + WAIT_GPU_TIMEOUT - now; - } - - wl_list_remove(&handler.listener.link); - - en = enumerate_drm_cards(session->udev); - if (!en) { - return -1; - } + cnt = drmGetDevices2(0, devs, cnt); + if (cnt <= 0) { + free(devs); + return cnt == 0 ? 0 : -1; } - struct udev_list_entry *entry; - size_t i = 0; + ssize_t total = 0; + size_t max = (size_t)cnt > ret_len ? ret_len : (size_t)cnt; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { - if (i == ret_len) { - break; + for (size_t i = 0; i < max; i++) { + if (!(devs[i]->available_nodes & (1 << DRM_NODE_PRIMARY))) { + continue; } + // TODO bool is_boot_vga = false; - const char *path = udev_list_entry_get_name(entry); - struct udev_device *dev = udev_device_new_from_syspath(session->udev, path); - if (!dev) { + // TODO https://todo.sr.ht/~kennylevinsen/seatd/1 + const char *seat = "seat0"; + if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { continue; } - const char *seat = udev_device_get_property_value(dev, "ID_SEAT"); - if (!seat) { - seat = "seat0"; - } - if (session->seat[0] && strcmp(session->seat, seat) != 0) { - udev_device_unref(dev); - continue; - } - - // This is owned by 'dev', so we don't need to free it - struct udev_device *pci = - udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); - - if (pci) { - const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); - if (id && strcmp(id, "1") == 0) { - is_boot_vga = true; - } - } - - struct wlr_device *wlr_dev = - session_open_if_kms(session, udev_device_get_devnode(dev)); + const char *devnode = devs[i]->nodes[DRM_NODE_PRIMARY]; + struct wlr_device *wlr_dev = session_open_if_kms(session, devnode); if (!wlr_dev) { - udev_device_unref(dev); continue; } - udev_device_unref(dev); - - ret[i] = wlr_dev; + ret[total] = wlr_dev; if (is_boot_vga) { struct wlr_device *tmp = ret[0]; - ret[0] = ret[i]; - ret[i] = tmp; + ret[0] = ret[total]; + ret[total] = tmp; } - ++i; + total++; } - udev_enumerate_unref(en); - - return i; + drmFreeDevices(devs, cnt); + free(devs); + return total; } diff --git a/include/backend/session/dev.h b/include/backend/session/dev.h new file mode 100644 index 000000000..bae6c14a9 --- /dev/null +++ b/include/backend/session/dev.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +#ifndef BACKEND_SESSION_DEV_H +#define BACKEND_SESSION_DEV_H + +int dev_init(struct wlr_session *session, struct wl_display *display); +void dev_finish(struct wlr_session *session); + +ssize_t dev_find_gpus(struct wlr_session *session, size_t ret_len, + struct wlr_device **ret); + +bool is_drm_card(const char *devname); +#endif diff --git a/include/backend/session/dev_demi.h b/include/backend/session/dev_demi.h new file mode 100644 index 000000000..b2a113fa6 --- /dev/null +++ b/include/backend/session/dev_demi.h @@ -0,0 +1,11 @@ +#include + +#ifndef BACKEND_SESSION_DEV_DEMI_H +#define BACKEND_SESSION_DEV_DEMI_H + +struct dev { + int fd; + struct wl_event_source *event; +}; + +#endif diff --git a/include/backend/session/dev_udev.h b/include/backend/session/dev_udev.h new file mode 100644 index 000000000..186984494 --- /dev/null +++ b/include/backend/session/dev_udev.h @@ -0,0 +1,13 @@ +#include +#include + +#ifndef BACKEND_SESSION_DEV_UDEV_H +#define BACKEND_SESSION_DEV_UDEV_H + +struct dev { + struct udev *udev; + struct udev_monitor *mon; + struct wl_event_source *udev_event; +}; + +#endif diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index e5deda574..c2c90f33b 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -1,17 +1,18 @@ #ifndef WLR_BACKEND_SESSION_H #define WLR_BACKEND_SESSION_H -#include #include #include #include struct libseat; +struct dev; struct wlr_device { int fd; int device_id; dev_t dev; + char *devnode; struct wl_list link; struct { @@ -34,9 +35,7 @@ struct wlr_session { unsigned vtnr; char seat[256]; - struct udev *udev; - struct udev_monitor *mon; - struct wl_event_source *udev_event; + struct dev *dev; struct libseat *seat_handle; struct wl_event_source *libseat_event; diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index 71868a349..73b81c1bd 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -11,4 +11,7 @@ #mesondefine WLR_HAS_XWAYLAND +#mesondefine WLR_HAS_UDEV +#mesondefine WLR_HAS_DEMI + #endif diff --git a/meson.build b/meson.build index 5d45073de..666d3d020 100644 --- a/meson.build +++ b/meson.build @@ -92,6 +92,8 @@ features = { 'xwayland': false, 'gles2-renderer': false, 'vulkan-renderer': false, + 'udev': false, + 'demi': false, } internal_features = { 'xcb-errors': false, @@ -127,7 +129,6 @@ drm = dependency('libdrm', ) gbm = dependency('gbm', version: '>=17.1.0') xkbcommon = dependency('xkbcommon') -udev = dependency('libudev') pixman = dependency('pixman-1') math = cc.find_library('m') rt = cc.find_library('rt') @@ -138,7 +139,6 @@ wlr_deps = [ drm, gbm, xkbcommon, - udev, pixman, math, rt, diff --git a/meson_options.txt b/meson_options.txt index 550acbe6f..491dd1e64 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,3 +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', 'demi'], value: 'udev', description: 'Select device enumeration backend')