From 09b26c5ec06ff7531a2a2a7b1146011c87285f81 Mon Sep 17 00:00:00 2001 From: bellard Date: Wed, 12 Apr 2006 21:09:08 +0000 Subject: [PATCH] USB tablet support (Brad Campbell, Anthony Liguori) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1810 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/ps2.c | 2 +- hw/usb-hid.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- hw/usb.h | 1 + sdl.c | 50 +++++++++++-- vl.c | 13 +++- vl.h | 3 +- 6 files changed, 265 insertions(+), 24 deletions(-) diff --git a/hw/ps2.c b/hw/ps2.c index 40be044..8438a5e 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -560,7 +560,7 @@ void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) s->common.update_arg = update_arg; ps2_reset(&s->common); register_savevm("ps2mouse", 0, 2, ps2_mouse_save, ps2_mouse_load, s); - qemu_add_mouse_event_handler(ps2_mouse_event, s); + qemu_add_mouse_event_handler(ps2_mouse_event, s, 0); qemu_register_reset(ps2_reset, &s->common); return s; } diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 6f9ee82..17160eb 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -30,9 +30,15 @@ #define SET_IDLE 0x210a #define SET_PROTOCOL 0x210b +#define USB_MOUSE 1 +#define USB_TABLET 2 + typedef struct USBMouseState { USBDevice dev; int dx, dy, dz, buttons_state; + int x, y; + int kind; + int mouse_grabbed; } USBMouseState; /* mostly the same values as the Bochs USB Mouse device */ @@ -93,6 +99,15 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ 0x05, /* u8 if_iInterface; */ + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + /* one endpoint (status change endpoint) */ 0x07, /* u8 ep_bLength; */ 0x05, /* u8 ep_bDescriptorType; Endpoint */ @@ -100,6 +115,44 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x03, /* u8 ep_bmAttributes; Interrupt */ 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ /* HID descriptor */ 0x09, /* u8 bLength; */ @@ -108,7 +161,15 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x00, /* u8 country_code */ 0x01, /* u8 num_descriptors */ 0x22, /* u8 type; Report */ - 50, 0, /* u16 len */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; static const uint8_t qemu_mouse_hid_report_descriptor[] = { @@ -121,6 +182,46 @@ static const uint8_t qemu_mouse_hid_report_descriptor[] = { 0xC0, 0xC0, }; +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + static void usb_mouse_event(void *opaque, int dx1, int dy1, int dz1, int buttons_state) { @@ -132,6 +233,17 @@ static void usb_mouse_event(void *opaque, s->buttons_state = buttons_state; } +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + static inline int int_clamp(int val, int vmin, int vmax) { if (val < vmin) @@ -146,6 +258,11 @@ static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) { int dx, dy, dz, b, l; + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + dx = int_clamp(s->dx, -128, 127); dy = int_clamp(s->dy, -128, 127); dz = int_clamp(s->dz, -128, 127); @@ -173,6 +290,39 @@ static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) return l; } +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + 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; + + 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; + + return l; +} + static void usb_mouse_handle_reset(USBDevice *dev) { USBMouseState *s = (USBMouseState *)dev; @@ -180,6 +330,8 @@ static void usb_mouse_handle_reset(USBDevice *dev) s->dx = 0; s->dy = 0; s->dz = 0; + s->x = 0; + s->y = 0; s->buttons_state = 0; } @@ -187,7 +339,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBMouseState *s = (USBMouseState *)dev; - int ret; + int ret = 0; switch(request) { case DeviceRequest | USB_REQ_GET_STATUS: @@ -224,9 +376,15 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, ret = sizeof(qemu_mouse_dev_descriptor); break; case USB_DT_CONFIG: - memcpy(data, qemu_mouse_config_descriptor, - sizeof(qemu_mouse_config_descriptor)); - ret = sizeof(qemu_mouse_config_descriptor); + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } break; case USB_DT_STRING: switch(value & 0xff) { @@ -244,7 +402,10 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, break; case 2: /* product description */ - ret = set_usb_string(data, "QEMU USB Mouse"); + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); break; case 3: /* vendor description */ @@ -282,16 +443,25 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: switch(value >> 8) { case 0x22: - memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); - ret = sizeof(qemu_mouse_hid_report_descriptor); - break; + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; default: goto fail; } break; case GET_REPORT: - ret = usb_mouse_poll(s, data, length); + 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); break; case SET_IDLE: ret = 0; @@ -308,12 +478,15 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, uint8_t devep, uint8_t *data, int len) { USBMouseState *s = (USBMouseState *)dev; - int ret; + int ret = 0; switch(pid) { case USB_TOKEN_IN: if (devep == 1) { - ret = usb_mouse_poll(s, data, len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); } else { goto fail; } @@ -327,6 +500,24 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, return ret; } +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + 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_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_TABLET; + + return (USBDevice *)s; +} + USBDevice *usb_mouse_init(void) { USBMouseState *s; @@ -340,8 +531,7 @@ USBDevice *usb_mouse_init(void) s->dev.handle_reset = usb_mouse_handle_reset; s->dev.handle_control = usb_mouse_handle_control; s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_MOUSE; - qemu_add_mouse_event_handler(usb_mouse_event, s); - return (USBDevice *)s; } diff --git a/hw/usb.h b/hw/usb.h index b4dee23..05502e0 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -163,3 +163,4 @@ void usb_host_info(void); /* usb-hid.c */ USBDevice *usb_mouse_init(void); +USBDevice *usb_tablet_init(void); diff --git a/sdl.c b/sdl.c index b8c82fd..72a7080 100644 --- a/sdl.c +++ b/sdl.c @@ -39,6 +39,10 @@ static int gui_keysym; static int gui_fullscreen_initial_grab; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static uint8_t modifiers_state[256]; +static int width, height; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled = 0; static void sdl_update(DisplayState *ds, int x, int y, int w, int h) { @@ -56,6 +60,9 @@ static void sdl_resize(DisplayState *ds, int w, int h) if (gui_fullscreen) flags |= SDL_FULLSCREEN; + width = w; + height = h; + again: screen = SDL_SetVideoMode(w, h, 0, flags); if (!screen) { @@ -271,9 +278,21 @@ static void sdl_update_caption(void) SDL_WM_SetCaption(buf, "QEMU"); } +static void sdl_hide_cursor(void) +{ + SDL_SetCursor(sdl_cursor_hidden); +} + +static void sdl_show_cursor(void) +{ + if (!kbd_mouse_is_absolute()) { + SDL_SetCursor(sdl_cursor_normal); + } +} + static void sdl_grab_start(void) { - SDL_ShowCursor(0); + sdl_hide_cursor(); SDL_WM_GrabInput(SDL_GRAB_ON); /* dummy read to avoid moving the mouse */ SDL_GetRelativeMouseState(NULL, NULL); @@ -284,7 +303,7 @@ static void sdl_grab_start(void) static void sdl_grab_end(void) { SDL_WM_GrabInput(SDL_GRAB_OFF); - SDL_ShowCursor(1); + sdl_show_cursor(); gui_grab = 0; sdl_update_caption(); } @@ -300,6 +319,21 @@ static void sdl_send_mouse_event(int dz) buttons |= MOUSE_EVENT_RBUTTON; if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) buttons |= MOUSE_EVENT_MBUTTON; + + if (kbd_mouse_is_absolute()) { + if (!absolute_enabled) { + sdl_hide_cursor(); + if (gui_grab) { + sdl_grab_end(); + } + absolute_enabled = 1; + } + + SDL_GetMouseState(&dx, &dy); + dx = dx * 0x7FFF / width; + dy = dy * 0x7FFF / height; + } + kbd_mouse_event(dx, dy, dz, buttons); } @@ -423,7 +457,7 @@ static void sdl_refresh(DisplayState *ds) qemu_system_shutdown_request(); break; case SDL_MOUSEMOTION: - if (gui_grab) { + if (gui_grab || kbd_mouse_is_absolute()) { sdl_send_mouse_event(0); } break; @@ -431,7 +465,7 @@ static void sdl_refresh(DisplayState *ds) case SDL_MOUSEBUTTONUP: { SDL_MouseButtonEvent *bev = &ev->button; - if (!gui_grab) { + if (!gui_grab && !kbd_mouse_is_absolute()) { if (ev->type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK)) { /* start grabbing all events */ @@ -441,9 +475,9 @@ static void sdl_refresh(DisplayState *ds) int dz; dz = 0; #ifdef SDL_BUTTON_WHEELUP - if (bev->button == SDL_BUTTON_WHEELUP) { + if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) { dz = -1; - } else if (bev->button == SDL_BUTTON_WHEELDOWN) { + } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) { dz = 1; } #endif @@ -471,6 +505,7 @@ static void sdl_cleanup(void) void sdl_display_init(DisplayState *ds, int full_screen) { int flags; + uint8_t data = 0; #if defined(__APPLE__) /* always use generic keymaps */ @@ -504,6 +539,9 @@ void sdl_display_init(DisplayState *ds, int full_screen) SDL_EnableUNICODE(1); gui_grab = 0; + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + atexit(sdl_cleanup); if (full_screen) { gui_fullscreen = 1; diff --git a/vl.c b/vl.c index 83c6716..8fba6e7 100644 --- a/vl.c +++ b/vl.c @@ -474,6 +474,7 @@ static QEMUPutKBDEvent *qemu_put_kbd_event; static void *qemu_put_kbd_event_opaque; static QEMUPutMouseEvent *qemu_put_mouse_event; static void *qemu_put_mouse_event_opaque; +static int qemu_put_mouse_event_absolute; void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { @@ -481,10 +482,11 @@ void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) qemu_put_kbd_event = func; } -void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque) +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute) { qemu_put_mouse_event_opaque = opaque; qemu_put_mouse_event = func; + qemu_put_mouse_event_absolute = absolute; } void kbd_put_keycode(int keycode) @@ -502,6 +504,11 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) } } +int kbd_mouse_is_absolute(void) +{ + return qemu_put_mouse_event_absolute; +} + /***********************************************************/ /* timers */ @@ -3242,6 +3249,10 @@ static int usb_device_add(const char *devname) dev = usb_mouse_init(); if (!dev) return -1; + } else if (!strcmp(devname, "tablet")) { + dev = usb_tablet_init(); + if (!dev) + return -1; } else { return -1; } diff --git a/vl.h b/vl.h index 9b09fa4..16d7e29 100644 --- a/vl.h +++ b/vl.h @@ -158,10 +158,11 @@ typedef void QEMUPutKBDEvent(void *opaque, int keycode); typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); -void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque); +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute); void kbd_put_keycode(int keycode); void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); +int kbd_mouse_is_absolute(void); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ -- 1.7.9.5