Compare commits

..

8 Commits

Author SHA1 Message Date
Gerd Hoffmann
2e6a64cb8d 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 <kraxel@redhat.com>
Message-id: 1466067800-25434-4-git-send-email-kraxel@redhat.com
2016-07-12 09:25:50 +02:00
Gerd Hoffmann
2330e9e7cc input-linux: factor out input_linux_handle_keyboard
No functional change.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 1466067800-25434-3-git-send-email-kraxel@redhat.com
2016-07-12 09:25:50 +02:00
Gerd Hoffmann
d4df42c431 input-linux: factor out input_linux_handle_mouse
No functional change.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 1466067800-25434-2-git-send-email-kraxel@redhat.com
2016-07-12 09:25:50 +02:00
Gerd Hoffmann
c80276b420 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 <kraxel@redhat.com>
Message-id: 1466675495-28797-1-git-send-email-kraxel@redhat.com
2016-07-12 09:25:28 +02:00
Gerd Hoffmann
d7b7f526b1 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 <kraxel@redhat.com>
Message-id: 1467625375-31774-5-git-send-email-kraxel@redhat.com
2016-07-12 09:24:31 +02:00
Gerd Hoffmann
96d7c0720e msmouse: switch to new input interface
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 1467625375-31774-4-git-send-email-kraxel@redhat.com
2016-07-12 09:24:31 +02:00
Gerd Hoffmann
57a4e3b92b 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 <kraxel@redhat.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1467625375-31774-3-git-send-email-kraxel@redhat.com
2016-07-12 09:24:31 +02:00
Gerd Hoffmann
cde8dcbc92 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 <kraxel@redhat.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1467625375-31774-2-git-send-email-kraxel@redhat.com
2016-07-12 09:24:31 +02:00
7 changed files with 272 additions and 169 deletions

View File

@@ -25,16 +25,51 @@
#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)
static void msmouse_event(void *opaque,
int dx, int dy, int dz, int buttons_state)
{
CharDriverState *chr = (CharDriverState *)opaque;
typedef struct {
CharDriverState *chr;
QemuInputHandlerState *hs;
int axis[INPUT_AXIS__MAX];
bool btns[INPUT_BUTTON__MAX];
bool btnc[INPUT_BUTTON__MAX];
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_queue_event(MouseState *mouse)
{
unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
int dx, dy, count = 3;
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);
@@ -42,14 +77,54 @@ 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);
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;
}
/* 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) - count) {
memcpy(mouse->outbuf + mouse->outlen, bytes, count);
mouse->outlen += count;
} else {
/* queue full -> drop event */
}
}
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;
mouse->btnc[btn->button] = true;
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)
@@ -60,26 +135,41 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
static void msmouse_chr_close (struct CharDriverState *chr)
{
MouseState *mouse = chr->opaque;
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,
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->chr_accept_input = msmouse_chr_accept_input;
chr->explicit_be_open = true;
qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
mouse = g_new0(MouseState, 1);
mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
&msmouse_handler);
mouse->chr = chr;
chr->opaque = mouse;
return chr;
}

View File

@@ -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++) {

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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;
};
@@ -188,53 +205,39 @@ static void input_linux_toggle_grab(InputLinux *il)
}
}
static void input_linux_event_keyboard(void *opaque)
static void input_linux_handle_keyboard(InputLinux *il,
struct input_event *event)
{
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;
}
switch (event.type) {
case EV_KEY:
if (event.value > 2 || (event.value > 1 && !il->repeat)) {
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
*/
continue;
return;
}
if (event.code >= KEY_CNT) {
if (event->code >= KEY_CNT) {
/*
* Should not happen. But better safe than sorry,
* and we make Coverity happy too.
*/
continue;
return;
}
/* keep track of key state */
if (!il->keydown[event.code] && event.value) {
il->keydown[event.code] = true;
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;
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);
int qcode = qemu_input_linux_to_qcode(event->code);
qemu_input_event_send_key_qcode(NULL, qcode, event->value);
}
/* hotkey -> record switch request ... */
@@ -252,8 +255,6 @@ static void input_linux_event_keyboard(void *opaque)
il->grab_request = false;
input_linux_toggle_grab(il);
}
break;
}
}
}
@@ -265,7 +266,59 @@ static void input_linux_event_mouse_button(int button)
qemu_input_event_sync();
}
static void input_linux_event_mouse(void *opaque)
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(void *opaque)
{
InputLinux *il = opaque;
struct input_event event;
@@ -282,54 +335,11 @@ static void input_linux_event_mouse(void *opaque)
break;
}
/* only send event to guest when grab is active */
if (!il->grab_active) {
continue;
if (il->num_keys) {
input_linux_handle_keyboard(il, &event);
}
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;
if (il->has_rel_x && il->num_btns) {
input_linux_handle_mouse(il, &event);
}
}
}
@@ -337,7 +347,8 @@ static void input_linux_event_mouse(void *opaque)
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) {
@@ -365,36 +376,36 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
}
if (evtmap & (1 << EV_REL)) {
rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
if (rc < 0) {
relmap = 0;
rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
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;
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);
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 {
/* Huh? What is this? */
error_setg(errp, "unknown kind of input device");
goto err_close;
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;

View File

@@ -349,7 +349,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h)
tight_fill_palette##bpp(VncState *vs, int x, int y, \
int max, size_t count, \
uint32_t *bg, uint32_t *fg, \
VncPalette *palette) { \
VncPalette **palette) { \
uint##bpp##_t *data; \
uint##bpp##_t c0, c1, ci; \
int i, n0, n1; \
@@ -396,23 +396,23 @@ tight_detect_smooth_image(VncState *vs, int w, int h)
return 0; \
} \
\
palette_init(palette, max, bpp); \
palette_put(palette, c0); \
palette_put(palette, c1); \
palette_put(palette, ci); \
*palette = palette_new(max, bpp); \
palette_put(*palette, c0); \
palette_put(*palette, c1); \
palette_put(*palette, ci); \
\
for (i++; i < count; i++) { \
if (data[i] == ci) { \
continue; \
} else { \
ci = data[i]; \
if (!palette_put(palette, (uint32_t)ci)) { \
if (!palette_put(*palette, (uint32_t)ci)) { \
return 0; \
} \
} \
} \
\
return palette_size(palette); \
return palette_size(*palette); \
}
DEFINE_FILL_PALETTE_FUNCTION(8)
@@ -421,7 +421,7 @@ DEFINE_FILL_PALETTE_FUNCTION(32)
static int tight_fill_palette(VncState *vs, int x, int y,
size_t count, uint32_t *bg, uint32_t *fg,
VncPalette *palette)
VncPalette **palette)
{
int max;
@@ -1457,11 +1457,9 @@ static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h,
}
#endif
static __thread VncPalette color_count_palette;
static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
{
VncPalette *palette = &color_count_palette;
VncPalette *palette = NULL;
uint32_t bg = 0, fg = 0;
int colors;
int ret = 0;
@@ -1490,7 +1488,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
}
#endif
colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, palette);
colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, &palette);
#ifdef CONFIG_VNC_JPEG
if (allow_jpeg && vs->tight.quality != (uint8_t)-1) {
@@ -1503,6 +1501,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
#endif
palette_destroy(palette);
return ret;
}

View File

@@ -1025,7 +1025,7 @@ static int find_and_clear_dirty_height(VncState *vs,
static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
{
vs->has_dirty += has_dirty;
if (vs->need_update && !vs->disconnecting) {
if (vs->need_update && vs->ioc != NULL) {
VncDisplay *vd = vs->vd;
VncJob *job;
int y;
@@ -1436,9 +1436,8 @@ static void vnc_jobs_bh(void *opaque)
* First function called whenever there is more data to be read from
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring decryption calls)
* Returns 0 on success, -1 if client disconnected
*/
static int vnc_client_read(VncState *vs)
static void vnc_client_read(VncState *vs)
{
ssize_t ret;
@@ -1451,9 +1450,8 @@ static int vnc_client_read(VncState *vs)
if (!ret) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
return -1;
}
return 0;
return;
}
while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
@@ -1463,7 +1461,7 @@ static int vnc_client_read(VncState *vs)
ret = vs->read_handler(vs, vs->input.buffer, len);
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
return -1;
return;
}
if (!ret) {
@@ -1472,7 +1470,6 @@ static int vnc_client_read(VncState *vs)
vs->read_handler_expect = ret;
}
}
return 0;
}
gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
@@ -1480,9 +1477,7 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
{
VncState *vs = opaque;
if (condition & G_IO_IN) {
if (vnc_client_read(vs) < 0) {
return TRUE;
}
vnc_client_read(vs);
}
if (condition & G_IO_OUT) {
vnc_client_write(vs);