X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/5bc8f026dd5eb76c89395933d38dc5b0cebf5763..7ffe3124edd4cfb68870f62263cd9830b68a7a46:/ui/input.c diff --git a/ui/input.c b/ui/input.c index fc91fba83c..ed88cda6d6 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,22 +1,47 @@ +#include "qemu/osdep.h" +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qapi-types.h" +#include "qemu/error-report.h" #include "qmp-commands.h" #include "trace.h" #include "ui/input.h" #include "ui/console.h" +#include "sysemu/replay.h" struct QemuInputHandlerState { DeviceState *dev; QemuInputHandler *handler; int id; int events; + QemuConsole *con; QTAILQ_ENTRY(QemuInputHandlerState) node; }; + +typedef struct QemuInputEventQueue QemuInputEventQueue; +struct QemuInputEventQueue { + enum { + QEMU_INPUT_QUEUE_DELAY = 1, + QEMU_INPUT_QUEUE_EVENT, + QEMU_INPUT_QUEUE_SYNC, + } type; + QEMUTimer *timer; + uint32_t delay_ms; + QemuConsole *src; + InputEvent *evt; + QTAILQ_ENTRY(QemuInputEventQueue) node; +}; + static QTAILQ_HEAD(, QemuInputHandlerState) handlers = QTAILQ_HEAD_INITIALIZER(handlers); static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); +static QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) kbd_queue = + QTAILQ_HEAD_INITIALIZER(kbd_queue); +static QEMUTimer *kbd_timer; +static uint32_t kbd_default_delay_ms = 10; + QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, QemuInputHandler *handler) { @@ -53,12 +78,40 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s) qemu_input_check_mode_change(); } +void qemu_input_handler_bind(QemuInputHandlerState *s, + const char *device_id, int head, + Error **errp) +{ + QemuConsole *con; + Error *err = NULL; + + con = qemu_console_lookup_by_device_name(device_id, head, &err); + if (err) { + error_propagate(errp, err); + return; + } + + s->con = con; +} + static QemuInputHandlerState* -qemu_input_find_handler(uint32_t mask) +qemu_input_find_handler(uint32_t mask, QemuConsole *con) { QemuInputHandlerState *s; QTAILQ_FOREACH(s, &handlers, node) { + if (s->con == NULL || s->con != con) { + continue; + } + if (mask & s->handler->mask) { + return s; + } + } + + QTAILQ_FOREACH(s, &handlers, node) { + if (s->con != NULL) { + continue; + } if (mask & s->handler->mask) { return s; } @@ -66,26 +119,72 @@ qemu_input_find_handler(uint32_t mask) return NULL; } +void qmp_input_send_event(bool has_device, const char *device, + bool has_head, int64_t head, + InputEventList *events, Error **errp) +{ + InputEventList *e; + QemuConsole *con; + Error *err = NULL; + + con = NULL; + if (has_device) { + if (!has_head) { + head = 0; + } + con = qemu_console_lookup_by_device_name(device, head, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + error_setg(errp, "VM not running"); + return; + } + + for (e = events; e != NULL; e = e->next) { + InputEvent *event = e->value; + + if (!qemu_input_find_handler(1 << event->type, con)) { + error_setg(errp, "Input handler not found for " + "event type %s", + InputEventKind_lookup[event->type]); + return; + } + } + + for (e = events; e != NULL; e = e->next) { + InputEvent *event = e->value; + + qemu_input_event_send(con, event); + } + + qemu_input_event_sync(); +} + static void qemu_input_transform_abs_rotate(InputEvent *evt) { + InputMoveEvent *move = evt->u.abs.data; switch (graphic_rotate) { case 90: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + if (move->axis == INPUT_AXIS_X) { + move->axis = INPUT_AXIS_Y; + } else if (move->axis == INPUT_AXIS_Y) { + move->axis = INPUT_AXIS_X; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; } break; case 180: - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; break; case 270: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; + if (move->axis == INPUT_AXIS_X) { + move->axis = INPUT_AXIS_Y; + move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value; + } else if (move->axis == INPUT_AXIS_Y) { + move->axis = INPUT_AXIS_X; } break; } @@ -94,62 +193,134 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt) static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { const char *name; - int idx = -1; + int qcode, idx = -1; + InputKeyEvent *key; + InputBtnEvent *btn; + InputMoveEvent *move; if (src) { idx = qemu_console_get_index(src); } - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - switch (evt->key->key->kind) { + key = evt->u.key.data; + switch (key->key->type) { case KEY_VALUE_KIND_NUMBER: - trace_input_event_key_number(idx, evt->key->key->number, - evt->key->down); + qcode = qemu_input_key_number_to_qcode(key->key->u.number.data); + name = QKeyCode_lookup[qcode]; + trace_input_event_key_number(idx, key->key->u.number.data, + name, key->down); break; case KEY_VALUE_KIND_QCODE: - name = QKeyCode_lookup[evt->key->key->qcode]; - trace_input_event_key_qcode(idx, name, evt->key->down); + name = QKeyCode_lookup[key->key->u.qcode.data]; + trace_input_event_key_qcode(idx, name, key->down); break; - case KEY_VALUE_KIND_MAX: + case KEY_VALUE_KIND__MAX: /* keep gcc happy */ break; } break; case INPUT_EVENT_KIND_BTN: - name = InputButton_lookup[evt->btn->button]; - trace_input_event_btn(idx, name, evt->btn->down); + btn = evt->u.btn.data; + name = InputButton_lookup[btn->button]; + trace_input_event_btn(idx, name, btn->down); break; case INPUT_EVENT_KIND_REL: - name = InputAxis_lookup[evt->rel->axis]; - trace_input_event_rel(idx, name, evt->rel->value); + move = evt->u.rel.data; + name = InputAxis_lookup[move->axis]; + trace_input_event_rel(idx, name, move->value); break; case INPUT_EVENT_KIND_ABS: - name = InputAxis_lookup[evt->abs->axis]; - trace_input_event_abs(idx, name, evt->abs->value); + move = evt->u.abs.data; + name = InputAxis_lookup[move->axis]; + trace_input_event_abs(idx, name, move->value); break; - case INPUT_EVENT_KIND_MAX: + case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; } } -void qemu_input_event_send(QemuConsole *src, InputEvent *evt) +static void qemu_input_queue_process(void *opaque) { - QemuInputHandlerState *s; + struct QemuInputEventQueueHead *queue = opaque; + QemuInputEventQueue *item; + + g_assert(!QTAILQ_EMPTY(queue)); + item = QTAILQ_FIRST(queue); + g_assert(item->type == QEMU_INPUT_QUEUE_DELAY); + QTAILQ_REMOVE(queue, item, node); + g_free(item); + + while (!QTAILQ_EMPTY(queue)) { + item = QTAILQ_FIRST(queue); + switch (item->type) { + case QEMU_INPUT_QUEUE_DELAY: + timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + item->delay_ms); + return; + case QEMU_INPUT_QUEUE_EVENT: + qemu_input_event_send(item->src, item->evt); + qapi_free_InputEvent(item->evt); + break; + case QEMU_INPUT_QUEUE_SYNC: + qemu_input_event_sync(); + break; + } + QTAILQ_REMOVE(queue, item, node); + g_free(item); + } +} - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; +static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue, + QEMUTimer *timer, uint32_t delay_ms) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + bool start_timer = QTAILQ_EMPTY(queue); + + item->type = QEMU_INPUT_QUEUE_DELAY; + item->delay_ms = delay_ms; + item->timer = timer; + QTAILQ_INSERT_TAIL(queue, item, node); + + if (start_timer) { + timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + item->delay_ms); } +} + +static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue, + QemuConsole *src, InputEvent *evt) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + + item->type = QEMU_INPUT_QUEUE_EVENT; + item->src = src; + item->evt = evt; + QTAILQ_INSERT_TAIL(queue, item, node); +} + +static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + + item->type = QEMU_INPUT_QUEUE_SYNC; + QTAILQ_INSERT_TAIL(queue, item, node); +} + +void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) +{ + QemuInputHandlerState *s; qemu_input_event_trace(src, evt); /* pre processing */ - if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { + if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) { qemu_input_transform_abs_rotate(evt); } /* send event */ - s = qemu_input_find_handler(1 << evt->kind); + s = qemu_input_find_handler(1 << evt->type, src); if (!s) { return; } @@ -157,14 +328,19 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) s->events++; } -void qemu_input_event_sync(void) +void qemu_input_event_send(QemuConsole *src, InputEvent *evt) { - QemuInputHandlerState *s; - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { return; } + replay_input_event(src, evt); +} + +void qemu_input_event_sync_impl(void) +{ + QemuInputHandlerState *s; + trace_input_event_sync(); QTAILQ_FOREACH(s, &handlers, node) { @@ -178,13 +354,22 @@ void qemu_input_event_sync(void) } } +void qemu_input_event_sync(void) +{ + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } + + replay_input_sync_event(); +} + InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->key = g_new0(InputKeyEvent, 1); - evt->kind = INPUT_EVENT_KIND_KEY; - evt->key->key = key; - evt->key->down = down; + evt->u.key.data = g_new0(InputKeyEvent, 1); + evt->type = INPUT_EVENT_KIND_KEY; + evt->u.key.data->key = key; + evt->u.key.data->down = down; return evt; } @@ -192,34 +377,49 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { InputEvent *evt; evt = qemu_input_event_new_key(key, down); - qemu_input_event_send(src, evt); - qemu_input_event_sync(); - qapi_free_InputEvent(evt); + if (QTAILQ_EMPTY(&kbd_queue)) { + qemu_input_event_send(src, evt); + qemu_input_event_sync(); + qapi_free_InputEvent(evt); + } else { + qemu_input_queue_event(&kbd_queue, src, evt); + qemu_input_queue_sync(&kbd_queue); + } } void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_NUMBER; - key->number = num; + key->type = KEY_VALUE_KIND_NUMBER; + key->u.number.data = num; qemu_input_event_send_key(src, key, down); } void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_QCODE; - key->qcode = q; + key->type = KEY_VALUE_KIND_QCODE; + key->u.qcode.data = q; qemu_input_event_send_key(src, key, down); } +void qemu_input_event_send_key_delay(uint32_t delay_ms) +{ + if (!kbd_timer) { + kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process, + &kbd_queue); + } + qemu_input_queue_delay(&kbd_queue, kbd_timer, + delay_ms ? delay_ms : kbd_default_delay_ms); +} + InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->btn = g_new0(InputBtnEvent, 1); - evt->kind = INPUT_EVENT_KIND_BTN; - evt->btn->button = btn; - evt->btn->down = down; + evt->u.btn.data = g_new0(InputBtnEvent, 1); + evt->type = INPUT_EVENT_KIND_BTN; + evt->u.btn.data->button = btn; + evt->u.btn.data->down = down; return evt; } @@ -237,7 +437,7 @@ void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, InputButton btn; uint32_t mask; - for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) { + for (btn = 0; btn < INPUT_BUTTON__MAX; btn++) { mask = button_map[btn]; if ((button_old & mask) == (button_new & mask)) { continue; @@ -250,7 +450,8 @@ bool qemu_input_is_absolute(void) { QemuInputHandlerState *s; - s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS); + s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS, + NULL); return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); } @@ -268,8 +469,8 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind, InputEvent *evt = g_new0(InputEvent, 1); InputMoveEvent *move = g_new0(InputMoveEvent, 1); - evt->kind = kind; - evt->data = move; + evt->type = kind; + evt->u.rel.data = move; /* evt->u.rel is the same as evt->u.abs */ move->axis = axis; move->value = value; return evt; @@ -345,7 +546,7 @@ MouseInfoList *qmp_query_mice(Error **errp) return mice_list; } -void do_mouse_set(Monitor *mon, const QDict *qdict) +void hmp_mouse_set(Monitor *mon, const QDict *qdict) { QemuInputHandlerState *s; int index = qdict_get_int(qdict, "index");