wlroots/backend/session/session_libudev.c
2026-05-23 11:11:17 +03:00

147 lines
3.8 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <libudev.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <wlr/backend/session.h>
#include "backend/session/session.h"
struct drm_event_monitor {
struct udev *udev;
struct udev_monitor *mon;
};
struct drm_event_monitor *monitor_drm_events(int *fd) {
struct drm_event_monitor *ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
return NULL;
}
ctx->udev = udev_new();
if (!ctx->udev) {
free(ctx);
return NULL;
}
ctx->mon = udev_monitor_new_from_netlink(ctx->udev, "udev");
if (!ctx->mon) {
udev_unref(ctx->udev);
free(ctx);
return NULL;
}
udev_monitor_filter_add_match_subsystem_devtype(ctx->mon, "drm", NULL);
udev_monitor_enable_receiving(ctx->mon);
*fd = udev_monitor_get_fd(ctx->mon);
return ctx;
}
void drm_event_monitor_free(struct drm_event_monitor *ctx) {
udev_monitor_unref(ctx->mon);
udev_unref(ctx->udev);
free(ctx);
}
static void populate_drm_event(struct drm_event_content *ev,
struct udev_device *dev) {
memset(ev, 0, sizeof(*ev));
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
int len = snprintf(ev->devnode, sizeof(ev->devnode), "%s", devnode);
assert((size_t)len < sizeof(ev->devnode));
}
ev->devnum = udev_device_get_devnum(dev);
const char *action = udev_device_get_action(dev);
if (strcmp(action, "add") == 0) {
ev->type = DRM_EVENT_ACTION_ADD;
} else if (strcmp(action, "change") == 0) {
ev->type = DRM_EVENT_ACTION_CHANGE;
} else if (strcmp(action, "remove") == 0) {
ev->type = DRM_EVENT_ACTION_REMOVE;
}
const char *hotplug = udev_device_get_property_value(dev, "HOTPLUG");
if (hotplug != NULL && strcmp(hotplug, "1") == 0) {
ev->has_lease = false;
const char *connector =
udev_device_get_property_value(dev, "CONNECTOR");
if (connector != NULL) {
ev->conn_id = strtoul(connector, NULL, 10);
}
const char *prop =
udev_device_get_property_value(dev, "PROPERTY");
if (prop != NULL) {
ev->prop_id = strtoul(prop, NULL, 10);
}
return;
}
const char *lease = udev_device_get_property_value(dev, "LEASE");
if (lease != NULL && strcmp(lease, "1") == 0) {
ev->has_lease = true;
}
}
int receive_drm_device(struct drm_event_monitor *ctx,
struct drm_event_content *ev) {
struct udev_device *dev = udev_monitor_receive_device(ctx->mon);
if (!dev) {
return -1;
}
populate_drm_event(ev, dev);
udev_device_unref(dev);
return 0;
}
static struct udev_device *get_udev_device_from_devnode(struct udev *udev,
const char *devnode) {
const char *sysname = strrchr(devnode, '/');
if (!sysname) {
return NULL;
}
sysname += 1;
struct udev_device *dev =
udev_device_new_from_subsystem_sysname(udev, "drm", sysname);
if (!dev) {
return NULL;
}
return dev;
}
// XXX: move it to libseat?
const char *get_device_seat(struct drm_event_monitor *ctx, const char *devnode) {
struct udev_device *dev = get_udev_device_from_devnode(ctx->udev, devnode);
if (!dev) {
return NULL;
}
const char *seat = udev_device_get_property_value(dev, "ID_SEAT");
udev_device_unref(dev);
return seat;
}
// XXX: use sysfs directly?
bool is_drm_device_primary(struct drm_event_monitor *ctx, const char *devnode) {
struct udev_device *dev = get_udev_device_from_devnode(ctx->udev, devnode);
if (!dev) {
return false;
}
const char *boot_display = udev_device_get_sysattr_value(dev, "boot_display");
if (boot_display && strcmp(boot_display, "1") == 0) {
udev_device_unref(dev);
return true;
}
// 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) {
udev_device_unref(dev);
return false;
}
const char *id = udev_device_get_sysattr_value(pci, "boot_vga");
bool primary = id && strcmp(id, "1") == 0;
udev_device_unref(dev);
return primary;
}