[gtk+/multitouch: 45/121] gdk: Generate crossing events around touch devices' press/release



commit fe3b7ce426c72591f344903810b07b26b3dda80c
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sat Dec 3 15:11:08 2011 +0100

    gdk: Generate crossing events around touch devices' press/release
    
    Anytime a touch device interacts, the crossing events generation
    will change to a touch mode where only events with mode
    GDK_CROSSING_TOUCH_PRESS/RELEASE are handled, and those are sent
    around button press/release. Those are virtual as the master
    device may still stay on the window.
    
    Whenever there is a switch of slave device (the user starts
    using another non-touch device), a crossing event with mode
    GDK_CROSSING_DEVICE_SWITCH may generated if needed, and the normal
    crossing event handling is resumed.

 gdk/gdkdisplay.c        |   28 ++++++++++++-----
 gdk/gdkdisplayprivate.h |    1 +
 gdk/gdkevents.h         |   14 ++++++++-
 gdk/gdkwindow.c         |   75 +++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 105 insertions(+), 13 deletions(-)
---
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 702f6b4..48a556c 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -897,15 +897,25 @@ switch_to_pointer_grab (GdkDisplay        *display,
 
       if (grab == NULL) /* Ungrabbed, send events */
 	{
-	  pointer_window = NULL;
-	  if (new_toplevel)
-	    {
-	      /* Find (possibly virtual) child window */
-	      pointer_window =
-		_gdk_window_find_descendant_at (new_toplevel,
-						x, y,
-						NULL, NULL);
-	    }
+          /* If the source device is a touch device, do not
+           * propagate any enter event yet, until one is
+           * synthesized when needed.
+           */
+          if (source_device &&
+              gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
+            info->need_touch_press_enter = TRUE;
+
+          pointer_window = NULL;
+
+          if (new_toplevel &&
+              !info->need_touch_press_enter)
+            {
+              /* Find (possibly virtual) child window */
+              pointer_window =
+                _gdk_window_find_descendant_at (new_toplevel,
+                                                x, y,
+                                                NULL, NULL);
+            }
 
 	  if (pointer_window != last_grab->window)
             synthesize_crossing_events (display, device, source_device,
diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h
index 8084332..d73449e 100644
--- a/gdk/gdkdisplayprivate.h
+++ b/gdk/gdkdisplayprivate.h
@@ -76,6 +76,7 @@ typedef struct
   guint32 state;
   guint32 button;
   GdkDevice *last_slave;
+  guint need_touch_press_enter : 1;
 } GdkPointerWindowInfo;
 
 typedef struct
diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
index d8a1f76..33cd8b0 100644
--- a/gdk/gdkevents.h
+++ b/gdk/gdkevents.h
@@ -354,6 +354,15 @@ typedef enum
  * @GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
  * @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
  *   state (e.g. sensitivity).
+ * @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
+ *   state (e.g. sensitivity).
+ * @GDK_CROSSING_TOUCH_PRESS: crossing because a touch device was pressed,
+ *   this event is synthetic as the pointer might have not left the window.
+ * @GDK_CROSSING_TOUCH_RELEASE: crossing because a touch device was released.
+ *   this event is synthetic as the pointer might have not left the window.
+ * @GDK_CROSSING_DEVICE_SWITCH: crossing because of a device switch (i.e.
+ *   a mouse taking control of the pointer after a touch device), this event
+ *   is synthetic as the pointer didn't leave the window.
  *
  * Specifies the crossing mode for #GdkEventCrossing.
  */
@@ -364,7 +373,10 @@ typedef enum
   GDK_CROSSING_UNGRAB,
   GDK_CROSSING_GTK_GRAB,
   GDK_CROSSING_GTK_UNGRAB,
-  GDK_CROSSING_STATE_CHANGED
+  GDK_CROSSING_STATE_CHANGED,
+  GDK_CROSSING_TOUCH_PRESS,
+  GDK_CROSSING_TOUCH_RELEASE,
+  GDK_CROSSING_DEVICE_SWITCH
 } GdkCrossingMode;
 
 /**
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index de6e4b6..2aabcfd 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -8335,9 +8335,11 @@ send_crossing_event (GdkDisplay                 *display,
   GdkEvent *event;
   guint32 window_event_mask, type_event_mask;
   GdkDeviceGrabInfo *grab;
+  GdkPointerWindowInfo *pointer_info;
   gboolean block_event = FALSE;
 
   grab = _gdk_display_has_device_grab (display, device, serial);
+  pointer_info = _gdk_display_get_pointer_info (display, device);
 
   if (grab != NULL &&
       !grab->owner_events)
@@ -8350,7 +8352,13 @@ send_crossing_event (GdkDisplay                 *display,
   else
     window_event_mask = window->event_mask;
 
-  if (type == GDK_LEAVE_NOTIFY)
+  if (pointer_info->need_touch_press_enter &&
+      mode != GDK_CROSSING_TOUCH_PRESS &&
+      mode != GDK_CROSSING_TOUCH_RELEASE)
+    {
+      block_event = TRUE;
+    }
+  else if (type == GDK_LEAVE_NOTIFY)
     {
       type_event_mask = GDK_LEAVE_NOTIFY_MASK;
       window->devices_inside = g_list_remove (window->devices_inside, device);
@@ -9174,7 +9182,7 @@ proxy_pointer_event (GdkDisplay                 *display,
   guint state;
   gdouble toplevel_x, toplevel_y;
   guint32 time_;
-  gboolean non_linear;
+  gboolean non_linear, need_synthetic_enter = FALSE;
 
   event_window = source_event->any.window;
   gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
@@ -9194,6 +9202,13 @@ proxy_pointer_event (GdkDisplay                 *display,
        source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL))
     non_linear = TRUE;
 
+  if (pointer_info->need_touch_press_enter &&
+      gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCH)
+    {
+      pointer_info->need_touch_press_enter = FALSE;
+      need_synthetic_enter = TRUE;
+    }
+
   /* If we get crossing events with subwindow unexpectedly being NULL
      that means there is a native subwindow that gdk doesn't know about.
      We track these and forward them, with the correct virtual window
@@ -9317,6 +9332,18 @@ proxy_pointer_event (GdkDisplay                 *display,
           gdk_window_get_device_events (event_win, device) == 0)
         return TRUE;
 
+      /* The last device to interact with the window was a touch device,
+       * which synthesized a leave notify event, so synthesize another enter
+       * notify to tell the pointer is on the window.
+       */
+      if (need_synthetic_enter)
+        _gdk_synthesize_crossing_events (display,
+                                         NULL, pointer_window,
+                                         device, source_device,
+                                         GDK_CROSSING_DEVICE_SWITCH,
+                                         toplevel_x, toplevel_y,
+                                         state, time_, NULL,
+                                         serial, FALSE);
       is_hint = FALSE;
 
       if (event_win &&
@@ -9375,6 +9402,7 @@ proxy_button_event (GdkEvent *source_event,
   GdkWindow *pointer_window;
   GdkWindow *parent;
   GdkEvent *event;
+  GdkPointerWindowInfo *pointer_info;
   guint state;
   guint32 time_;
   GdkEventType type;
@@ -9394,6 +9422,7 @@ proxy_button_event (GdkEvent *source_event,
   toplevel_window = convert_native_coords_to_toplevel (event_window,
 						       toplevel_x, toplevel_y,
 						       &toplevel_x, &toplevel_y);
+  pointer_info = _gdk_display_get_pointer_info (display, device);
 
   if (type == GDK_BUTTON_PRESS &&
       !source_event->any.send_event &&
@@ -9446,6 +9475,30 @@ proxy_button_event (GdkEvent *source_event,
       gdk_window_get_device_events (event_win, device) == 0)
     return TRUE;
 
+  if (type == GDK_BUTTON_PRESS &&
+      pointer_info->need_touch_press_enter)
+    {
+      GdkCrossingMode mode;
+
+      /* The last device to interact with the window was a touch device,
+       * which synthesized a leave notify event, so synthesize another enter
+       * notify to tell the pointer is on the window.
+       */
+      if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
+        mode = GDK_CROSSING_TOUCH_PRESS;
+      else
+        mode = GDK_CROSSING_DEVICE_SWITCH;
+
+      pointer_info->need_touch_press_enter = FALSE;
+      _gdk_synthesize_crossing_events (display,
+                                       NULL,
+                                       pointer_info->window_under_pointer,
+                                       device, source_device, mode,
+                                       toplevel_x, toplevel_y,
+                                       state, time_, source_event,
+                                       serial, FALSE);
+    }
+
   event = _gdk_make_event (event_win, type, source_event, FALSE);
 
   switch (type)
@@ -9466,7 +9519,23 @@ proxy_button_event (GdkEvent *source_event,
       gdk_event_set_source_device (event, source_device);
 
       if (type == GDK_BUTTON_PRESS)
-	_gdk_event_button_generate (display, event);
+        _gdk_event_button_generate (display, event);
+      else if (type == GDK_BUTTON_RELEASE &&
+               pointer_window == pointer_info->window_under_pointer &&
+               gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
+        {
+          /* Synthesize a leave notify event
+           * whenever a touch device is released
+           */
+          pointer_info->need_touch_press_enter = TRUE;
+          _gdk_synthesize_crossing_events (display,
+                                           pointer_window, NULL,
+                                           device, source_device,
+                                           GDK_CROSSING_TOUCH_RELEASE,
+                                           toplevel_x, toplevel_y,
+                                           state, time_, NULL,
+                                           serial, FALSE);
+        }
       return TRUE;
 
     case GDK_SCROLL:



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