@@ -43,30 +43,41 @@
#define USB_TABLET 2
#define USB_KEYBOARD 3
-typedef struct USBMouseState {
- int dx, dy, dz, buttons_state;
- int x, y;
- int mouse_grabbed;
+typedef struct USBPointerEvent {
+ int xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
+ int dz, buttons_state;
+} USBPointerEvent;
+
+#define QUEUELENSHIFT 4 /* 16 events should be enough for a triple-click */
+#define QUEUELEN (1u<<QUEUELENSHIFT)
+#define QUEUEINDEXMASK (QUEUELEN-1u)
+#define QUEUE_INCR(v) ((v)++, (v) &= QUEUEINDEXMASK)
+
+typedef struct USBPointerState {
+ USBPointerEvent queue[QUEUELEN];
+ unsigned head, tail; /* indices into circular queue */
+
+ int mouse_grabbed, xyrel;
QEMUPutMouseEntry *eh_entry;
-} USBMouseState;
+} USBPointerState;
typedef struct USBKeyboardState {
uint16_t modifiers;
uint8_t leds;
uint8_t key[16];
int keys;
+ int changed;
} USBKeyboardState;
typedef struct USBHIDState {
USBDevice dev;
union {
- USBMouseState ptr;
+ USBPointerState ptr;
USBKeyboardState kbd;
};
int kind;
int protocol;
uint8_t idle;
- int changed;
void *datain_opaque;
void (*datain)(void *);
} USBHIDState;
@@ -404,40 +415,64 @@ static const uint8_t usb_hid_usage_keys[0x100] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
-static void usb_hid_changed(USBHIDState *hs)
+static void usb_notify_datain_cb(USBHIDState *hs)
{
- hs->changed = 1;
-
if (hs->datain)
hs->datain(hs->datain_opaque);
}
-static void usb_mouse_event(void *opaque,
- int dx1, int dy1, int dz1, int buttons_state)
-{
- USBHIDState *hs = opaque;
- USBMouseState *s = &hs->ptr;
-
- s->dx += dx1;
- s->dy += dy1;
- s->dz += dz1;
- s->buttons_state = buttons_state;
-
- usb_hid_changed(hs);
+static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) {
+ e->xdx = e->ydy = e->dz = 0;
+ e->buttons_state = buttons;
}
-static void usb_tablet_event(void *opaque,
- int x, int y, int dz, int buttons_state)
-{
- USBHIDState *hs = opaque;
- USBMouseState *s = &hs->ptr;
+static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel,
+ int x1, int y1, int z1) {
+ if (xyrel) {
+ e->xdx += x1;
+ e->ydy += y1;
+ } else {
+ e->xdx = x1;
+ e->ydy = y1;
+ }
+ e->dz += z1;
+}
- s->x = x;
- s->y = y;
- s->dz += dz;
- s->buttons_state = buttons_state;
+static void usb_pointer_event(void *hs_v, int x1, int y1, int z1,
+ int buttons_state) {
+ /* We combine events where feasible to keep the queue small.
+ * We shouldn't combine anything with the first event with
+ * a particular button state, as that would change the
+ * location of the button state change. */
+ USBHIDState *hs= hs_v;
+ USBPointerState *s = &hs->ptr;
+ unsigned use_slot= (s->tail-1) & QUEUEINDEXMASK;
+ unsigned previous_slot= (use_slot-1) & QUEUEINDEXMASK;
+
+ if (s->tail == s->head) {
+ use_slot= s->tail;
+ QUEUE_INCR(s->tail);
+ usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
+ } else if (use_slot == s->head ||
+ s->queue[use_slot].buttons_state != buttons_state ||
+ s->queue[previous_slot].buttons_state != buttons_state) {
+ /* can't or shouldn't combine this event with previous one */
+ use_slot= s->tail;
+ QUEUE_INCR(s->tail);
+ if (use_slot == s->head) {
+ /* queue full, oh well, discard something */
+ s->head++; s->head &= QUEUEINDEXMASK;
+ /* but we preserve the relative motions */
+ usb_pointer_event_combine(&s->queue[s->head], s->xyrel,
+ s->queue[use_slot].xdx,
+ s->queue[use_slot].ydy,
+ s->queue[use_slot].dz);
+ }
+ usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
+ }
+ usb_pointer_event_combine(&s->queue[use_slot],s->xyrel, x1,y1,z1);
- usb_hid_changed(hs);
+ usb_notify_datain_cb(hs);
}
static void usb_keyboard_event(void *opaque, int keycode)
@@ -451,6 +486,9 @@ static void usb_keyboard_event(void *opaque, int keycode)
hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
s->modifiers &= ~(1 << 8);
+ s->changed = 1;
+ usb_notify_datain_cb(hs);
+
switch (hid_code) {
case 0x00:
return;
@@ -475,7 +513,6 @@ static void usb_keyboard_event(void *opaque, int keycode)
if (s->key[i] == hid_code) {
s->key[i] = s->key[-- s->keys];
s->key[s->keys] = 0x00;
- usb_hid_changed(hs);
break;
}
if (i < 0)
@@ -490,8 +527,6 @@ static void usb_keyboard_event(void *opaque, int keycode)
} else
return;
}
-
- usb_hid_changed(hs);
}
static inline int int_clamp(int val, int vmin, int vmax)
@@ -504,85 +539,100 @@ static inline int int_clamp(int val, int vmin, int vmax)
return val;
}
-static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len)
+static int usb_suppress_report(USBHIDState *hs, int unchanged) {
+ /* TODO: Implement finite idle delays. */
+ return !hs->idle && unchanged; /* SET_IDLE 0 means do not report */
+}
+
+static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len)
{
int dx, dy, dz, b, l;
- USBMouseState *s = &hs->ptr;
+ USBPointerState *s = &hs->ptr;
+ USBPointerEvent *e;
if (!s->mouse_grabbed) {
- s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs,
- 0, "QEMU USB Mouse");
+ s->eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, hs,
+ !s->xyrel, "QEMU USB Pointer");
s->mouse_grabbed = 1;
}
- dx = int_clamp(s->dx, -127, 127);
- dy = int_clamp(s->dy, -127, 127);
- dz = int_clamp(s->dz, -127, 127);
-
- s->dx -= dx;
- s->dy -= dy;
- s->dz -= dz;
-
- /* Appears we have to invert the wheel direction */
- dz = 0 - dz;
+ if (usb_suppress_report(hs, s->head == s->tail))
+ return USB_RET_NAK;
- b = 0;
- if (s->buttons_state & MOUSE_EVENT_LBUTTON)
- b |= 0x01;
- if (s->buttons_state & MOUSE_EVENT_RBUTTON)
- b |= 0x02;
- if (s->buttons_state & MOUSE_EVENT_MBUTTON)
- b |= 0x04;
+ if (s->head == s->tail)
+ /* use the last report */
+ s->head = (s->head - 1) & QUEUEINDEXMASK;
- l = 0;
- if (len > l)
- buf[l ++] = b;
- if (len > l)
- buf[l ++] = dx;
- if (len > l)
- buf[l ++] = dy;
- if (len > l)
- buf[l ++] = dz;
- return l;
-}
+ e = &s->queue[s->head];
-static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len)
-{
- int dz, b, l;
- USBMouseState *s = &hs->ptr;
+ dz = int_clamp(e->dz, -128, 127);
- if (!s->mouse_grabbed) {
- s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs,
- 1, "QEMU USB Tablet");
- s->mouse_grabbed = 1;
+ e->dz -= dz;
+ if (s->xyrel) {
+ dx = int_clamp(e->xdx, -128, 127);
+ dy = int_clamp(e->ydy, -128, 127);
+ e->xdx -= dx;
+ e->ydy -= dy;
+ } else {
+ dx = e->xdx;
+ dy = e->ydy;
}
-
- dz = int_clamp(s->dz, -127, 127);
- s->dz -= dz;
-
/* Appears we have to invert the wheel direction */
dz = 0 - dz;
+
+ if (!(e->dz ||
+ (s->xyrel && (e->xdx || e->ydy)))) {
+ /* that deals with this event */
+ QUEUE_INCR(s->head);
+ }
+
b = 0;
- if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ if (e->buttons_state & MOUSE_EVENT_LBUTTON)
b |= 0x01;
- if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ if (e->buttons_state & MOUSE_EVENT_RBUTTON)
b |= 0x02;
- if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ if (e->buttons_state & MOUSE_EVENT_MBUTTON)
b |= 0x04;
- buf[0] = b;
- buf[1] = s->x & 0xff;
- buf[2] = s->x >> 8;
- buf[3] = s->y & 0xff;
- buf[4] = s->y >> 8;
- buf[5] = dz;
- l = 6;
+ switch (hs->kind) {
+ case USB_MOUSE:
+ l = 0;
+ if (len > l)
+ buf[l ++] = b;
+ if (len > l)
+ buf[l ++] = dx;
+ if (len > l)
+ buf[l ++] = dy;
+ if (len > l)
+ buf[l ++] = dz;
+ break;
+
+ case USB_TABLET:
+ buf[0] = b;
+ buf[1] = dx & 0xff;
+ buf[2] = dx >> 8;
+ buf[3] = dy & 0xff;
+ buf[4] = dy >> 8;
+ buf[5] = dz;
+ l = 6;
+ break;
+
+ default:
+ abort();
+ }
return l;
}
-static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len)
+static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len)
{
+ USBKeyboardState *s= &hs->kbd;
+
+ if (usb_suppress_report(hs, !s->changed))
+ return USB_RET_NAK;
+
+ s->changed= 0;
+
if (len < 2)
return 0;
@@ -609,16 +659,11 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
return 0;
}
-static void usb_mouse_handle_reset(USBDevice *dev)
+static void usb_pointer_handle_reset(USBDevice *dev)
{
USBHIDState *s = (USBHIDState *)dev;
- s->ptr.dx = 0;
- s->ptr.dy = 0;
- s->ptr.dz = 0;
- s->ptr.x = 0;
- s->ptr.y = 0;
- s->ptr.buttons_state = 0;
+ s->ptr.head= s->ptr.tail = 0;
s->protocol = 1;
}
@@ -764,12 +809,10 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value,
}
break;
case GET_REPORT:
- if (s->kind == USB_MOUSE)
- ret = usb_mouse_poll(s, data, length);
- else if (s->kind == USB_TABLET)
- ret = usb_tablet_poll(s, data, length);
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
+ ret = usb_pointer_poll(s, data, length);
else if (s->kind == USB_KEYBOARD)
- ret = usb_keyboard_poll(&s->kbd, data, length);
+ ret = usb_keyboard_poll(s, data, length);
break;
case SET_REPORT:
if (s->kind == USB_KEYBOARD)
@@ -813,16 +856,10 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
switch(p->pid) {
case USB_TOKEN_IN:
if (p->devep == 1) {
- /* TODO: Implement finite idle delays. */
- if (!(s->changed || s->idle))
- return USB_RET_NAK;
- s->changed = 0;
- if (s->kind == USB_MOUSE)
- ret = usb_mouse_poll(s, p->data, p->len);
- else if (s->kind == USB_TABLET)
- ret = usb_tablet_poll(s, p->data, p->len);
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
+ ret = usb_pointer_poll(s, p->data, p->len);
else if (s->kind == USB_KEYBOARD)
- ret = usb_keyboard_poll(&s->kbd, p->data, p->len);
+ ret = usb_keyboard_poll(s, p->data, p->len);
} else {
goto fail;
}
@@ -846,7 +883,7 @@ static void usb_hid_handle_destroy(USBDevice *dev)
qemu_free(s);
}
-USBDevice *usb_tablet_init(void)
+static USBDevice *usb_pointer_init(int kind, int xyrel, const char *devname)
{
USBHIDState *s;
@@ -854,38 +891,31 @@ USBDevice *usb_tablet_init(void)
s->dev.speed = USB_SPEED_FULL;
s->dev.handle_packet = usb_generic_handle_packet;
- s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_reset = usb_pointer_handle_reset;
s->dev.handle_control = usb_hid_handle_control;
s->dev.handle_data = usb_hid_handle_data;
s->dev.handle_destroy = usb_hid_handle_destroy;
- s->kind = USB_TABLET;
- /* Force poll routine to be run and grab input the first time. */
- s->changed = 1;
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
+ s->kind = kind;
+ s->ptr.xyrel = xyrel;
+ usb_pointer_handle_reset((USBDevice*)s);
+ /* Force poll routine to be run and grab input the first time. */
+ usb_pointer_event_clear(&s->ptr.queue[0], 0);
+ s->ptr.tail = 1;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), devname);
return (USBDevice *)s;
}
-USBDevice *usb_mouse_init(void)
+USBDevice *usb_tablet_init(void)
{
- USBHIDState *s;
-
- s = qemu_mallocz(sizeof(USBHIDState));
- s->dev.speed = USB_SPEED_FULL;
- s->dev.handle_packet = usb_generic_handle_packet;
-
- s->dev.handle_reset = usb_mouse_handle_reset;
- s->dev.handle_control = usb_hid_handle_control;
- s->dev.handle_data = usb_hid_handle_data;
- s->dev.handle_destroy = usb_hid_handle_destroy;
- s->kind = USB_MOUSE;
- /* Force poll routine to be run and grab input the first time. */
- s->changed = 1;
-
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+ return usb_pointer_init(USB_TABLET, 0, "QEMU USB Tablet");
+}
- return (USBDevice *)s;
+USBDevice *usb_mouse_init(void)
+{
+ return usb_pointer_init(USB_MOUSE, 1, "QEMU USB Mouse");
}
USBDevice *usb_keyboard_init(void)
@@ -901,6 +931,7 @@ USBDevice *usb_keyboard_init(void)
s->dev.handle_data = usb_hid_handle_data;
s->dev.handle_destroy = usb_hid_handle_destroy;
s->kind = USB_KEYBOARD;
+ s->kbd.changed= 0;
pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard");