summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2011-08-02 19:29:07 +0200
committerCarlos Garnacho <carlosg@gnome.org>2011-10-30 18:22:19 +0100
commit15186016c21ec3b7ccbaf8dbef15e33f1836387f (patch)
treea996b030c2683530c3426f07eb534e89a14c0a0e
parentbd77a67ea4d9dd4834f4ba70e5c2ca2f57483207 (diff)
downloadmutter-15186016c21ec3b7ccbaf8dbef15e33f1836387f.tar.gz
window: Implement window moving through touch events
Window moving is triggered by 3-4 simultaneous touch events on the window, the hotspot being in the center of the touch area bounding rect.
-rw-r--r--src/core/display-private.h12
-rw-r--r--src/core/display.c24
-rw-r--r--src/core/input-events.c30
-rw-r--r--src/core/input-events.h3
-rw-r--r--src/core/window-private.h7
-rw-r--r--src/core/window.c234
6 files changed, 277 insertions, 33 deletions
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 2a178af63..46aaeecbc 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -59,6 +59,7 @@ typedef struct MetaEdgeResistanceData MetaEdgeResistanceData;
typedef struct _MetaGrabInfo MetaGrabInfo;
typedef struct _MetaFocusInfo MetaFocusInfo;
+typedef struct _MetaTouchInfo MetaTouchInfo;
typedef void (* MetaWindowPingFunc) (MetaDisplay *display,
Window xwindow,
@@ -150,6 +151,17 @@ struct _MetaFocusInfo
guint32 last_focus_time;
};
+struct _MetaTouchInfo
+{
+ gdouble root_x;
+ gdouble root_y;
+
+ gdouble initial_root_x;
+ gdouble initial_root_y;
+
+ guint notified : 1;
+};
+
struct _MetaDisplay
{
GObject parent_instance;
diff --git a/src/core/display.c b/src/core/display.c
index e77cb0681..da7f136e0 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1816,6 +1816,14 @@ event_callback (XEvent *event,
filter_out_event = bypass_compositor = TRUE;
break;
case ButtonPress:
+ if (window &&
+ meta_input_event_get_touch_id (display, event, NULL))
+ {
+ meta_window_update_touch (window, event);
+ filter_out_event = TRUE;
+ break;
+ }
+
meta_input_event_get_button (display, event, &n_button);
meta_input_event_get_state (display, event, &state);
meta_input_event_get_coordinates (display, event,
@@ -2031,6 +2039,14 @@ event_callback (XEvent *event,
}
break;
case ButtonRelease:
+ if (window &&
+ meta_input_event_get_touch_id (display, event, NULL))
+ {
+ meta_window_end_touch (window, event);
+ filter_out_event = TRUE;
+ break;
+ }
+
if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
break;
@@ -2040,6 +2056,14 @@ event_callback (XEvent *event,
meta_window_handle_mouse_grab_op_event (window, event);
break;
case MotionNotify:
+ if (window &&
+ meta_input_event_get_touch_id (display, event, NULL))
+ {
+ meta_window_update_touch (window, event);
+ filter_out_event = TRUE;
+ break;
+ }
+
if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
break;
diff --git a/src/core/input-events.c b/src/core/input-events.c
index c9f225ccd..197634b38 100644
--- a/src/core/input-events.c
+++ b/src/core/input-events.c
@@ -160,36 +160,6 @@ meta_input_event_is_type (MetaDisplay *display,
}
gboolean
-meta_input_event_ignore (MetaDisplay *display,
- XEvent *ev)
-{
-#if defined(HAVE_XINPUT2) && defined(HAVE_XTOUCH)
- if (ev->type == GenericEvent &&
- ev->xcookie.extension == display->xinput2_opcode)
- {
- XIEvent *xev;
-
- g_assert (display->have_xinput2 == TRUE);
- xev = (XIEvent *) ev->xcookie.data;
-
- switch (xev->evtype)
- {
- case XI_Motion:
- case XI_ButtonPress:
- case XI_ButtonRelease:
- if (((XIDeviceEvent *) xev)->flags & XIPointerEmulated)
- return TRUE;
-
- default:
- return FALSE;
- }
- }
-#endif /* HAVE_XINPUT2 && HAVE_XTOUCH */
-
- return FALSE;
-}
-
-gboolean
meta_input_event_get_touch_id (MetaDisplay *display,
XEvent *ev,
guint *touch_id)
diff --git a/src/core/input-events.h b/src/core/input-events.h
index 99e322419..a70107ba0 100644
--- a/src/core/input-events.h
+++ b/src/core/input-events.h
@@ -47,9 +47,6 @@ gboolean meta_input_event_is_type (MetaDisplay *display,
XEvent *ev,
guint ev_type);
-gboolean meta_input_event_ignore (MetaDisplay *display,
- XEvent *ev);
-
gboolean meta_input_event_get_touch_id (MetaDisplay *display,
XEvent *ev,
guint *touch_id);
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 51095d511..0cb0670ed 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -415,6 +415,8 @@ struct _MetaWindow
/* Focus info if the window is focused, or NULL */
MetaFocusInfo *cur_focus;
+
+ GHashTable *cur_touches;
};
struct _MetaWindowClass
@@ -678,4 +680,9 @@ MetaDevice * meta_window_get_client_pointer (MetaWindow *window);
MetaDevice * meta_window_guess_grab_pointer (MetaWindow *window);
+gboolean meta_window_update_touch (MetaWindow *window,
+ XEvent *event);
+void meta_window_end_touch (MetaWindow *window,
+ XEvent *event);
+
#endif
diff --git a/src/core/window.c b/src/core/window.c
index 5e4e5f965..c0382084e 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -207,6 +207,9 @@ meta_window_finalize (GObject *object)
if (window->menu)
meta_ui_window_menu_free (window->menu);
+ if (window->cur_touches)
+ g_hash_table_destroy (window->cur_touches);
+
meta_icon_cache_free (&window->icon_cache);
g_free (window->sm_client_id);
@@ -10777,3 +10780,234 @@ meta_window_guess_grab_pointer (MetaWindow *window)
return meta_window_get_client_pointer (window);
}
+
+typedef struct
+{
+ gdouble top_left_x;
+ gdouble top_left_y;
+ gdouble bottom_right_x;
+ gdouble bottom_right_y;
+} BoundingRectCoords;
+
+static void
+calculate_touch_bounding_rect (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ BoundingRectCoords *bounding_rect = user_data;
+ MetaTouchInfo *touch_info = value;
+
+ if (touch_info->root_x < bounding_rect->top_left_x)
+ bounding_rect->top_left_x = touch_info->root_x;
+ if (touch_info->root_x > bounding_rect->bottom_right_x)
+ bounding_rect->bottom_right_x = touch_info->root_x;
+
+ if (touch_info->root_y < bounding_rect->top_left_y)
+ bounding_rect->top_left_y = touch_info->root_y;
+ if (touch_info->root_y > bounding_rect->bottom_right_y)
+ bounding_rect->bottom_right_y = touch_info->root_y;
+}
+
+static void
+notify_touch (MetaWindow *window,
+ MetaDevice *source,
+ guint touch_id,
+ gboolean accept_events)
+{
+ meta_error_trap_push_with_return (window->display);
+
+ XIAllowTouchEvents (window->display->xdisplay,
+ meta_device_get_id (source),
+ touch_id,
+ (accept_events) ?
+ XITouchOwnerAccept :
+ XITouchOwnerRejectEnd);
+
+ if (meta_error_trap_pop_with_return (window->display) != Success)
+ meta_warning ("XIAllowTouchEvents failed on touch sequence %d\n", touch_id);
+}
+
+static void
+notify_touch_events (MetaWindow *window,
+ MetaDevice *source,
+ gboolean accept_events)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, window->cur_touches);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ guint touch_id = GPOINTER_TO_UINT (key);
+ MetaTouchInfo *info = value;
+
+ if (!info->notified)
+ {
+ notify_touch (window, source, touch_id, accept_events);
+ info->notified = TRUE;
+ }
+ }
+}
+
+gboolean
+meta_window_update_touch (MetaWindow *window,
+ XEvent *event)
+{
+ gdouble root_x, root_y;
+ MetaTouchInfo *touch_info;
+ gboolean new_touch = FALSE;
+ MetaDevice *device, *source;
+ guint touch_id, n_touches;
+ Time evtime;
+
+ if (!window->cur_touches)
+ window->cur_touches = g_hash_table_new (NULL, NULL);
+
+ meta_input_event_get_touch_id (window->display,
+ event, &touch_id);
+
+ touch_info = g_hash_table_lookup (window->cur_touches,
+ GUINT_TO_POINTER (touch_id));
+
+ meta_input_event_get_coordinates (window->display, event,
+ NULL, NULL,
+ &root_x, &root_y);
+
+ evtime = meta_input_event_get_time (window->display, event);
+ device = meta_input_event_get_device (window->display, event);
+ source = meta_input_event_get_source_device (window->display, event);
+
+ if (!touch_info)
+ {
+ touch_info = g_slice_new (MetaTouchInfo);
+ touch_info->initial_root_x = root_x;
+ touch_info->initial_root_y = root_y;
+
+ g_hash_table_insert (window->cur_touches,
+ GUINT_TO_POINTER (touch_id),
+ touch_info);
+ new_touch = TRUE;
+ }
+
+ touch_info->root_x = root_x;
+ touch_info->root_y = root_y;
+
+ n_touches = g_hash_table_size (window->cur_touches);
+
+ if (!new_touch && n_touches < 3 &&
+ (ABS (touch_info->initial_root_x - touch_info->root_x) >= 16 ||
+ ABS (touch_info->initial_root_y - touch_info->root_y) >= 16))
+ {
+ /* There aren't yet enough touches on the window to trigger
+ * window moving, and one of the touches moved past the
+ * threshold, so the current touch sequences could actually
+ * be meant for the client window, release all touches
+ * altogether.
+ */
+ notify_touch_events (window, source, FALSE);
+ return TRUE;
+ }
+ else if (n_touches >= 3)
+ {
+ gdouble x, y, width, height;
+ BoundingRectCoords bounding_rect = { DBL_MAX, DBL_MAX,
+ DBL_MIN, DBL_MIN };
+
+ if (n_touches == 3 && new_touch)
+ {
+ /* Accept all touches for the move operation */
+ notify_touch_events (window, source, TRUE);
+ }
+ else if (!touch_info->notified)
+ {
+ /* All other touch sequences have been already
+ * accepted, so only deal with the current one */
+ notify_touch (window, source, touch_id, TRUE);
+ touch_info->notified = TRUE;
+ }
+
+ g_hash_table_foreach (window->cur_touches,
+ calculate_touch_bounding_rect,
+ &bounding_rect);
+
+ /* Get x/y coordinates at the middle of the bounding box,
+ * this will be the hotspot for the window moving operation
+ */
+ width = bounding_rect.bottom_right_x - bounding_rect.top_left_x;
+ height = bounding_rect.bottom_right_y - bounding_rect.top_left_y;
+ x = bounding_rect.top_left_x + (width / 2);
+ y = bounding_rect.top_left_y + (height / 2);
+
+ if (new_touch)
+ {
+ if (n_touches == 3)
+ {
+ /* Start window move operation with the
+ * bounding rectangle center as the hotspot
+ */
+ meta_display_begin_grab_op (window->display,
+ window->screen,
+ window,
+ device,
+ META_GRAB_OP_MOVING,
+ TRUE, FALSE,
+ 1, 0,
+ evtime,
+ x, y);
+ }
+
+ window->initial_touch_area_width = width;
+ window->initial_touch_area_height = height;
+ }
+ else if (window->cur_grab)
+ {
+ window->cur_touch_area_width = width;
+ window->cur_touch_area_height = height;
+
+ update_move (window, device, FALSE, x, y);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+meta_window_end_touch (MetaWindow *window,
+ XEvent *event)
+{
+ MetaTouchInfo *info;
+ MetaDevice *source;
+ guint touch_id, n_touches;
+ Time evtime;
+
+ meta_input_event_get_touch_id (window->display, event, &touch_id);
+ evtime = meta_input_event_get_time (window->display, event);
+ source = meta_input_event_get_source_device (window->display, event);
+
+ info = g_hash_table_lookup (window->cur_touches,
+ GUINT_TO_POINTER (touch_id));
+ if (!info)
+ return;
+
+ if (!info->notified)
+ {
+ notify_touch (window, source, touch_id, FALSE);
+ info->notified = TRUE;
+ }
+
+ g_hash_table_remove (window->cur_touches,
+ GUINT_TO_POINTER (touch_id));
+
+ n_touches = g_hash_table_size (window->cur_touches);
+
+ if (n_touches == 2)
+ {
+ MetaDevice *device;
+
+ device = meta_input_event_get_device (window->display, event);
+ meta_display_end_grab_op (window->display, device, evtime);
+ }
+}