/* open.c — handle lifecycle and the input batch API. vmctl_open dispatches to a * driver factory by cfg->driver; vmctl_close releases via ops.close. The batch * builders set vmctl_event.kind (the single event-kind code that drivers read), * and the single-event wrappers are thin batches of one. */ #include "driver.h" #include #include vmctl_t* vmctl_open(const vmctl_config* cfg) { if (!cfg) return NULL; switch (cfg->driver) { case VMCTL_DRIVER_QMP: return vmctl_open_qmp_driver(cfg); case VMCTL_DRIVER_UINPUT: return vmctl_open_uinput_driver(cfg); default: return NULL; } } void vmctl_close(vmctl_t* v) { if (!v) return; v->ops.close(v); free(v); } /* ===== Batch builders ===== */ void vmctl_batch_init(vmctl_batch* b) { b->count = 0; } void vmctl_batch_abs(vmctl_batch* b, int axis, int value) { if (b->count >= VMCTL_BATCH_MAX) return; vmctl_event* e = &b->ev[b->count++]; e->kind = VMCTL_EV_ABS; e->code = axis; e->value = value; e->scroll = 0.0; } void vmctl_batch_rel(vmctl_batch* b, int axis, int delta) { if (b->count >= VMCTL_BATCH_MAX) return; vmctl_event* e = &b->ev[b->count++]; e->kind = VMCTL_EV_REL; e->code = axis; e->value = delta; e->scroll = 0.0; } void vmctl_batch_btn(vmctl_batch* b, int btn, int down) { if (b->count >= VMCTL_BATCH_MAX) return; vmctl_event* e = &b->ev[b->count++]; e->kind = VMCTL_EV_BTN; e->code = btn; e->value = down; e->scroll = 0.0; } void vmctl_batch_key(vmctl_batch* b, int evdev_code, int down) { if (b->count >= VMCTL_BATCH_MAX) return; vmctl_event* e = &b->ev[b->count++]; e->kind = VMCTL_EV_KEY; e->code = evdev_code; e->value = down; e->scroll = 0.0; } void vmctl_batch_scroll(vmctl_batch* b, int axis, double value) { if (b->count >= VMCTL_BATCH_MAX) return; vmctl_event* e = &b->ev[b->count++]; e->kind = VMCTL_EV_SCROLL; e->code = axis; e->value = 0; e->scroll = value; } int vmctl_batch_send(vmctl_t* v, vmctl_batch* b) { if (b->count == 0) return 0; int rc = v->ops.send(v, b); if (rc != 0) return rc; /* not sent = not recorded; never touch the receipt */ /* Record the actuated key/btn down-bits (write-only; the send path above * never reads this map). abs/rel/scroll have no held state. */ for (int i = 0; i < b->count; i++) { const vmctl_event* e = &b->ev[i]; int down = e->value ? 1 : 0; switch (e->kind) { case VMCTL_EV_KEY: { int code = e->code; if (code < 0 || code > VMCTL_KEY_CODE_MAX) break; /* out of range: ignore */ unsigned char mask = (unsigned char)(1u << (code & 7)); if (down) v->keys_held[code >> 3] |= mask; else v->keys_held[code >> 3] &= (unsigned char)~mask; break; } case VMCTL_EV_BTN: { int btn = e->code; if (btn < 0 || btn >= 8) break; /* out of range: ignore */ unsigned mask = 1u << btn; if (down) v->btns_held |= mask; else v->btns_held &= ~mask; break; } default: break; /* abs/rel/scroll: no-op for receipt */ } } return rc; } /* ===== Single-event wrappers ===== */ int vmctl_abs(vmctl_t* v, int axis, int value) { vmctl_batch b; vmctl_batch_init(&b); vmctl_batch_abs(&b, axis, value); return vmctl_batch_send(v, &b); } int vmctl_rel(vmctl_t* v, int axis, int delta) { vmctl_batch b; vmctl_batch_init(&b); vmctl_batch_rel(&b, axis, delta); return vmctl_batch_send(v, &b); } int vmctl_btn(vmctl_t* v, int btn, int down) { vmctl_batch b; vmctl_batch_init(&b); vmctl_batch_btn(&b, btn, down); return vmctl_batch_send(v, &b); } int vmctl_key(vmctl_t* v, int evdev_code, int down) { vmctl_batch b; vmctl_batch_init(&b); vmctl_batch_key(&b, evdev_code, down); return vmctl_batch_send(v, &b); } int vmctl_scroll(vmctl_t* v, int axis, double value) { vmctl_batch b; vmctl_batch_init(&b); vmctl_batch_scroll(&b, axis, value); return vmctl_batch_send(v, &b); } /* ===== Held-state receipt (read-only) ===== * Reads of the actuator's own last output; never mutate driver state. The * in-range predicate matches the write path in vmctl_batch_send. */ int vmctl_key_held(vmctl_t* v, int evdev_code) { if (!v || evdev_code < 0 || evdev_code > VMCTL_KEY_CODE_MAX) return 0; return (v->keys_held[evdev_code >> 3] >> (evdev_code & 7)) & 1; } int vmctl_btn_held(vmctl_t* v, int btn) { if (!v || btn < 0 || btn >= 8) return 0; return (int)((v->btns_held >> btn) & 1u); } int vmctl_keys_snapshot(vmctl_t* v, unsigned char* bits, size_t nbytes) { if (!v || !bits) return -1; size_t n = nbytes < VMCTL_KEYS_SNAPSHOT_BYTES ? nbytes : VMCTL_KEYS_SNAPSHOT_BYTES; memcpy(bits, v->keys_held, n); return (int)n; } unsigned vmctl_btns_snapshot(vmctl_t* v) { if (!v) return 0; return v->btns_held; } /* ===== uinput evdev export (UINPUT-only) ===== */ int vmctl_uinput_evdev(vmctl_t* v, char a[64], char b[64]) { if (!v || v->driver != VMCTL_DRIVER_UINPUT) return -1; int n = 0; if (a) { memcpy(a, v->ui_evdev_a, sizeof v->ui_evdev_a); if (a[0]) n++; } if (b) { memcpy(b, v->ui_evdev_b, sizeof v->ui_evdev_b); if (b[0]) n++; } return n; }