qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] usb-tablet support


From: Brad Campbell
Subject: [Qemu-devel] usb-tablet support
Date: Tue, 11 Apr 2006 02:28:15 +0400
User-agent: Thunderbird 1.6a1 (X11/20060214)

G'day all,

This takes Anthony Liguori's <address@hidden> patch from this morning and rolls into it my hid descriptor fixes and the wheel sdl double step fix.

<quote from Anthonys original post>

I spent some time cleaning this all up. The following integrates Brad's patches and the patch from http://gnome.dnsalias.net/patches/qemu-hidmousexp.patch

It adds a new emulated USB device that reports absolute coordinates. It also modifies SDL to operate in grabless mode when an absolute input device is enabled. I think it's pretty close to apply-able. To use, just specify: -usbdevice tablet

With Xorg from CVS, the evdev driver segfaults. This is apparently expected behavior. Hopefully it will be fixed soon. It works quite nicely under Win2k.

</quote>

I've now comprehensively tested this with Win98, Win2k and WinXP.
I suspect it's ready to go in.


Regards,
Brad
--
"Human beings, who are almost unique in having the ability
to learn from the experience of others, are also remarkable
for their apparent disinclination to do so." -- Douglas Adams
diff -urN qemu-clean2/hw/ps2.c qemu-clean3/hw/ps2.c
--- qemu-clean2/hw/ps2.c        2006-04-08 18:12:31.000000000 +0400
+++ qemu-clean3/hw/ps2.c        2006-04-11 02:20:54.000000000 +0400
@@ -560,7 +560,7 @@
     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 -urN qemu-clean2/hw/usb.h qemu-clean3/hw/usb.h
--- qemu-clean2/hw/usb.h        2005-11-06 20:13:29.000000000 +0400
+++ qemu-clean3/hw/usb.h        2006-04-11 02:20:54.000000000 +0400
@@ -163,3 +163,4 @@
 
 /* usb-hid.c */
 USBDevice *usb_mouse_init(void);
+USBDevice *usb_tablet_init(void);
diff -urN qemu-clean2/hw/usb-hid.c qemu-clean3/hw/usb-hid.c
--- qemu-clean2/hw/usb-hid.c    2005-11-05 20:57:08.000000000 +0400
+++ qemu-clean3/hw/usb-hid.c    2006-04-11 02:21:37.000000000 +0400
@@ -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 @@
        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 @@
        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 @@
         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 @@
     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 @@
     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 @@
 {
     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 @@
     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 @@
     s->dx = 0;
     s->dy = 0;
     s->dz = 0;
+    s->x = 0;
+    s->y = 0;
     s->buttons_state = 0;
 }
 
@@ -187,7 +339,7 @@
                                   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 @@
             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 @@
                 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 @@
     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 @@
                                  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 @@
     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 @@
     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 -urN qemu-clean2/sdl.c qemu-clean3/sdl.c
--- qemu-clean2/sdl.c   2006-04-10 23:49:43.000000000 +0400
+++ qemu-clean3/sdl.c   2006-04-11 02:21:43.000000000 +0400
@@ -39,6 +39,10 @@
 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 @@
     if (gui_fullscreen)
         flags |= SDL_FULLSCREEN;
 
+    width = w;
+    height = h;
+
  again:
     screen = SDL_SetVideoMode(w, h, 0, flags);
     if (!screen) {
@@ -271,9 +278,21 @@
     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_end(void)
 {
     SDL_WM_GrabInput(SDL_GRAB_OFF);
-    SDL_ShowCursor(1);
+    sdl_show_cursor();
     gui_grab = 0;
     sdl_update_caption();
 }
@@ -300,6 +319,21 @@
         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 @@
             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 @@
         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 @@
                     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 @@
 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 @@
     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 -urN qemu-clean2/vl.c qemu-clean3/vl.c
--- qemu-clean2/vl.c    2006-04-10 23:49:52.000000000 +0400
+++ qemu-clean3/vl.c    2006-04-11 02:20:55.000000000 +0400
@@ -474,6 +474,7 @@
 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 @@
     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 @@
     }
 }
 
+int kbd_mouse_is_absolute(void)
+{
+    return qemu_put_mouse_event_absolute;
+}
+
 /***********************************************************/
 /* timers */
 
@@ -2855,6 +2862,10 @@
         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 -urN qemu-clean2/vl.h qemu-clean3/vl.h
--- qemu-clean2/vl.h    2006-04-10 23:49:52.000000000 +0400
+++ qemu-clean3/vl.h    2006-04-11 02:20:55.000000000 +0400
@@ -158,10 +158,11 @@
 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) */

reply via email to

[Prev in Thread] Current Thread [Next in Thread]