uinput: configurable device identity with fallback to built-in defaults

Add vmctl_uinput_id and an optional vmctl_config.uinput_id pointer. NULL keeps the built-in identity verbatim; a non-NULL config supplies all numeric fields literally, with the device name suffixed -A/-B for the dual-device mode.
This commit is contained in:
2026-06-17 14:42:25 +03:00
parent bb757b64cf
commit 329b12bf07
2 changed files with 41 additions and 7 deletions
+9
View File
@@ -19,11 +19,20 @@ typedef enum {
#define VMCTL_PTR_REL 2 /* uinput: relative mouse */ #define VMCTL_PTR_REL 2 /* uinput: relative mouse */
#define VMCTL_PTR_BOTH 3 /* uinput: two devices A=abs B=rel */ #define VMCTL_PTR_BOTH 3 /* uinput: two devices A=abs B=rel */
typedef struct {
unsigned bustype; /* HID bus type, e.g. 0x0003 (USB) */
unsigned vendor; /* vendor id */
unsigned product; /* product id */
unsigned version; /* device version */
const char* name; /* device name; library copies it */
} vmctl_uinput_id;
typedef struct { typedef struct {
vmctl_driver driver; vmctl_driver driver;
const char* qmp_path; /* QMP unix socket; required for QMP, optional (passthrough) for UINPUT */ const char* qmp_path; /* QMP unix socket; required for QMP, optional (passthrough) for UINPUT */
const char* input_bus; /* virtio-input-host-pci bus "pci.0" for passthrough; "" = none */ const char* input_bus; /* virtio-input-host-pci bus "pci.0" for passthrough; "" = none */
int ptr_mode; /* UINPUT VMCTL_PTR_*; 0 for QMP */ int ptr_mode; /* UINPUT VMCTL_PTR_*; 0 for QMP */
const vmctl_uinput_id* uinput_id; /* UINPUT only; NULL = built-in defaults */
} vmctl_config; } vmctl_config;
vmctl_t* vmctl_open (const vmctl_config* cfg); /* NULL on error */ vmctl_t* vmctl_open (const vmctl_config* cfg); /* NULL on error */
+32 -7
View File
@@ -50,7 +50,7 @@ static void emit(int fd, uint16_t type, uint16_t code, int32_t val) {
static void syn(int fd) { emit(fd, EV_SYN, SYN_REPORT, 0); } static void syn(int fd) { emit(fd, EV_SYN, SYN_REPORT, 0); }
static int uinput_create(int rel_motion, const char* name, char evdev[64]) { static int uinput_create(int rel_motion, const vmctl_uinput_id* id, const char* name, char evdev[64]) {
int fd = open("/dev/uinput", O_RDWR | O_CLOEXEC); int fd = open("/dev/uinput", O_RDWR | O_CLOEXEC);
if (fd < 0) return -1; if (fd < 0) return -1;
@@ -89,10 +89,10 @@ static int uinput_create(int rel_motion, const char* name, char evdev[64]) {
struct uinput_setup us; struct uinput_setup us;
memset(&us, 0, sizeof us); memset(&us, 0, sizeof us);
us.id.bustype = HWID_BUS; us.id.bustype = (uint16_t)id->bustype;
us.id.vendor = HWID_VENDOR; us.id.vendor = (uint16_t)id->vendor;
us.id.product = HWID_PRODUCT; us.id.product = (uint16_t)id->product;
us.id.version = HWID_VERSION; us.id.version = (uint16_t)id->version;
strncpy(us.name, name, sizeof us.name - 1); strncpy(us.name, name, sizeof us.name - 1);
if (ioctl(fd, UI_DEV_SETUP, &us) < 0 || ioctl(fd, UI_DEV_CREATE) < 0) { if (ioctl(fd, UI_DEV_SETUP, &us) < 0 || ioctl(fd, UI_DEV_CREATE) < 0) {
@@ -201,13 +201,38 @@ vmctl_t* vmctl_open_uinput_driver(const vmctl_config* cfg) {
v->ui_fd_a = -1; v->ui_fd_a = -1;
v->ui_fd_b = -1; v->ui_fd_b = -1;
/* HID identity: NULL config selects the built-in defaults verbatim; a
* non-NULL config supplies all numeric fields literally (zeros included). */
const vmctl_uinput_id DEFAULT_ID = {
HWID_BUS, HWID_VENDOR, HWID_PRODUCT, HWID_VERSION, HWID_NAME_A
};
const vmctl_uinput_id* id = cfg->uinput_id ? cfg->uinput_id : &DEFAULT_ID;
/* Base name: caller's non-empty name, else NULL = use default A/B names. */
const char* base = (cfg->uinput_id && cfg->uinput_id->name && cfg->uinput_id->name[0])
? cfg->uinput_id->name : NULL;
/* A/B suffix is added by the library only when two devices are created
* (VMCTL_PTR_BOTH) and only over a caller-supplied base name. */
char name_a[UINPUT_MAX_NAME_SIZE];
char name_b[UINPUT_MAX_NAME_SIZE];
const char* dev_a = base ? base : HWID_NAME_A;
const char* dev_b = HWID_NAME_B;
if (cfg->ptr_mode == VMCTL_PTR_BOTH && base) {
int base_max = (int)(sizeof name_a - 1 /*NUL*/ - 2 /*"-A"*/);
snprintf(name_a, sizeof name_a, "%.*s-A", base_max, base);
snprintf(name_b, sizeof name_b, "%.*s-B", base_max, base);
dev_a = name_a;
dev_b = name_b;
}
char evdev_a[64], evdev_b[64]; char evdev_a[64], evdev_b[64];
int rel_a = (cfg->ptr_mode == VMCTL_PTR_REL); int rel_a = (cfg->ptr_mode == VMCTL_PTR_REL);
v->ui_fd_a = uinput_create(rel_a, HWID_NAME_A, evdev_a); v->ui_fd_a = uinput_create(rel_a, id, dev_a, evdev_a);
if (v->ui_fd_a < 0) { free(v); return NULL; } if (v->ui_fd_a < 0) { free(v); return NULL; }
if (cfg->ptr_mode == VMCTL_PTR_BOTH) { if (cfg->ptr_mode == VMCTL_PTR_BOTH) {
v->ui_fd_b = uinput_create(1, HWID_NAME_B, evdev_b); v->ui_fd_b = uinput_create(1, id, dev_b, evdev_b);
if (v->ui_fd_b < 0) { if (v->ui_fd_b < 0) {
ioctl(v->ui_fd_a, UI_DEV_DESTROY); ioctl(v->ui_fd_a, UI_DEV_DESTROY);
close(v->ui_fd_a); close(v->ui_fd_a);