]> Pileus Git - ~andy/gtk/commitdiff
gdk: Generate crossing events around touch devices' press/release
authorCarlos Garnacho <carlosg@gnome.org>
Sat, 3 Dec 2011 14:11:08 +0000 (15:11 +0100)
committerMatthias Clasen <mclasen@redhat.com>
Thu, 1 Mar 2012 21:25:21 +0000 (16:25 -0500)
Anytime a touch device interacts, the crossing events generation
will change to a touch mode where only events with mode
GDK_CROSSING_TOUCH_BEGIN/END are handled, and those are sent
around touch begin/end. 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
gdk/gdkdisplayprivate.h
gdk/gdkevents.h
gdk/gdkwindow.c

index b177f2afc9ccc3701016915f2ea78f53f3704ad8..bb4be5f6b254f08dd39885ba7e8131256af1f613 100644 (file)
@@ -895,15 +895,26 @@ 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_TOUCHSCREEN ||
+               gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
+            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,
index 7a1ccf8606e1211c6637ca09f8178d89f98208ac..62c2cc067b653ee87dc5e50d2da6998b4f9d79bc 100644 (file)
@@ -74,6 +74,7 @@ typedef struct
   guint32 state;
   guint32 button;
   GdkDevice *last_slave;
+  guint need_touch_press_enter : 1;
 } GdkPointerWindowInfo;
 
 typedef struct
index e0341526bf1087f29e31273737bc92e3c0f54899..e574bc019040ffc345586609d07dae85a81e53eb 100644 (file)
@@ -399,6 +399,13 @@ 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_TOUCH_BEGIN: crossing because a touch sequence has begun,
+ *   this event is synthetic as the pointer might have not left the window.
+ * @GDK_CROSSING_TOUCH_END: crossing because a touch sequence has ended,
+ *   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.
  */
@@ -409,7 +416,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_BEGIN,
+  GDK_CROSSING_TOUCH_END,
+  GDK_CROSSING_DEVICE_SWITCH
 } GdkCrossingMode;
 
 /**
index 854a9728cb81ec9a0acd16ad34aee3f65b364fe1..7ba32847f3cb4f6b4f7bc65e6b278e29385af4d4 100644 (file)
@@ -8337,9 +8337,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)
@@ -8352,7 +8354,14 @@ send_crossing_event (GdkDisplay                 *display,
   else
     window_event_mask = window->event_mask;
 
-  if (type == GDK_LEAVE_NOTIFY)
+  if (type == GDK_ENTER_NOTIFY &&
+      pointer_info->need_touch_press_enter &&
+      mode != GDK_CROSSING_TOUCH_BEGIN &&
+      mode != GDK_CROSSING_TOUCH_END)
+    {
+      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);
@@ -9176,7 +9185,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);
@@ -9196,6 +9205,15 @@ 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_TOUCHSCREEN &&
+      gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHPAD)
+
+    {
+      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
@@ -9319,6 +9337,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 &&
@@ -9377,6 +9407,7 @@ proxy_button_event (GdkEvent *source_event,
   GdkWindow *pointer_window;
   GdkWindow *parent;
   GdkEvent *event;
+  GdkPointerWindowInfo *pointer_info;
   guint state;
   guint32 time_;
   GdkEventType type;
@@ -9396,6 +9427,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 &&
@@ -9448,6 +9480,31 @@ 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_TOUCHSCREEN ||
+          gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)
+        mode = GDK_CROSSING_TOUCH_BEGIN;
+      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)
@@ -9468,7 +9525,24 @@ 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_TOUCHSCREEN ||
+                gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
+        {
+          /* 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_END,
+                                           toplevel_x, toplevel_y,
+                                           state, time_, NULL,
+                                           serial, FALSE);
+        }
       return TRUE;
 
     case GDK_SCROLL: