From f5c0ab131265270c1e7852ec0d4e284a219d63d4 Mon Sep 17 00:00:00 2001 From: Andrew Oates Date: Fri, 23 May 2014 20:16:09 -0400 Subject: [PATCH 01/10] input (curses): mask keycodes to remove modifier bits Without the mask, control bits are passed on in the keycode, generating incorrect PS/2 sequences when SHIFT, ALT, etc are held down. Cc: qemu-stable@nongnu.org Signed-off-by: Andrew Oates Signed-off-by: Gerd Hoffmann --- ui/curses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/curses.c b/ui/curses.c index b044790e43..de85f7610f 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -288,8 +288,8 @@ static void curses_refresh(DisplayChangeListener *dcl) qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); } - qemu_input_event_send_key_number(NULL, keycode, true); - qemu_input_event_send_key_number(NULL, keycode, false); + qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true); + qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false); if (keycode & ALTGR) { qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); From 11c7fa7fa602704d9673ea59c575f58777008d93 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 21 May 2014 13:28:32 +0200 Subject: [PATCH 02/10] input: add qemu_input_key_number_to_qcode Signed-off-by: Gerd Hoffmann --- include/ui/input.h | 1 + ui/input-keymap.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/ui/input.h b/include/ui/input.h index 3d3d487f18..d84ed8b41c 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -36,6 +36,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); +int qemu_input_key_number_to_qcode(uint8_t nr); int qemu_input_key_value_to_number(const KeyValue *value); int qemu_input_key_value_to_qcode(const KeyValue *value); int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 6da4495103..4c4f0d03a9 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -129,7 +129,7 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_MAX] = 0, }; -static int number_to_qcode[0xff]; +static int number_to_qcode[0x100]; int qemu_input_key_value_to_number(const KeyValue *value) { @@ -141,7 +141,7 @@ int qemu_input_key_value_to_number(const KeyValue *value) } } -int qemu_input_key_value_to_qcode(const KeyValue *value) +int qemu_input_key_number_to_qcode(uint8_t nr) { static int first = true; @@ -155,11 +155,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value) } } + return number_to_qcode[nr]; +} + +int qemu_input_key_value_to_qcode(const KeyValue *value) +{ if (value->kind == KEY_VALUE_KIND_QCODE) { return value->qcode; } else { assert(value->kind == KEY_VALUE_KIND_NUMBER); - return number_to_qcode[value->number]; + return qemu_input_key_number_to_qcode(value->number); } } From 2386a907301134d344649426e6928614567d1235 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 21 May 2014 13:49:59 +0200 Subject: [PATCH 03/10] input: add name to input_event_key_number Signed-off-by: Gerd Hoffmann --- trace-events | 2 +- ui/input.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/trace-events b/trace-events index b6d289d720..0870204736 100644 --- a/trace-events +++ b/trace-events @@ -1050,7 +1050,7 @@ gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" # ui/input.c -input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d" +input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d" input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" diff --git a/ui/input.c b/ui/input.c index fc91fba83c..9d630355e8 100644 --- a/ui/input.c +++ b/ui/input.c @@ -94,7 +94,7 @@ 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; if (src) { idx = qemu_console_get_index(src); @@ -103,8 +103,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) case INPUT_EVENT_KIND_KEY: switch (evt->key->key->kind) { case KEY_VALUE_KIND_NUMBER: + qcode = qemu_input_key_number_to_qcode(evt->key->key->number); + name = QKeyCode_lookup[qcode]; trace_input_event_key_number(idx, evt->key->key->number, - evt->key->down); + name, evt->key->down); break; case KEY_VALUE_KIND_QCODE: name = QKeyCode_lookup[evt->key->key->qcode]; From 86846bfe64a0104df081226711804b8517258108 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 21 May 2014 13:54:32 +0200 Subject: [PATCH 04/10] input: keymap: add meta keys Signed-off-by: Gerd Hoffmann --- ui/input-keymap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 4c4f0d03a9..5d299353a8 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -13,6 +13,8 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_CTRL] = 0x1d, [Q_KEY_CODE_CTRL_R] = 0x9d, + [Q_KEY_CODE_META_L] = 0xdb, + [Q_KEY_CODE_META_R] = 0xdc, [Q_KEY_CODE_MENU] = 0xdd, [Q_KEY_CODE_ESC] = 0x01, From 1ff5eedd1d0facf94b2f272058b83856b361b079 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 11 Mar 2014 13:52:27 +0100 Subject: [PATCH 05/10] input: switch hid keyboard to new input layer api. Minimal patch to get the switchover done. We continue processing ps/2 scancodes for now as they are part of the live migration stream. Fixing that, then mapping directly from QKeyValue to HID keycodes is left as excercise for another day. Signed-off-by: Gerd Hoffmann --- hw/input/hid.c | 29 ++++++++++++++++++++++------- include/hw/input/hid.h | 3 ++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/hw/input/hid.c b/hw/input/hid.c index bb0fa6a619..ff75e41a46 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -158,17 +158,24 @@ static void hid_pointer_event(void *opaque, hs->event(hs); } -static void hid_keyboard_event(void *opaque, int keycode) +static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - HIDState *hs = opaque; + HIDState *hs = (HIDState *)dev; + int scancodes[3], i, count; int slot; - if (hs->n == QUEUE_LENGTH) { + count = qemu_input_key_value_to_scancode(evt->key->key, + evt->key->down, + scancodes); + if (hs->n + count > QUEUE_LENGTH) { fprintf(stderr, "usb-kbd: warning: key event queue full\n"); return; } - slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - hs->kbd.keycodes[slot] = keycode; + for (i = 0; i < count; i++) { + slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; + hs->kbd.keycodes[slot] = scancodes[i]; + } hs->event(hs); } @@ -415,7 +422,7 @@ void hid_free(HIDState *hs) { switch (hs->kind) { case HID_KEYBOARD: - qemu_remove_kbd_event_handler(hs->kbd.eh_entry); + qemu_input_handler_unregister(hs->s); break; case HID_MOUSE: case HID_TABLET: @@ -425,13 +432,21 @@ void hid_free(HIDState *hs) hid_del_idle_timer(hs); } +static QemuInputHandler hid_keyboard_handler = { + .name = "QEMU HID Keyboard", + .mask = INPUT_EVENT_MASK_KEY, + .event = hid_keyboard_event, +}; + void hid_init(HIDState *hs, int kind, HIDEventFunc event) { hs->kind = kind; hs->event = event; if (hs->kind == HID_KEYBOARD) { - hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs); + hs->s = qemu_input_handler_register((DeviceState *)hs, + &hid_keyboard_handler); + qemu_input_handler_activate(hs->s); } else if (hs->kind == HID_MOUSE) { hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, 0, "QEMU HID Mouse"); diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h index 2567879399..fb913ba3bb 100644 --- a/include/hw/input/hid.h +++ b/include/hw/input/hid.h @@ -2,6 +2,7 @@ #define QEMU_HID_H #include "migration/vmstate.h" +#include "ui/input.h" #define HID_MOUSE 1 #define HID_TABLET 2 @@ -31,7 +32,6 @@ typedef struct HIDKeyboardState { uint8_t leds; uint8_t key[16]; int32_t keys; - QEMUPutKbdEntry *eh_entry; } HIDKeyboardState; struct HIDState { @@ -47,6 +47,7 @@ struct HIDState { bool idle_pending; QEMUTimer *idle_timer; HIDEventFunc event; + QemuInputHandlerState *s; }; void hid_init(HIDState *hs, int kind, HIDEventFunc event); From 8b84286f4cd0ff5dbf367dc55cf20d9762ac5634 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 19 May 2014 15:02:21 +0200 Subject: [PATCH 06/10] input: switch hid mouse and tablet to the new input layer api. Signed-off-by: Gerd Hoffmann --- hw/input/hid.c | 195 ++++++++++++++++++++++++++--------------- include/hw/input/hid.h | 1 - 2 files changed, 124 insertions(+), 72 deletions(-) diff --git a/hw/input/hid.c b/hw/input/hid.c index ff75e41a46..295bdab652 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -105,57 +105,115 @@ void hid_set_next_idle(HIDState *hs) } } -static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) +static void hid_pointer_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - e->xdx = e->ydy = e->dz = 0; - e->buttons_state = buttons; + static const int bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = 0x01, + [INPUT_BUTTON_RIGHT] = 0x02, + [INPUT_BUTTON_MIDDLE] = 0x04, + }; + HIDState *hs = (HIDState *)dev; + HIDPointerEvent *e; + + assert(hs->n < QUEUE_LENGTH); + e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; + + switch (evt->kind) { + case INPUT_EVENT_KIND_REL: + if (evt->rel->axis == INPUT_AXIS_X) { + e->xdx += evt->rel->value; + } else if (evt->rel->axis == INPUT_AXIS_Y) { + e->ydy -= evt->rel->value; + } + break; + + case INPUT_EVENT_KIND_ABS: + if (evt->rel->axis == INPUT_AXIS_X) { + e->xdx = evt->rel->value; + } else if (evt->rel->axis == INPUT_AXIS_Y) { + e->ydy = evt->rel->value; + } + break; + + case INPUT_EVENT_KIND_BTN: + if (evt->btn->down) { + e->buttons_state |= bmap[evt->btn->button]; + if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + e->dz--; + } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + e->dz++; + } + } else { + e->buttons_state &= ~bmap[evt->btn->button]; + } + break; + + default: + /* keep gcc happy */ + break; + } + } -static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, - int x1, int y1, int z1) { - if (xyrel) { - e->xdx += x1; - e->ydy += y1; - } else { - e->xdx = x1; - e->ydy = y1; - /* Windows drivers do not like the 0/0 position and ignore such - * events. */ - if (!(x1 | y1)) { - e->xdx = 1; +static void hid_pointer_sync(DeviceState *dev) +{ + HIDState *hs = (HIDState *)dev; + HIDPointerEvent *prev, *curr, *next; + bool event_compression = false; + + if (hs->n == QUEUE_LENGTH-1) { + /* + * Queue full. We are loosing information, but we at least + * keep track of most recent button state. + */ + return; + } + + prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK]; + curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; + next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK]; + + if (hs->n > 0) { + /* + * No button state change between previous and current event + * (and previous wasn't seen by the guest yet), so there is + * motion information only and we can combine the two event + * into one. + */ + if (curr->buttons_state == prev->buttons_state) { + event_compression = true; } } - e->dz += z1; -} -static void hid_pointer_event(void *opaque, - int x1, int y1, int z1, int buttons_state) -{ - HIDState *hs = opaque; - unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; - unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; - - /* We combine events where feasible to keep the queue small. We shouldn't - * combine anything with the first event of a particular button state, as - * that would change the location of the button state change. When the - * queue is empty, a second event is needed because we don't know if - * the first event changed the button state. */ - if (hs->n == QUEUE_LENGTH) { - /* Queue full. Discard old button state, combine motion normally. */ - hs->ptr.queue[use_slot].buttons_state = buttons_state; - } else if (hs->n < 2 || - hs->ptr.queue[use_slot].buttons_state != buttons_state || - hs->ptr.queue[previous_slot].buttons_state != - hs->ptr.queue[use_slot].buttons_state) { - /* Cannot or should not combine, so add an empty item to the queue. */ - QUEUE_INCR(use_slot); + if (event_compression) { + /* add current motion to previous, clear current */ + if (hs->kind == HID_MOUSE) { + prev->xdx += curr->xdx; + curr->xdx = 0; + prev->ydy -= curr->ydy; + curr->ydy = 0; + } else { + prev->xdx = curr->xdx; + prev->ydy = curr->ydy; + } + prev->dz += curr->dz; + curr->dz = 0; + } else { + /* prepate next (clear rel, copy abs + btns) */ + if (hs->kind == HID_MOUSE) { + next->xdx = 0; + next->ydy = 0; + } else { + next->xdx = curr->xdx; + next->ydy = curr->ydy; + } + next->dz = 0; + next->buttons_state = curr->buttons_state; + /* make current guest visible, notify guest */ hs->n++; - hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); + hs->event(hs); } - hid_pointer_event_combine(&hs->ptr.queue[use_slot], - hs->kind == HID_MOUSE, - x1, y1, z1); - hs->event(hs); } static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, @@ -254,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax) void hid_pointer_activate(HIDState *hs) { if (!hs->ptr.mouse_grabbed) { - qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + qemu_input_handler_activate(hs->s); hs->ptr.mouse_grabbed = 1; } } int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) { - int dx, dy, dz, b, l; + int dx, dy, dz, l; int index; HIDPointerEvent *e; @@ -286,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) dz = int_clamp(e->dz, -127, 127); e->dz -= dz; - b = 0; - if (e->buttons_state & MOUSE_EVENT_LBUTTON) { - b |= 0x01; - } - if (e->buttons_state & MOUSE_EVENT_RBUTTON) { - b |= 0x02; - } - if (e->buttons_state & MOUSE_EVENT_MBUTTON) { - b |= 0x04; - } - if (hs->n && !e->dz && (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { @@ -311,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) switch (hs->kind) { case HID_MOUSE: if (len > l) { - buf[l++] = b; + buf[l++] = e->buttons_state; } if (len > l) { buf[l++] = dx; @@ -326,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) case HID_TABLET: if (len > l) { - buf[l++] = b; + buf[l++] = e->buttons_state; } if (len > l) { buf[l++] = dx & 0xff; @@ -420,15 +467,7 @@ void hid_reset(HIDState *hs) void hid_free(HIDState *hs) { - switch (hs->kind) { - case HID_KEYBOARD: - qemu_input_handler_unregister(hs->s); - break; - case HID_MOUSE: - case HID_TABLET: - qemu_remove_mouse_event_handler(hs->ptr.eh_entry); - break; - } + qemu_input_handler_unregister(hs->s); hid_del_idle_timer(hs); } @@ -438,6 +477,20 @@ static QemuInputHandler hid_keyboard_handler = { .event = hid_keyboard_event, }; +static QemuInputHandler hid_mouse_handler = { + .name = "QEMU HID Mouse", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, + .event = hid_pointer_event, + .sync = hid_pointer_sync, +}; + +static QemuInputHandler hid_tablet_handler = { + .name = "QEMU HID Tablet", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, + .event = hid_pointer_event, + .sync = hid_pointer_sync, +}; + void hid_init(HIDState *hs, int kind, HIDEventFunc event) { hs->kind = kind; @@ -448,11 +501,11 @@ void hid_init(HIDState *hs, int kind, HIDEventFunc event) &hid_keyboard_handler); qemu_input_handler_activate(hs->s); } else if (hs->kind == HID_MOUSE) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 0, "QEMU HID Mouse"); + hs->s = qemu_input_handler_register((DeviceState *)hs, + &hid_mouse_handler); } else if (hs->kind == HID_TABLET) { - hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, - 1, "QEMU HID Tablet"); + hs->s = qemu_input_handler_register((DeviceState *)hs, + &hid_tablet_handler); } } diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h index fb913ba3bb..2127c7ce45 100644 --- a/include/hw/input/hid.h +++ b/include/hw/input/hid.h @@ -23,7 +23,6 @@ typedef void (*HIDEventFunc)(HIDState *s); typedef struct HIDMouseState { HIDPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; - QEMUPutMouseEntry *eh_entry; } HIDMouseState; typedef struct HIDKeyboardState { From 6f5943cf45b89b70c228dc51d83e0f861a478ead Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 19 May 2014 15:18:37 +0200 Subject: [PATCH 07/10] input: bind devices and input routing Add function to bind input devices to display devices. Implementing input routing on top of this: Events coming from the display device in question are routed to the input device bound to it (if there is one). Signed-off-by: Gerd Hoffmann --- include/ui/input.h | 3 +++ ui/input.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/ui/input.h b/include/ui/input.h index d84ed8b41c..aa99b0cac7 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -29,6 +29,9 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, void qemu_input_handler_activate(QemuInputHandlerState *s); void qemu_input_handler_deactivate(QemuInputHandlerState *s); void qemu_input_handler_unregister(QemuInputHandlerState *s); +void qemu_input_handler_bind(QemuInputHandlerState *s, + const char *device_id, int head, + Error **errp); void qemu_input_event_send(QemuConsole *src, InputEvent *evt); void qemu_input_event_sync(void); diff --git a/ui/input.c b/ui/input.c index 9d630355e8..14c9434f5e 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,3 +1,4 @@ +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qapi-types.h" #include "qmp-commands.h" @@ -10,6 +11,7 @@ struct QemuInputHandlerState { QemuInputHandler *handler; int id; int events; + QemuConsole *con; QTAILQ_ENTRY(QemuInputHandlerState) node; }; static QTAILQ_HEAD(, QemuInputHandlerState) handlers = @@ -53,12 +55,46 @@ 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) +{ + DeviceState *dev; + QemuConsole *con; + + dev = qdev_find_recursive(sysbus_get_default(), device_id); + if (dev == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device_id); + return; + } + + con = qemu_console_lookup_by_device(dev, head); + if (con == NULL) { + error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); + 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; } @@ -151,7 +187,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) } /* send event */ - s = qemu_input_find_handler(1 << evt->kind); + s = qemu_input_find_handler(1 << evt->kind, src); if (!s) { return; } @@ -252,7 +288,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); } From ee8c0b622c80ed33de5dc3c5444973a1099ca5f6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 20 May 2014 08:11:25 +0200 Subject: [PATCH 08/10] sdl: pass key event source to input layer So the input layer knows where the input is coming from and input routing works correctly. Signed-off-by: Gerd Hoffmann --- ui/sdl2.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index 361de619fa..0e884f96fd 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -190,30 +190,33 @@ static void sdl_switch(DisplayChangeListener *dcl, } } -static void reset_keys(void) +static void reset_keys(struct sdl2_state *scon) { + QemuConsole *con = scon ? scon->dcl.con : NULL; int i; for (i = 0; i < 256; i++) { if (modifiers_state[i]) { int qcode = sdl2_scancode_to_qcode[i]; - qemu_input_event_send_key_qcode(NULL, qcode, false); + qemu_input_event_send_key_qcode(con, qcode, false); modifiers_state[i] = 0; } } } -static void sdl_process_key(SDL_KeyboardEvent *ev) +static void sdl_process_key(struct sdl2_state *scon, + SDL_KeyboardEvent *ev) { int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; + QemuConsole *con = scon ? scon->dcl.con : NULL; switch (ev->keysym.scancode) { #if 0 case SDL_SCANCODE_NUMLOCKCLEAR: case SDL_SCANCODE_CAPSLOCK: /* SDL does not send the key up event, so we generate it */ - qemu_input_event_send_key_qcode(NULL, qcode, true); - qemu_input_event_send_key_qcode(NULL, qcode, false); + qemu_input_event_send_key_qcode(con, qcode, true); + qemu_input_event_send_key_qcode(con, qcode, false); return; #endif case SDL_SCANCODE_LCTRL: @@ -231,7 +234,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) } /* fall though */ default: - qemu_input_event_send_key_qcode(NULL, qcode, + qemu_input_event_send_key_qcode(con, qcode, ev->type == SDL_KEYDOWN); } } @@ -506,7 +509,7 @@ static void handle_keydown(SDL_Event *ev) } } if (!gui_keysym) { - sdl_process_key(&ev->key); + sdl_process_key(scon, &ev->key); } } @@ -531,13 +534,13 @@ static void handle_keyup(SDL_Event *ev) } /* SDL does not send back all the modifiers key, so we must * correct it. */ - reset_keys(); + reset_keys(scon); return; } gui_keysym = 0; } if (!gui_keysym) { - sdl_process_key(&ev->key); + sdl_process_key(scon, &ev->key); } } From f85d28316ab2560e0063f51d2b281bbcdc138442 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 19 May 2014 15:26:51 +0200 Subject: [PATCH 09/10] usb: add input routing support for tablet and keyboard Add display property to the keyboard. Add display and head properties to the tablet. If properties are set bind device to the display specified to setup input routing. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-hid.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index d097d937ea..67a57f1dcd 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -47,6 +47,8 @@ typedef struct USBHIDState { USBEndpoint *intr; HIDState hid; uint32_t usb_version; + char *display; + uint32_t head; } USBHIDState; enum { @@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind) usb_desc_init(dev); us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); hid_init(&us->hid, kind, usb_hid_changed); + if (us->display && us->hid.s) { + qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL); + } return 0; } @@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data) static Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), + DEFINE_PROP_STRING("display", USBHIDState, display), + DEFINE_PROP_UINT32("head", USBHIDState, head, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = { .class_init = usb_mouse_class_initfn, }; +static Property usb_keyboard_properties[] = { + DEFINE_PROP_STRING("display", USBHIDState, display), + DEFINE_PROP_END_OF_LIST(), +}; + static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) uc->product_desc = "QEMU USB Keyboard"; uc->usb_desc = &desc_keyboard; dc->vmsd = &vmstate_usb_kbd; + dc->props = usb_keyboard_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } From 8977bd111f62035b675d41c432eb8b6bf6b86b0f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 20 May 2014 09:17:01 +0200 Subject: [PATCH 10/10] docs: add multiseat.txt Howto on setting up multiseat for guests. Signed-off-by: Gerd Hoffmann --- docs/multiseat.txt | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/multiseat.txt diff --git a/docs/multiseat.txt b/docs/multiseat.txt new file mode 100644 index 0000000000..a6c71dd74f --- /dev/null +++ b/docs/multiseat.txt @@ -0,0 +1,76 @@ + +multiseat howto (with some multihead coverage) +============================================== + +host side +--------- + +First you must compile qemu with a user interface supporting +multihead/multiseat and input event routing. Right now this list is +pretty short: sdl2. + + ./configure --enable-sdl --with-sdlabi=2.0 + + +Next put together the qemu command line: + +qemu -enable-kvm -usb $memory $disk $whatever \ + -display sdl \ + -vga std \ + -device usb-tablet + +That is it for the first head, which will use the standard vga, the +standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the +additional switches for the second head: + + -device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \ + -device secondary-vga,bus=head.2,addr=02.0,id=video.2 \ + -device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \ + -device usb-kbd,bus=usb.2.0,port=1,display=video.2 \ + -device usb-tablet,bus=usb.2.0,port=2,display=video.2 + +This places a pci bridge in slot 12, connects a display adapter and +xhci (usb) controller to the bridge. Then it adds a usb keyboard and +usb mouse, both connected to the xhci and linked to the display. + +The "display=video2" sets up the input routing. Any input coming from +the window which belongs to the video.2 display adapter will be routed +to these input devices. + + +guest side +---------- + +You need a pretty recent linux guest. systemd with loginctl. kernel +3.14+ with CONFIG_DRM_BOCHS enabled. Fedora 20 will do. Must be +fully updated for the new kernel though, i.e. the live iso doesn't cut +it. + +Now we'll have to configure the guest. Boot and login. By default +all devices belong to seat0. You can use "loginctl seat-status seat0" +to list them all (and to get the sysfs paths for cut+paste). Now +we'll go assign all pci devices connected the pci bridge in slot 12 to +a new head: + +loginctl attach seat-qemu \ + /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1 +loginctl attach seat-qemu \ + /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1 +loginctl attach seat-qemu \ + /sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2 + +Use "loginctl seat-status seat-qemu" to check the result. It isn't +needed to assign the usb devices to the head individually, assigning a +usb (root) hub will automatically assign all usb devices connected to +it too. + +BTW: loginctl writes udev rules to /etc/udev/rules.d to make these +device assignments permanent, so you need to do this only once. + +Now simply restart gdm (rebooting will do too), and a login screen +should show up on the second head. + +Enjoy! + +-- +Gerd Hoffmann