mirror of
https://dev.lirent.ru/Vatrog/vm-control-io.git
synced 2026-07-08 22:16:37 +03:00
vmctl: read-only held-state receipt for keys and pointer buttons
Add vmctl_key_held, vmctl_btn_held, vmctl_keys_snapshot and vmctl_btns_snapshot, exposing the down-state each handle last actuated. Two bitmaps in the handle (keys by evdev KEY_*, buttons by VMCTL_BTN_*) are updated after a successful send in vmctl_batch_send; the send path never reads them. VMCTL_KEY_CODE_MAX and VMCTL_KEYS_SNAPSHOT_BYTES give callers the keys-snapshot buffer size.
This commit is contained in:
@@ -53,6 +53,9 @@ void vmctl_close(vmctl_t* v); /* safe on NULL */
|
|||||||
#define VMCTL_BTN_BACK 6
|
#define VMCTL_BTN_BACK 6
|
||||||
#define VMCTL_BTN_TASK 7
|
#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) ===== */
|
/* ===== Event batch (value-type, stack; build ONLY via builders — ev[] is not API) ===== */
|
||||||
#define VMCTL_BATCH_MAX 64
|
#define VMCTL_BATCH_MAX 64
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -78,6 +81,19 @@ 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_key (vmctl_t* v, int evdev_code, int down); /* Linux KEY_* */
|
||||||
int vmctl_scroll(vmctl_t* v, int axis, double value); /* VMCTL_SCROLL_* */
|
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) ===== */
|
/* ===== 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_powerdown(vmctl_t* v); /* system_powerdown (ACPI soft-off) */
|
||||||
int vmctl_reset (vmctl_t* v); /* system_reset */
|
int vmctl_reset (vmctl_t* v); /* system_reset */
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ struct vmctl {
|
|||||||
int ui_fd_a; /* uinput driver: device A; -1 for QMP */
|
int ui_fd_a; /* uinput driver: device A; -1 for QMP */
|
||||||
int ui_fd_b; /* uinput driver: device B (BOTH); -1 */
|
int ui_fd_b; /* uinput driver: device B (BOTH); -1 */
|
||||||
int ptr_mode; /* uinput driver: VMCTL_PTR_*; 0 for QMP */
|
int ptr_mode; /* uinput driver: VMCTL_PTR_*; 0 for QMP */
|
||||||
|
|
||||||
|
/* Held-state receipt: key/btn down-bits as THIS handle last actuated them
|
||||||
|
* (not guest truth). Written only after a successful send in
|
||||||
|
* vmctl_batch_send; the send path never reads them. Zero-initialised by
|
||||||
|
* calloc at open = all up. Single-threaded (one handle owner): no locks. */
|
||||||
|
unsigned char keys_held[VMCTL_KEYS_SNAPSHOT_BYTES]; /* evdev-indexed key down-bits */
|
||||||
|
unsigned btns_held; /* VMCTL_BTN_* 0..7 down-bits */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* driver factories (called from open.c per cfg->driver) */
|
/* driver factories (called from open.c per cfg->driver) */
|
||||||
|
|||||||
+56
-1
@@ -6,6 +6,7 @@
|
|||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
vmctl_t* vmctl_open(const vmctl_config* cfg) {
|
vmctl_t* vmctl_open(const vmctl_config* cfg) {
|
||||||
if (!cfg) return NULL;
|
if (!cfg) return NULL;
|
||||||
@@ -60,7 +61,35 @@ void vmctl_batch_scroll(vmctl_batch* b, int axis, double value) {
|
|||||||
|
|
||||||
int vmctl_batch_send(vmctl_t* v, vmctl_batch* b) {
|
int vmctl_batch_send(vmctl_t* v, vmctl_batch* b) {
|
||||||
if (b->count == 0) return 0;
|
if (b->count == 0) return 0;
|
||||||
return v->ops.send(v, b);
|
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 ===== */
|
/* ===== Single-event wrappers ===== */
|
||||||
@@ -99,3 +128,29 @@ int vmctl_scroll(vmctl_t* v, int axis, double value) {
|
|||||||
vmctl_batch_scroll(&b, axis, value);
|
vmctl_batch_scroll(&b, axis, value);
|
||||||
return vmctl_batch_send(v, &b);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user