[Title] Emulator multitouch supporting
authorgiwoong.kim <giwoong.kim@samsung.com>
Thu, 8 Dec 2011 04:55:06 +0000 (13:55 +0900)
committergiwoong.kim <giwoong.kim@samsung.com>
Thu, 8 Dec 2011 04:55:06 +0000 (13:55 +0900)
[Type] Feature
[Module] Emulator / touch
[Priority] Major
[CQ#] n/a
[Redmine#] 2184
[Problem] no support fot multitouch
[Cause] n/a
[Solution] implement multitouch protocol
[TestCase] Ctrl key + mouse click

hw/usb-wacom.c [changed mode: 0644->0755]
tizen/src/event_handler.c [changed mode: 0644->0755]
tizen/src/qemu_gtk_widget.c [changed mode: 0644->0755]
tizen/src/qemu_gtk_widget.h [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 5397894..300df7b
@@ -148,7 +148,8 @@ static void usb_wacom_event(void *opaque,
     /* scale to Penpartner resolution */
     s->x = (x * 5040 / 0x7FFF);
     s->y = (y * 3780 / 0x7FFF);
-    s->dz += dz;
+    //s->dz += dz;
+    s->dz = dz;
     s->buttons_state = buttons_state;
     s->changed = 1;
 }
@@ -224,7 +225,7 @@ static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
         return 0;
 
     buf[0] = s->mode;
-    buf[5] = 0x00 | (b & 0xf0);
+    buf[5] = s->dz; //00 | (b & 0xf0);
     buf[1] = s->x & 0xff;
     buf[2] = s->x >> 8;
     buf[3] = s->y & 0xff;
old mode 100644 (file)
new mode 100755 (executable)
index 27e9662..bff8f60
@@ -88,6 +88,7 @@ static int gui_grab_code = gtk_alt_left | gtk_control_left;
 static int gui_key_modifier_pressed;
 static int gui_keysym;
 static kbd_layout_t *kbd_layout = NULL;
+extern multi_touch_state qemu_mts;
 
 
 static uint8_t gtk_keyevent_to_keycode_generic(const GdkEventKey *event)
@@ -344,6 +345,7 @@ static void reset_keys(void)
 static void gtk_process_key(GdkEventKey *event)
 {
        int keycode, v;
+       int i;
 
        if (event->keyval == GDK_Pause) {
                /* specific case */
@@ -371,6 +373,19 @@ static void gtk_process_key(GdkEventKey *event)
        case 0x2a:                          /* Left Shift */
        case 0x36:                          /* Right Shift */
        case 0x1d:                          /* Left CTRL */
+               if (event->type == GDK_KEY_RELEASE) {
+                       qemu_mts.multitouch_enable = 0;
+
+                       if (qemu_mts.finger_cnt > 0) {
+                               for (i = 0; i < qemu_mts.finger_cnt; i++) {
+                                       kbd_mouse_event(qemu_mts.finger_slot[i].dx, qemu_mts.finger_slot[i].dy, i, 0);
+                               }
+                               qemu_mts.finger_cnt = 0;
+                       }
+               } else {
+                       qemu_mts.multitouch_enable = 1;
+               }
+
        case 0x9d:                          /* Right CTRL */
        case 0x38:                          /* Left ALT */
        case 0xb8:                         /* Right ALT */
@@ -581,10 +596,36 @@ gboolean key_event_handler (GtkWidget *wid, GdkEventKey *event)
        return TRUE;
 }
 
+static void do_multitouch_prossesing(int x, int y, int dx, int dy, int dz, int touch_type)
+{
+       if (touch_type == 1 && qemu_mts.finger_cnt < MAX_MULTI_TOUCH_CNT) {
+               qemu_mts.finger_slot[qemu_mts.finger_cnt].x = x; //skin position
+               qemu_mts.finger_slot[qemu_mts.finger_cnt].y = y;
+               qemu_mts.finger_slot[qemu_mts.finger_cnt].dx = dx; //lcd position
+               qemu_mts.finger_slot[qemu_mts.finger_cnt].dy = dy;
+
+               dz = qemu_mts.finger_cnt;
+               //fprintf(stderr, "!!!!! dx=%d, dy=%d, dz=%d, touch_type=%d\n", dx, dy, dz, touch_type);
+               kbd_mouse_event(dx, dy, dz, 1);
+
+               qemu_mts.finger_cnt++;
+       } else if (touch_type == 1 && qemu_mts.finger_cnt == MAX_MULTI_TOUCH_CNT) {
+               //move last touch point
+               qemu_mts.finger_slot[MAX_MULTI_TOUCH_CNT - 1].x = x;
+               qemu_mts.finger_slot[MAX_MULTI_TOUCH_CNT - 1].y = y;
+               qemu_mts.finger_slot[MAX_MULTI_TOUCH_CNT - 1].dx = dx;
+               qemu_mts.finger_slot[MAX_MULTI_TOUCH_CNT - 1].dy = dy;
+               dz = qemu_mts.finger_cnt - 1;
+               //fprintf(stderr, "!!!!! dx=%d, dy=%d, dz=%d, touch_type=%d\n", dx, dy, dz, touch_type);
+               kbd_mouse_event(dx, dy, dz, 1);
+       }
+}
+
 static int button_status = -1;
-static void touch_shoot_for_type(int x, int y, int lcd_status, int touch_type)
+static void touch_shoot_for_type(GtkWidget *widget, int x, int y, int lcd_status, int touch_type)
 {
        int dx, dy, dz = 0, lcd_height, lcd_width;
+       int i;
        GtkWidget *pWidget = NULL;
        GtkWidget *popup_menu = get_widget(EMULATOR_ID, POPUP_MENU);
        lcd_height = (int)(PHONE.mode[UISTATE.current_mode].lcd_list[lcd_status].lcd_region.h);
@@ -659,26 +700,35 @@ static void touch_shoot_for_type(int x, int y, int lcd_status, int touch_type)
 
        /* when portrait */
        pWidget = g_object_get_data((GObject *) popup_menu, PORTRAIT);
-       if(pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
-               if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(pWidget)) == TRUE)  /* touch drag process */
-                       kbd_mouse_event(dx, dy, dz, touch_type);
+       if (pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
+               if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(pWidget)) == TRUE) { /* touch drag process */
+                       if (qemu_mts.multitouch_enable == 1) {
+                               /* ctrl key pressed */
+                               do_multitouch_prossesing(x, y, dx, dy, dz, touch_type);
+                       } else {
+                               kbd_mouse_event(dx, dy, dz, touch_type);
+                       }
+               }
        }
-       else
-               kbd_mouse_event(dx, dy, dz, touch_type);
+
        /* when landscape */
        pWidget = g_object_get_data((GObject *) popup_menu, LANDSCAPE);
-       if(pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
+       if (pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
                if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(pWidget)) == TRUE) {
-                       if (qemu_arch_is_arm()){
+                       if (qemu_arch_is_arm()) {
                                /* here dx comes dy and since it is (dy - 0x7FFF - lcd_height - 1) * -1) becomes dx
                                   am subtracting 0x7fff because it was multiplied earlier, No idea why 0x7fff is used
                                   Similar comments for other rotations
                                */
+                               //todo : multitouch
                                kbd_mouse_event(((dy - 0x7FFF - lcd_height - 1) * -1) , dx, dz, touch_type);
-                       }
-                       else
-                       {
-                               kbd_mouse_event(dx, dy, dz, touch_type);
+                       } else {
+                               if (qemu_mts.multitouch_enable == 1) {
+                                       /* ctrl key pressed */
+                                       do_multitouch_prossesing(x, y, dx, dy, dz, touch_type);
+                               } else {
+                                       kbd_mouse_event(dx, dy, dz, touch_type);
+                               }
                        }
                }
        }
@@ -687,21 +737,35 @@ static void touch_shoot_for_type(int x, int y, int lcd_status, int touch_type)
        pWidget = g_object_get_data((GObject *) popup_menu, REVERSE_PORTRAIT);
        if(pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
                if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(pWidget)) == TRUE) {
-                               if (qemu_arch_is_arm())
-                                       kbd_mouse_event((dx - 0x7FFF - lcd_width - 1)*-1, (dy -0x7FFF) * -1, dz, touch_type);
-                               else
+                       if (qemu_arch_is_arm()) {
+                               //todo : multitouch
+                               kbd_mouse_event((dx - 0x7FFF - lcd_width - 1)*-1, (dy -0x7FFF) * -1, dz, touch_type);
+                       } else {
+                               if (qemu_mts.multitouch_enable == 1) {
+                                       /* ctrl key pressed */
+                                       do_multitouch_prossesing(x, y, dx, dy, dz, touch_type);
+                               } else {
                                        kbd_mouse_event(dx, dy, dz, touch_type);
+                               }
+                       }
                }
        }
 
        /* when reverse landscape */
        pWidget = g_object_get_data((GObject *) popup_menu, REVERSE_LANDSCAPE);
-       if(pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
+       if (pWidget && GTK_IS_CHECK_MENU_ITEM(pWidget)) {
                if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(pWidget)) == TRUE) {
-                               if (qemu_arch_is_arm())
-                                       kbd_mouse_event(dy, (dx - 0x7FFF)*-1, dz, touch_type);
-                               else
+                       if (qemu_arch_is_arm()) {
+                               //todo : multitouch
+                               kbd_mouse_event(dy, (dx - 0x7FFF)*-1, dz, touch_type);
+                       } else {
+                               if (qemu_mts.multitouch_enable == 1) {
+                                       /* ctrl key pressed */
+                                       do_multitouch_prossesing(x, y, dx, dy, dz, touch_type);
+                               } else {
                                        kbd_mouse_event(dx, dy, dz, touch_type);
+                               }
+                       }
                }
        }
 }
@@ -845,7 +909,7 @@ gint motion_notify_event_handler(GtkWidget *widget, GdkEventButton *event, gpoin
 
                if (event->type == GDK_MOTION_NOTIFY) 
                        if (lcd_status == LCD_REGION || lcd_status == DUAL_LCD_REGION)  
-                               touch_shoot_for_type(dx, dy, lcd_status, TOUCH_DRAG);
+                               touch_shoot_for_type(widget, dx, dy, lcd_status, TOUCH_DRAG);
        }
        
        /* 4. when event press */
@@ -857,7 +921,7 @@ gint motion_notify_event_handler(GtkWidget *widget, GdkEventButton *event, gpoin
                if (lcd_status == LCD_REGION || lcd_status == DUAL_LCD_REGION) {        
 
                        lcd_press_flag = TRUE;                  
-                       touch_shoot_for_type(dx, dy, lcd_status, TOUCH_PRESS);
+                       touch_shoot_for_type(widget, dx, dy, lcd_status, TOUCH_PRESS);
                }
 
                /* 5.2  when event is not in lcd region (keycode region) */
@@ -900,7 +964,7 @@ gint motion_notify_event_handler(GtkWidget *widget, GdkEventButton *event, gpoin
                /* 5.1  when event is in lcd region (touch region) */           
 
                if (lcd_status == LCD_REGION || lcd_status == DUAL_LCD_REGION)  
-                       touch_shoot_for_type(dx, dy, lcd_status, TOUCH_RELEASE);
+                       touch_shoot_for_type(widget, dx, dy, lcd_status, TOUCH_RELEASE);
 
                /* 5.2  when event is not in lcd region (keycode region) */
 
old mode 100644 (file)
new mode 100755 (executable)
index 8b86412..1abaf35
@@ -37,6 +37,7 @@
 #include "qemu_gtk_widget.h"
 #include "utils.h"
 #include <pthread.h>
+#include "cursor_left_ptr.xpm"
 
 #include "debug_ch.h"
 #include "../ui/sdl_rotate.h"
@@ -62,6 +63,8 @@ qemu_state_t *qemu_state;
 static int widget_exposed;
 int qemu_state_initialized = 0;
 static pthread_mutex_t sdl_mutex = PTHREAD_MUTEX_INITIALIZER;
+static SDL_Cursor* sdl_cursor_normal;
+multi_touch_state qemu_mts;
 
 #define SDL_THREAD
 
@@ -372,12 +375,175 @@ static void qemu_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocat
 }
 
 
+static SDL_Cursor *sdl_cursor_init(const char *image[])
+{
+       int i, row, col;
+           Uint8 data[4*32];
+           Uint8 mask[4*32];
+           int w, h;
+
+           sscanf(image[0], "%d %d", &w, &h);
+
+           i = -1;
+           for ( row=0; row<32; ++row ) {
+               for ( col=0; col<32; ++col ) {
+                   if ( col % 8 ) {
+                       data[i] <<= 1;
+                       mask[i] <<= 1;
+                   } else {
+                       ++i;
+                       data[i] = mask[i] = 0;
+                   }
+                   switch (image[4+row][col]) {
+                       case 'X':
+                           data[i] |= 0x01;
+                           mask[i] |= 0x01;
+                           break;
+                       case '.':
+                           mask[i] |= 0x01;
+                           break;
+                       case ' ':
+                           break;
+                   }
+               }
+           }
+           return SDL_CreateCursor(data, mask, 32, 32, 0, 0);
+}
+
+/*
+ * This is a 32-bit pixel function created with help from this
+* website: http://www.libsdl.org/intro.en/usingvideo.html
+*
+* You will need to make changes if you want it to work with
+* 8-, 16- or 24-bit surfaces.  Consult the above website for
+* more information.
+*/
+void sdl_set_pixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {
+   Uint8 *target_pixel = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
+   *(Uint32 *)target_pixel = pixel;
+}
+
+/*
+* This is an implementation of the Midpoint Circle Algorithm 
+* found on Wikipedia at the following link:
+*
+*   http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+*
+* The algorithm elegantly draws a circle quickly, using a
+* set_pixel function for clarity.
+*/
+void sdl_draw_circle(SDL_Surface *surface, int cx, int cy, int radius, Uint32 pixel) {
+   int error = -radius;
+   int x = radius;
+   int y = 0;
+
+   while (x >= y)
+   {
+       sdl_set_pixel(surface, cx + x, cy + y, pixel);
+       sdl_set_pixel(surface, cx + y, cy + x, pixel);
+           
+       if (x != 0)
+       {
+           sdl_set_pixel(surface, cx - x, cy + y, pixel);
+           sdl_set_pixel(surface, cx + y, cy - x, pixel);
+       }
+       
+       if (y != 0)
+       {
+           sdl_set_pixel(surface, cx + x, cy - y, pixel);
+           sdl_set_pixel(surface, cx - y, cy + x, pixel);
+       }
+       
+       if (x != 0 && y != 0)
+       {
+           sdl_set_pixel(surface, cx - x, cy - y, pixel);
+           sdl_set_pixel(surface, cx - y, cy - x, pixel);
+       }
+           
+       error += y;
+       ++y;
+       error += y;
+
+       if (error >= 0)
+       {
+           --x;
+           error -= x;
+           error -= x;
+       }
+   }
+}
+
+/*
+ * SDL_Surface 32-bit circle-fill algorithm without using trig
+*
+* While I humbly call this "Celdecea's Method", odds are that the
+* procedure has already been documented somewhere long ago.  All of
+* the circle-fill examples I came across utilized trig functions or
+* scanning neighbor pixels.  This algorithm identifies the width of
+* a semi-circle at each pixel height and draws a scan-line covering
+* that width.
+*
+* The code is not optimized but very fast, owing to the fact that it
+* alters pixels in the provided surface directly rather than through
+* function calls.
+*
+* WARNING:  This function does not lock surfaces before altering, so
+* use SDL_LockSurface in any release situation.
+*/
+static void sdl_fill_circle(SDL_Surface *surface, int cx, int cy, int radius, Uint32 pixel)
+{
+   // Note that there is more to altering the bitrate of this
+   // method than just changing this value.  See how pixels are
+   // altered at the following web page for tips:
+   //   http://www.libsdl.org/intro.en/usingvideo.html
+   const int bpp = 4;
+   double dy;
+
+   double r = (double)radius;
+
+   for (dy = 1; dy <= r; dy += 1.0)
+   {
+       // This loop is unrolled a bit, only iterating through half of the
+       // height of the circle.  The result is used to draw a scan line and
+       // its mirror image below it.
+       // The following formula has been simplified from our original.  We
+       // are using half of the width of the circle because we are provided
+       // with a center and we need left/right coordinates.
+       double dx = floor(sqrt((2.0 * r * dy) - (dy * dy)));
+       int x = cx - dx;
+       // Grab a pointer to the left-most pixel for each half of the circle
+       Uint8 *target_pixel_a = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * bpp;
+       Uint8 *target_pixel_b = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * bpp;
+
+
+
+       for (; x <= cx + dx; x++)
+       {
+           *(Uint32 *)target_pixel_a = pixel;
+           *(Uint32 *)target_pixel_b = pixel;
+           target_pixel_a += bpp;
+           target_pixel_b += bpp;
+       }
+   }
+}
+
+static void qemu_sdl_cleanup(void)
+{
+    if (sdl_cursor_normal)
+        SDL_FreeCursor(sdl_cursor_normal);
+
+    SDL_FreeSurface(qemu_mts.finger_point);
+
+    SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
 static void qemu_sdl_init(qemu_state_t *qemu_state)
 {
        GtkWidget *qw = GTK_WIDGET(qemu_state);
        gchar SDL_windowhack[32];
        SDL_SysWMinfo info;
        long window;
+       int temp;
 
        if (use_qemu_display)
                return;
@@ -398,6 +564,21 @@ static void qemu_sdl_init(qemu_state_t *qemu_state)
                exit(1);
        }
 
+       /* cursor init */
+       sdl_cursor_normal = sdl_cursor_init(cursor_left_ptr_xpm);
+       SDL_SetCursor(sdl_cursor_normal);
+
+       /* finger point surface init */
+       qemu_mts.finger_point_size = DEFAULT_FINGER_POINT_SIZE;
+       temp = qemu_mts.finger_point_size / 2;
+       qemu_mts.finger_point = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
+               qemu_mts.finger_point_size + 2, qemu_mts.finger_point_size + 2, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
+       
+       sdl_fill_circle(qemu_mts.finger_point, temp, temp, temp, DEFAULT_FINGER_POINT_COLOR); //finger point
+       sdl_draw_circle(qemu_mts.finger_point, temp, temp, temp, 0xFF000000); // finger point outline
+
+       //atexit(qemu_sdl_cleanup); TODO:
+
        qemu_state->surface_screen = SDL_SetVideoMode(qemu_state->width,
                        qemu_state->height, 0, qemu_state->flags);
 
@@ -427,6 +608,8 @@ gint qemu_widget_expose (GtkWidget *widget, GdkEventExpose *event)
 
 static void qemu_update (qemu_state_t *qemu_state)
 {
+       int i = 0;
+       SDL_Rect r;
        SDL_Surface *surface  = NULL;
 
        if (!qemu_state->ds)
@@ -441,10 +624,33 @@ static void qemu_update (qemu_state_t *qemu_state)
 
        surface = SDL_GetVideoSurface ();
 
-       if ((qemu_state->scale == 1) && (UISTATE.current_mode %4 == 0))
-               SDL_BlitSurface(qemu_state->surface_qemu, NULL, qemu_state->surface_screen, NULL);
-       else
-       {
+       if (qemu_state->scale == 1) {
+               if (UISTATE.current_mode %4 != 0) { //rotation
+                       // work-around to remove afterimage on black color in Window and Ubuntu 11.10
+                       if( qemu_state->surface_qemu ) {
+                               // set color key 'magenta'
+                               qemu_state->surface_qemu->format->colorkey = 0xFF00FF;
+                       }
+
+                       SDL_Surface *rot_screen;
+                       rot_screen = rotozoomSurface(qemu_state->surface_qemu,
+                                       (UISTATE.current_mode %4) * 90, 1, SMOOTHING_ON);
+                       SDL_BlitSurface(rot_screen, NULL, qemu_state->surface_screen, NULL);
+                       
+                       SDL_FreeSurface(rot_screen);
+               } else {
+                       SDL_BlitSurface(qemu_state->surface_qemu, NULL, qemu_state->surface_screen, NULL);
+               }
+
+               /* draw finger points (multi-touch) */
+               for (i = 0; i < qemu_mts.finger_cnt; i++) {
+                       r.x = qemu_mts.finger_slot[i].x - (qemu_mts.finger_point_size / 2);
+                       r.y = qemu_mts.finger_slot[i].y - (qemu_mts.finger_point_size / 2);
+                       r.w = r.h = qemu_mts.finger_point_size;
+                       
+                       SDL_BlitSurface(qemu_mts.finger_point, NULL, qemu_state->surface_screen, &r);
+               }
+       } else { //resize
                // work-around to remove afterimage on black color in Window and Ubuntu 11.10
                if( qemu_state->surface_qemu ) {
                        // set color key 'magenta'
@@ -455,6 +661,16 @@ static void qemu_update (qemu_state_t *qemu_state)
                down_screen = rotozoomSurface(qemu_state->surface_qemu,
                                (UISTATE.current_mode %4) * 90, 1 / qemu_state->scale, SMOOTHING_ON);
                SDL_BlitSurface(down_screen, NULL, qemu_state->surface_screen, NULL);
+
+               /* draw finger points (multi-touch) */
+               for (i = 0; i < qemu_mts.finger_cnt; i++) {
+                       r.x = (qemu_mts.finger_slot[i].x - qemu_mts.finger_point_size) / qemu_state->scale;
+                       r.y = (qemu_mts.finger_slot[i].y - qemu_mts.finger_point_size) / qemu_state->scale;
+                       r.w = r.h = qemu_mts.finger_point_size;
+                       
+                       SDL_BlitSurface(qemu_mts.finger_point, NULL, qemu_state->surface_screen, &r);
+               }
+
                SDL_FreeSurface(down_screen);
        }
 
old mode 100644 (file)
new mode 100755 (executable)
index cc978e8..82fe20d
@@ -94,4 +94,22 @@ void qemu_display_init (DisplayState *ds);
 gint qemu_widget_new (GtkWidget **widget);
 gint qemu_widget_expose (GtkWidget *widget, GdkEventExpose *event);
 
+
+#define MAX_MULTI_TOUCH_CNT 2
+#define DEFAULT_FINGER_POINT_SIZE 32
+#define DEFAULT_FINGER_POINT_COLOR 0x7E0f0f0f
+
+typedef struct _finger_point {
+       int x; int y;
+       int dx; int dy;
+} finger_point;
+
+typedef struct _multi_touch_state {
+       int multitouch_enable;
+       int finger_cnt;
+       finger_point finger_slot[MAX_MULTI_TOUCH_CNT];
+       int finger_point_size;
+       SDL_Surface* finger_point;
+} multi_touch_state;
+
 #endif