diff --git a/include/vmctl.h b/include/vmctl.h index e8302b8..30e24db 100644 --- a/include/vmctl.h +++ b/include/vmctl.h @@ -19,11 +19,20 @@ typedef enum { #define VMCTL_PTR_REL 2 /* uinput: relative mouse */ #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 { vmctl_driver driver; 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 */ int ptr_mode; /* UINPUT VMCTL_PTR_*; 0 for QMP */ + const vmctl_uinput_id* uinput_id; /* UINPUT only; NULL = built-in defaults */ } vmctl_config; vmctl_t* vmctl_open (const vmctl_config* cfg); /* NULL on error */ diff --git a/src/vmctl/linux/uinput_driver.c b/src/vmctl/linux/uinput_driver.c index d287c0e..d33ff5b 100644 --- a/src/vmctl/linux/uinput_driver.c +++ b/src/vmctl/linux/uinput_driver.c @@ -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 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); 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; memset(&us, 0, sizeof us); - us.id.bustype = HWID_BUS; - us.id.vendor = HWID_VENDOR; - us.id.product = HWID_PRODUCT; - us.id.version = HWID_VERSION; + us.id.bustype = (uint16_t)id->bustype; + us.id.vendor = (uint16_t)id->vendor; + us.id.product = (uint16_t)id->product; + us.id.version = (uint16_t)id->version; strncpy(us.name, name, sizeof us.name - 1); 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_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]; 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 (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) { ioctl(v->ui_fd_a, UI_DEV_DESTROY); close(v->ui_fd_a);