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.
115 lines
5.8 KiB
C
115 lines
5.8 KiB
C
#ifndef VMCTL_H
|
|
#define VMCTL_H
|
|
#include <stddef.h>
|
|
|
|
/* vmctl.h — public API for a QEMU VM Input layer (actuator): input injection +
|
|
* power/lifecycle actuation. One handle; the input driver is selected
|
|
* declaratively through vmctl_config. OS-agnostic surface. */
|
|
|
|
typedef struct vmctl vmctl_t; /* opaque handle */
|
|
|
|
/* ===== Input drivers + open ===== */
|
|
typedef enum {
|
|
VMCTL_DRIVER_QMP, /* QMP input-send-event (no guest driver required) */
|
|
VMCTL_DRIVER_UINPUT /* host uinput source; optional passthrough into guest */
|
|
/* via QEMU virtio-input-host-pci (Linux). uinput != virtio. */
|
|
} vmctl_driver;
|
|
|
|
#define VMCTL_PTR_ABS 1 /* uinput: absolute tablet */
|
|
#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 */
|
|
void vmctl_close(vmctl_t* v); /* safe on NULL */
|
|
|
|
/* Copy the host evdev node paths of the created uinput devices (UINPUT driver only).
|
|
* a[] receives device A, b[] receives device B (empty if not VMCTL_PTR_BOTH); each buffer
|
|
* must be >=64 bytes. Returns the count of non-empty paths filled (0/1/2), or -1 if the
|
|
* handle's driver is not UINPUT. Paths are valid while the handle is open. */
|
|
int vmctl_uinput_evdev(vmctl_t* v, char a[64], char b[64]);
|
|
|
|
/* ===== Input constants ===== */
|
|
#define VMCTL_ABS_MAX 32767 /* abs coordinates 0..VMCTL_ABS_MAX */
|
|
#define VMCTL_AXIS_X 0
|
|
#define VMCTL_AXIS_Y 1
|
|
#define VMCTL_SCROLL_V 0 /* vertical */
|
|
#define VMCTL_SCROLL_H 1 /* horizontal */
|
|
#define VMCTL_BTN_LEFT 0
|
|
#define VMCTL_BTN_RIGHT 1
|
|
#define VMCTL_BTN_MIDDLE 2
|
|
#define VMCTL_BTN_SIDE 3
|
|
#define VMCTL_BTN_EXTRA 4
|
|
#define VMCTL_BTN_FORWARD 5
|
|
#define VMCTL_BTN_BACK 6
|
|
#define VMCTL_BTN_TASK 7
|
|
|
|
#define VMCTL_KEY_CODE_MAX 0x2ff /* highest supported evdev key code (inclusive) */
|
|
#define VMCTL_KEYS_SNAPSHOT_BYTES ((VMCTL_KEY_CODE_MAX + 1) / 8) /* bytes for vmctl_keys_snapshot */
|
|
|
|
/* ===== Event batch (value-type, stack; build ONLY via builders — ev[] is not API) ===== */
|
|
#define VMCTL_BATCH_MAX 64
|
|
typedef struct {
|
|
int kind; /* internal event-kind code; set by builders */
|
|
int code; /* axis / button / evdev-code (per kind) */
|
|
int value; /* abs-value / rel-delta / down(0|1) */
|
|
double scroll; /* scroll magnitude (scroll only) */
|
|
} vmctl_event;
|
|
typedef struct { vmctl_event ev[VMCTL_BATCH_MAX]; int count; } vmctl_batch;
|
|
|
|
void vmctl_batch_init (vmctl_batch* b);
|
|
void vmctl_batch_abs (vmctl_batch* b, int axis, int value);
|
|
void vmctl_batch_rel (vmctl_batch* b, int axis, int delta);
|
|
void vmctl_batch_btn (vmctl_batch* b, int btn, int down);
|
|
void vmctl_batch_key (vmctl_batch* b, int evdev_code, int down);
|
|
void vmctl_batch_scroll(vmctl_batch* b, int axis, double value);
|
|
int vmctl_batch_send (vmctl_t* v, vmctl_batch* b); /* one round-trip; 0=ok, -1=err */
|
|
|
|
/* ===== Single events (wrappers over a 1-event batch) ===== */
|
|
int vmctl_abs (vmctl_t* v, int axis, int value); /* 0..VMCTL_ABS_MAX */
|
|
int vmctl_rel (vmctl_t* v, int axis, int delta);
|
|
int vmctl_btn (vmctl_t* v, int btn, int down); /* VMCTL_BTN_* */
|
|
int vmctl_key (vmctl_t* v, int evdev_code, int down); /* Linux KEY_* */
|
|
int vmctl_scroll(vmctl_t* v, int axis, double value); /* VMCTL_SCROLL_* */
|
|
|
|
/* ===== Held-state receipt (read-only) =====
|
|
* "held" = key/button state as THIS handle last actuated it, not guest truth.
|
|
* It is the actuator's record of its own last output (sensing the guest belongs
|
|
* to the sensors layer, not here). Updated only after a successful send; the
|
|
* send path NEVER reads this map (no dedup, no auto-release, no autorepeat). */
|
|
|
|
int vmctl_key_held (vmctl_t* v, int evdev_code); /* Linux KEY_*; 1=down 0=up */
|
|
int vmctl_btn_held (vmctl_t* v, int btn); /* VMCTL_BTN_*; 1=down 0=up */
|
|
int vmctl_keys_snapshot(vmctl_t* v, unsigned char* bits, size_t nbytes);
|
|
/* copy key down-bits (EVIOCGKEY-style);
|
|
returns bytes written, -1 on bad args */
|
|
unsigned vmctl_btns_snapshot(vmctl_t* v); /* VMCTL_BTN_* down-bits as a mask (bits 0..7) */
|
|
|
|
/* ===== Power/lifecycle actuation (requires a QMP connection; -1 if there is none) ===== */
|
|
int vmctl_powerdown(vmctl_t* v); /* system_powerdown (ACPI soft-off) */
|
|
int vmctl_reset (vmctl_t* v); /* system_reset */
|
|
int vmctl_wakeup (vmctl_t* v); /* system_wakeup (from S3/S4) */
|
|
int vmctl_pause (vmctl_t* v); /* stop */
|
|
int vmctl_resume (vmctl_t* v); /* cont */
|
|
|
|
/* Transfer sequencing/context belongs to signaling; timing and decisions to
|
|
* control; reading VM state to sensors. Here, in the Input layer, only atomic
|
|
* actuation. */
|
|
|
|
#endif /* VMCTL_H */
|