/* qmp_driver.c — QMP input driver: serialises an input batch into a single * input-send-event command and sends it in one round-trip. No guest driver is * required. Switches on vmctl_ev_kind (never on magic numbers). */ #include "driver.h" #include "keymap.h" #include #include static const char* btn_names[] = { "left", "right", "middle", "side", "extra", "forward", "back", "task" }; #define BTN_NAMES_LEN ((int)(sizeof btn_names / sizeof btn_names[0])) static int qmp_driver_send(vmctl_t* v, const vmctl_batch* b) { char json[8192]; int pos = 0; pos += snprintf(json + pos, (int)sizeof json - pos, "{\"execute\":\"input-send-event\",\"arguments\":{\"events\":["); for (int i = 0; i < b->count; i++) { if (i > 0) pos += snprintf(json + pos, (int)sizeof json - pos, ","); int code = b->ev[i].code; int value = b->ev[i].value; double scl = b->ev[i].scroll; switch ((vmctl_ev_kind)b->ev[i].kind) { case VMCTL_EV_ABS: pos += snprintf(json + pos, (int)sizeof json - pos, "{\"type\":\"abs\",\"data\":{\"axis\":\"%s\",\"value\":%d}}", code == VMCTL_AXIS_X ? "x" : "y", value); break; case VMCTL_EV_REL: pos += snprintf(json + pos, (int)sizeof json - pos, "{\"type\":\"rel\",\"data\":{\"axis\":\"%s\",\"value\":%d}}", code == VMCTL_AXIS_X ? "x" : "y", value); break; case VMCTL_EV_BTN: if (code < 0 || code >= BTN_NAMES_LEN) return -1; pos += snprintf(json + pos, (int)sizeof json - pos, "{\"type\":\"btn\",\"data\":{\"button\":\"%s\",\"down\":%s}}", btn_names[code], value ? "true" : "false"); break; case VMCTL_EV_KEY: { const char* qcode = vmctl_evdev_to_qcode(code); if (!qcode) return -1; pos += snprintf(json + pos, (int)sizeof json - pos, "{\"type\":\"key\",\"data\":{\"key\":{\"type\":\"qcode\"," "\"data\":\"%s\"},\"down\":%s}}", qcode, value ? "true" : "false"); break; } case VMCTL_EV_SCROLL: pos += snprintf(json + pos, (int)sizeof json - pos, "{\"type\":\"scl\",\"data\":{\"axis\":\"%s\",\"value\":%g}}", code == VMCTL_SCROLL_V ? "vertical" : "horizontal", scl); break; default: return -1; } } pos += snprintf(json + pos, (int)sizeof json - pos, "]}}"); char resp[4096]; return qmp_exec(v->qmp, json, resp, sizeof resp); } static void qmp_driver_close(vmctl_t* v) { qmp_disconnect(v->qmp); } vmctl_t* vmctl_open_qmp_driver(const vmctl_config* cfg) { qmp_conn* qmp = qmp_connect(cfg->qmp_path); if (!qmp) return NULL; vmctl_t* v = calloc(1, sizeof *v); if (!v) { qmp_disconnect(qmp); return NULL; } v->driver = VMCTL_DRIVER_QMP; v->qmp = qmp; v->ui_fd_a = -1; v->ui_fd_b = -1; v->ptr_mode = 0; v->ops.send = qmp_driver_send; v->ops.close = qmp_driver_close; return v; }