From cde8dcbc926d9b960a96c7d637b9c23515e06c0b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 4 Jul 2016 11:42:52 +0200 Subject: [PATCH 1/8] msmouse: add MouseState, unregister handler on close Add struct to track serial mouse state. Store mouse event handler there. Unregister properly on chardev close. Signed-off-by: Gerd Hoffmann Acked-by: Paolo Bonzini Message-id: 1467625375-31774-2-git-send-email-kraxel@redhat.com --- backends/msmouse.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/backends/msmouse.c b/backends/msmouse.c index 8dea5a130f..731784ff6c 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -29,6 +29,11 @@ #define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) +typedef struct { + CharDriverState *chr; + QEMUPutMouseEntry *entry; +} MouseState; + static void msmouse_event(void *opaque, int dx, int dy, int dz, int buttons_state) { @@ -60,7 +65,11 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int static void msmouse_chr_close (struct CharDriverState *chr) { - g_free (chr); + MouseState *mouse = chr->opaque; + + qemu_remove_mouse_event_handler(mouse->entry); + g_free(mouse); + g_free(chr); } static CharDriverState *qemu_chr_open_msmouse(const char *id, @@ -69,17 +78,20 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id, Error **errp) { ChardevCommon *common = backend->u.msmouse.data; + MouseState *mouse; CharDriverState *chr; chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; chr->explicit_be_open = true; - qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); + mouse = g_new0(MouseState, 1); + mouse->entry = qemu_add_mouse_event_handler(msmouse_event, chr, 0, + "QEMU Microsoft Mouse"); + + mouse->chr = chr; + chr->opaque = mouse; return chr; } From 57a4e3b92b6e87158c1e5192fb99a5ac3b82dd5a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 4 Jul 2016 11:42:53 +0200 Subject: [PATCH 2/8] msmouse: fix buffer handling The msmouse chardev backend writes data without checking whenever there is enough space. That happens to work with linux guests, probably by pure luck because the linux driver enables the fifo and the serial port emulation accepts more data than announced via qemu_chr_be_can_write() in that case. Handle this properly by adding a buffer to MouseState. Hook up a CharDriverState->accept_input() handler which feeds the buffer to the serial port. msmouse_event() only fills the buffer now, and calls the accept_input handler too to kick off the transmission. Signed-off-by: Gerd Hoffmann Acked-by: Paolo Bonzini Message-id: 1467625375-31774-3-git-send-email-kraxel@redhat.com --- backends/msmouse.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/backends/msmouse.c b/backends/msmouse.c index 731784ff6c..9ade31b747 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -32,13 +32,35 @@ typedef struct { CharDriverState *chr; QEMUPutMouseEntry *entry; + uint8_t outbuf[32]; + int outlen; } MouseState; +static void msmouse_chr_accept_input(CharDriverState *chr) +{ + MouseState *mouse = chr->opaque; + int len; + + len = qemu_chr_be_can_write(chr); + if (len > mouse->outlen) { + len = mouse->outlen; + } + if (!len) { + return; + } + + qemu_chr_be_write(chr, mouse->outbuf, len); + mouse->outlen -= len; + if (mouse->outlen) { + memmove(mouse->outbuf, mouse->outbuf + len, mouse->outlen); + } +} + static void msmouse_event(void *opaque, int dx, int dy, int dz, int buttons_state) { CharDriverState *chr = (CharDriverState *)opaque; - + MouseState *mouse = chr->opaque; unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; /* Movement deltas */ @@ -51,10 +73,17 @@ static void msmouse_event(void *opaque, bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00); bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00); - /* We always send the packet of, so that we do not have to keep track - of previous state of the middle button. This can potentially confuse - some very old drivers for two button mice though. */ - qemu_chr_be_write(chr, bytes, 4); + if (mouse->outlen <= sizeof(mouse->outbuf) - 4) { + /* We always send the packet of, so that we do not have to keep track + of previous state of the middle button. This can potentially confuse + some very old drivers for two button mice though. */ + memcpy(mouse->outbuf + mouse->outlen, bytes, 4); + mouse->outlen += 4; + } else { + /* queue full -> drop event */ + } + + msmouse_chr_accept_input(chr); } static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) @@ -84,6 +113,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id, chr = qemu_chr_alloc(common, errp); chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; + chr->chr_accept_input = msmouse_chr_accept_input; chr->explicit_be_open = true; mouse = g_new0(MouseState, 1); From 96d7c0720e6a9dff7a8fd61528e6b04bd9aa9673 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 4 Jul 2016 11:42:54 +0200 Subject: [PATCH 3/8] msmouse: switch to new input interface Signed-off-by: Gerd Hoffmann Message-id: 1467625375-31774-4-git-send-email-kraxel@redhat.com --- backends/msmouse.c | 68 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/backends/msmouse.c b/backends/msmouse.c index 9ade31b747..b1e1bea62b 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -25,13 +25,16 @@ #include "qemu-common.h" #include "sysemu/char.h" #include "ui/console.h" +#include "ui/input.h" #define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) typedef struct { CharDriverState *chr; - QEMUPutMouseEntry *entry; + QemuInputHandlerState *hs; + int axis[INPUT_AXIS__MAX]; + bool btns[INPUT_BUTTON__MAX]; uint8_t outbuf[32]; int outlen; } MouseState; @@ -56,12 +59,16 @@ static void msmouse_chr_accept_input(CharDriverState *chr) } } -static void msmouse_event(void *opaque, - int dx, int dy, int dz, int buttons_state) +static void msmouse_queue_event(MouseState *mouse) { - CharDriverState *chr = (CharDriverState *)opaque; - MouseState *mouse = chr->opaque; unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; + int dx, dy; + + dx = mouse->axis[INPUT_AXIS_X]; + mouse->axis[INPUT_AXIS_X] = 0; + + dy = mouse->axis[INPUT_AXIS_Y]; + mouse->axis[INPUT_AXIS_Y] = 0; /* Movement deltas */ bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx); @@ -69,9 +76,9 @@ static void msmouse_event(void *opaque, bytes[2] |= MSMOUSE_LO6(dy); /* Buttons */ - bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00); - bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00); - bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00); + bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT] ? 0x20 : 0x00); + bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT] ? 0x10 : 0x00); + bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00); if (mouse->outlen <= sizeof(mouse->outbuf) - 4) { /* We always send the packet of, so that we do not have to keep track @@ -82,8 +89,38 @@ static void msmouse_event(void *opaque, } else { /* queue full -> drop event */ } +} - msmouse_chr_accept_input(chr); +static void msmouse_input_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + MouseState *mouse = (MouseState *)dev; + InputMoveEvent *move; + InputBtnEvent *btn; + + switch (evt->type) { + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + mouse->axis[move->axis] += move->value; + break; + + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + mouse->btns[btn->button] = btn->down; + break; + + default: + /* keep gcc happy */ + break; + } +} + +static void msmouse_input_sync(DeviceState *dev) +{ + MouseState *mouse = (MouseState *)dev; + + msmouse_queue_event(mouse); + msmouse_chr_accept_input(mouse->chr); } static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) @@ -96,11 +133,18 @@ static void msmouse_chr_close (struct CharDriverState *chr) { MouseState *mouse = chr->opaque; - qemu_remove_mouse_event_handler(mouse->entry); + qemu_input_handler_unregister(mouse->hs); g_free(mouse); g_free(chr); } +static QemuInputHandler msmouse_handler = { + .name = "QEMU Microsoft Mouse", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, + .event = msmouse_input_event, + .sync = msmouse_input_sync, +}; + static CharDriverState *qemu_chr_open_msmouse(const char *id, ChardevBackend *backend, ChardevReturn *ret, @@ -117,8 +161,8 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id, chr->explicit_be_open = true; mouse = g_new0(MouseState, 1); - mouse->entry = qemu_add_mouse_event_handler(msmouse_event, chr, 0, - "QEMU Microsoft Mouse"); + mouse->hs = qemu_input_handler_register((DeviceState *)mouse, + &msmouse_handler); mouse->chr = chr; chr->opaque = mouse; From d7b7f526b18dacda8ee559dc18c13d4430a78b68 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 4 Jul 2016 11:42:55 +0200 Subject: [PATCH 4/8] msmouse: send short messages if possible. Keep track of button changes. Send the extended 4-byte messages for three button mice only in case we have something to report for the middle button. Use the short 3-byte messages (original protocol for two-button microsoft mouse) otherwise. Signed-off-by: Gerd Hoffmann Message-id: 1467625375-31774-5-git-send-email-kraxel@redhat.com --- backends/msmouse.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/backends/msmouse.c b/backends/msmouse.c index b1e1bea62b..aeb905562d 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -35,6 +35,7 @@ typedef struct { QemuInputHandlerState *hs; int axis[INPUT_AXIS__MAX]; bool btns[INPUT_BUTTON__MAX]; + bool btnc[INPUT_BUTTON__MAX]; uint8_t outbuf[32]; int outlen; } MouseState; @@ -62,7 +63,7 @@ static void msmouse_chr_accept_input(CharDriverState *chr) static void msmouse_queue_event(MouseState *mouse) { unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; - int dx, dy; + int dx, dy, count = 3; dx = mouse->axis[INPUT_AXIS_X]; mouse->axis[INPUT_AXIS_X] = 0; @@ -78,14 +79,16 @@ static void msmouse_queue_event(MouseState *mouse) /* Buttons */ bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT] ? 0x20 : 0x00); bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT] ? 0x10 : 0x00); - bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00); + if (mouse->btns[INPUT_BUTTON_MIDDLE] || + mouse->btnc[INPUT_BUTTON_MIDDLE]) { + bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00); + mouse->btnc[INPUT_BUTTON_MIDDLE] = false; + count = 4; + } - if (mouse->outlen <= sizeof(mouse->outbuf) - 4) { - /* We always send the packet of, so that we do not have to keep track - of previous state of the middle button. This can potentially confuse - some very old drivers for two button mice though. */ - memcpy(mouse->outbuf + mouse->outlen, bytes, 4); - mouse->outlen += 4; + if (mouse->outlen <= sizeof(mouse->outbuf) - count) { + memcpy(mouse->outbuf + mouse->outlen, bytes, count); + mouse->outlen += count; } else { /* queue full -> drop event */ } @@ -107,6 +110,7 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src, case INPUT_EVENT_KIND_BTN: btn = evt->u.btn.data; mouse->btns[btn->button] = btn->down; + mouse->btnc[btn->button] = true; break; default: From c80276b4206d534daaaf87305df5805db1b8db02 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 23 Jun 2016 11:51:35 +0200 Subject: [PATCH 5/8] input: add trace events for full queues It isn't unusual to happen, for example during reboot when the guest doesn't reveice events for a while. So better don't flood stderr with alarming messages. Turn them into tracepoints instead so they can be enabled in case they are needed for trouble-shooting. Signed-off-by: Gerd Hoffmann Message-id: 1466675495-28797-1-git-send-email-kraxel@redhat.com --- hw/input/hid.c | 3 ++- hw/input/trace-events | 6 ++++++ hw/input/virtio-input.c | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/input/hid.c b/hw/input/hid.c index d92c7463ba..5e2850e655 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -27,6 +27,7 @@ #include "ui/console.h" #include "qemu/timer.h" #include "hw/input/hid.h" +#include "trace.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 #define HID_USAGE_POSTFAIL 0x02 @@ -234,7 +235,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, key->down, scancodes); if (hs->n + count > QUEUE_LENGTH) { - fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + trace_hid_kbd_queue_full(); return; } for (i = 0; i < count; i++) { diff --git a/hw/input/trace-events b/hw/input/trace-events index 00fcec12b9..f24dff2f8b 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -23,3 +23,9 @@ milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value % milkymist_softusb_mevt(uint8_t m) "m %d" milkymist_softusb_kevt(uint8_t m) "m %d" milkymist_softusb_pulse_irq(void) "Pulse IRQ" + +# hw/input/hid.c +hid_kbd_queue_full(void) "queue full" + +# hw/input/virtio +virtio_input_queue_full(void) "queue full" diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index f59749a943..edf69903a6 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/iov.h" +#include "trace.h" #include "hw/qdev.h" #include "hw/virtio/virtio.h" @@ -47,7 +48,7 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); if (have < need) { vinput->qindex = 0; - fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__); + trace_virtio_input_queue_full(); return; } From d4df42c43168873521632836bdffecf2f3a9f9f1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 Jun 2016 11:03:18 +0200 Subject: [PATCH 6/8] input-linux: factor out input_linux_handle_mouse No functional change. Signed-off-by: Gerd Hoffmann Message-id: 1466067800-25434-2-git-send-email-kraxel@redhat.com --- ui/input-linux.c | 102 ++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index 1d33b5c121..a932290a9f 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -265,6 +265,58 @@ static void input_linux_event_mouse_button(int button) qemu_input_event_sync(); } +static void input_linux_handle_mouse(InputLinux *il, struct input_event *event) +{ + if (!il->grab_active) { + return; + } + + switch (event->type) { + case EV_KEY: + switch (event->code) { + case BTN_LEFT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value); + break; + case BTN_RIGHT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value); + break; + case BTN_MIDDLE: + qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value); + break; + case BTN_GEAR_UP: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value); + break; + case BTN_GEAR_DOWN: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN, + event->value); + break; + }; + break; + case EV_REL: + switch (event->code) { + case REL_X: + qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value); + break; + case REL_Y: + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value); + break; + case REL_WHEEL: + il->wheel = event->value; + break; + } + break; + case EV_SYN: + qemu_input_event_sync(); + if (il->wheel != 0) { + input_linux_event_mouse_button((il->wheel > 0) + ? INPUT_BUTTON_WHEEL_UP + : INPUT_BUTTON_WHEEL_DOWN); + il->wheel = 0; + } + break; + } +} + static void input_linux_event_mouse(void *opaque) { InputLinux *il = opaque; @@ -282,55 +334,7 @@ static void input_linux_event_mouse(void *opaque) break; } - /* only send event to guest when grab is active */ - if (!il->grab_active) { - continue; - } - - switch (event.type) { - case EV_KEY: - switch (event.code) { - case BTN_LEFT: - qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value); - break; - case BTN_RIGHT: - qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value); - break; - case BTN_MIDDLE: - qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value); - break; - case BTN_GEAR_UP: - qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value); - break; - case BTN_GEAR_DOWN: - qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN, - event.value); - break; - }; - break; - case EV_REL: - switch (event.code) { - case REL_X: - qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value); - break; - case REL_Y: - qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value); - break; - case REL_WHEEL: - il->wheel = event.value; - break; - } - break; - case EV_SYN: - qemu_input_event_sync(); - if (il->wheel != 0) { - input_linux_event_mouse_button((il->wheel > 0) - ? INPUT_BUTTON_WHEEL_UP - : INPUT_BUTTON_WHEEL_DOWN); - il->wheel = 0; - } - break; - } + input_linux_handle_mouse(il, &event); } } From 2330e9e7cca29346df74b21105a587d9478ff296 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 Jun 2016 11:03:19 +0200 Subject: [PATCH 7/8] input-linux: factor out input_linux_handle_keyboard No functional change. Signed-off-by: Gerd Hoffmann Message-id: 1466067800-25434-3-git-send-email-kraxel@redhat.com --- ui/input-linux.c | 103 +++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index a932290a9f..ffff97f113 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -188,6 +188,59 @@ static void input_linux_toggle_grab(InputLinux *il) } } +static void input_linux_handle_keyboard(InputLinux *il, + struct input_event *event) +{ + if (event->type == EV_KEY) { + if (event->value > 2 || (event->value > 1 && !il->repeat)) { + /* + * ignore autorepeat + unknown key events + * 0 == up, 1 == down, 2 == autorepeat, other == undefined + */ + return; + } + if (event->code >= KEY_CNT) { + /* + * Should not happen. But better safe than sorry, + * and we make Coverity happy too. + */ + return; + } + + /* keep track of key state */ + if (!il->keydown[event->code] && event->value) { + il->keydown[event->code] = true; + il->keycount++; + } + if (il->keydown[event->code] && !event->value) { + il->keydown[event->code] = false; + il->keycount--; + } + + /* send event to guest when grab is active */ + if (il->grab_active) { + int qcode = qemu_input_linux_to_qcode(event->code); + qemu_input_event_send_key_qcode(NULL, qcode, event->value); + } + + /* hotkey -> record switch request ... */ + if (il->keydown[KEY_LEFTCTRL] && + il->keydown[KEY_RIGHTCTRL]) { + il->grab_request = true; + } + + /* + * ... and do the switch when all keys are lifted, so we + * confuse neither guest nor host with keys which seem to + * be stuck due to missing key-up events. + */ + if (il->grab_request && !il->keycount) { + il->grab_request = false; + input_linux_toggle_grab(il); + } + } +} + static void input_linux_event_keyboard(void *opaque) { InputLinux *il = opaque; @@ -205,55 +258,7 @@ static void input_linux_event_keyboard(void *opaque) break; } - switch (event.type) { - case EV_KEY: - if (event.value > 2 || (event.value > 1 && !il->repeat)) { - /* - * ignore autorepeat + unknown key events - * 0 == up, 1 == down, 2 == autorepeat, other == undefined - */ - continue; - } - if (event.code >= KEY_CNT) { - /* - * Should not happen. But better safe than sorry, - * and we make Coverity happy too. - */ - continue; - } - /* keep track of key state */ - if (!il->keydown[event.code] && event.value) { - il->keydown[event.code] = true; - il->keycount++; - } - if (il->keydown[event.code] && !event.value) { - il->keydown[event.code] = false; - il->keycount--; - } - - /* send event to guest when grab is active */ - if (il->grab_active) { - int qcode = qemu_input_linux_to_qcode(event.code); - qemu_input_event_send_key_qcode(NULL, qcode, event.value); - } - - /* hotkey -> record switch request ... */ - if (il->keydown[KEY_LEFTCTRL] && - il->keydown[KEY_RIGHTCTRL]) { - il->grab_request = true; - } - - /* - * ... and do the switch when all keys are lifted, so we - * confuse neither guest nor host with keys which seem to - * be stuck due to missing key-up events. - */ - if (il->grab_request && !il->keycount) { - il->grab_request = false; - input_linux_toggle_grab(il); - } - break; - } + input_linux_handle_keyboard(il, &event); } } From 2e6a64cb8d7506ad27d3b6c8000bc8d773936932 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 Jun 2016 11:03:20 +0200 Subject: [PATCH 8/8] input-linux: better capability checks, merge input_linux_event_{mouse, keyboard} Improve capability checks (count keys and buttons), store results. Merge the input_linux_event_mouse and input_linux_event_keyboard functions into one, dispatch into input_linux_handle_mouse and input_linux_handle_keyboard depending on device capabilities. Allow calling both handle functions, so we can handle mice which also send key events, by routing those key events to the keyboard. Signed-off-by: Gerd Hoffmann Message-id: 1466067800-25434-4-git-send-email-kraxel@redhat.com --- ui/input-linux.c | 92 +++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index ffff97f113..0e230ce699 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -129,6 +129,17 @@ static int qemu_input_linux_to_qcode(unsigned int lnx) return linux_to_qcode[lnx]; } +static bool linux_is_button(unsigned int lnx) +{ + if (lnx < 0x100) { + return false; + } + if (lnx >= 0x160 && lnx < 0x2c0) { + return false; + } + return true; +} + #define TYPE_INPUT_LINUX "input-linux" #define INPUT_LINUX(obj) \ OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX) @@ -153,6 +164,12 @@ struct InputLinux { int keycount; int wheel; bool initialized; + + bool has_rel_x; + bool has_abs_x; + int num_keys; + int num_btns; + QTAILQ_ENTRY(InputLinux) next; }; @@ -241,27 +258,6 @@ static void input_linux_handle_keyboard(InputLinux *il, } } -static void input_linux_event_keyboard(void *opaque) -{ - InputLinux *il = opaque; - struct input_event event; - int rc; - - for (;;) { - rc = read(il->fd, &event, sizeof(event)); - if (rc != sizeof(event)) { - if (rc < 0 && errno != EAGAIN) { - fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno)); - qemu_set_fd_handler(il->fd, NULL, NULL, NULL); - close(il->fd); - } - break; - } - - input_linux_handle_keyboard(il, &event); - } -} - static void input_linux_event_mouse_button(int button) { qemu_input_queue_btn(NULL, button, true); @@ -322,7 +318,7 @@ static void input_linux_handle_mouse(InputLinux *il, struct input_event *event) } } -static void input_linux_event_mouse(void *opaque) +static void input_linux_event(void *opaque) { InputLinux *il = opaque; struct input_event event; @@ -339,14 +335,20 @@ static void input_linux_event_mouse(void *opaque) break; } - input_linux_handle_mouse(il, &event); + if (il->num_keys) { + input_linux_handle_keyboard(il, &event); + } + if (il->has_rel_x && il->num_btns) { + input_linux_handle_mouse(il, &event); + } } } static void input_linux_complete(UserCreatable *uc, Error **errp) { InputLinux *il = INPUT_LINUX(uc); - uint32_t evtmap, relmap, absmap; + uint8_t evtmap, relmap, absmap, keymap[KEY_CNT / 8]; + unsigned int i; int rc, ver; if (!il->evdev) { @@ -374,36 +376,36 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) } if (evtmap & (1 << EV_REL)) { + relmap = 0; rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap); - if (rc < 0) { - relmap = 0; + if (relmap & (1 << REL_X)) { + il->has_rel_x = true; } } if (evtmap & (1 << EV_ABS)) { - ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); - if (rc < 0) { - absmap = 0; + absmap = 0; + rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); + if (absmap & (1 << ABS_X)) { + il->has_abs_x = true; } } - if ((evtmap & (1 << EV_REL)) && - (relmap & (1 << REL_X))) { - /* has relative x axis -> assume mouse */ - qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il); - } else if ((evtmap & (1 << EV_ABS)) && - (absmap & (1 << ABS_X))) { - /* has absolute x axis -> not supported */ - error_setg(errp, "tablet/touchscreen not supported"); - goto err_close; - } else if (evtmap & (1 << EV_KEY)) { - /* has keys/buttons (and no x axis) -> assume keyboard */ - qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il); - } else { - /* Huh? What is this? */ - error_setg(errp, "unknown kind of input device"); - goto err_close; + if (evtmap & (1 << EV_KEY)) { + memset(keymap, 0, sizeof(keymap)); + rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap); + for (i = 0; i < KEY_CNT; i++) { + if (keymap[i / 8] & (1 << (i % 8))) { + if (linux_is_button(i)) { + il->num_btns++; + } else { + il->num_keys++; + } + } + } } + + qemu_set_fd_handler(il->fd, input_linux_event, NULL, il); input_linux_toggle_grab(il); QTAILQ_INSERT_TAIL(&inputs, il, next); il->initialized = true;