mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-25 20:36:36 +03:00
d6c45ddb04
The uinput devices the input adapter creates were never forwarded into the guest: the input-linux bridge was external (manual monitor object_add), so it was lost on every reconfigure and whenever the kernel-assigned device numbers changed. Make the vmhost seam -- which already owns the VM's single QMP connection -- add the input-linux objects itself on reaching READY (A=keyboard +abs with grab_all, B=mouse) and object_del them on teardown. The input adapter publishes the uinput evdev paths into a per-endpoint home; discovery attaches input before vmhost so the paths are ready. No second QMP socket or connection. Also move the mouse buttons (incl. middle) and the wheel onto device B, so B is a complete relative mouse and A is keyboard+abs only. Bump 0.3.8.
167 lines
5.3 KiB
C
167 lines
5.3 KiB
C
/* 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|