Compare commits
	
		
			16 Commits
		
	
	
		
			3.33.92
			...
			wip/gestur
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3ed80495e0 | ||
|   | f13c86d651 | ||
|   | be2ca66735 | ||
|   | 2df807549e | ||
|   | 66cdb1bb71 | ||
|   | 8030a2972e | ||
|   | ba086dea8c | ||
|   | 6a02d7dfa3 | ||
|   | c5db56da5c | ||
|   | b5c605df5e | ||
|   | 4ad2865cce | ||
|   | b9687d1a72 | ||
|   | 66d18fcc55 | ||
|   | 321cd5d85f | ||
|   | ae91de5d03 | ||
|   | 7247b8d81b | 
| @@ -152,6 +152,8 @@ libmutter_la_SOURCES =				\ | ||||
| 	core/frame.h				\ | ||||
| 	ui/gradient.c				\ | ||||
| 	meta/gradient.h				\ | ||||
| 	core/gesture-tracker.c			\ | ||||
| 	core/gesture-tracker-private.h		\ | ||||
| 	core/keybindings.c			\ | ||||
| 	core/keybindings-private.h		\ | ||||
| 	core/main.c				\ | ||||
|   | ||||
| @@ -51,6 +51,7 @@ struct _MetaBackendX11Private | ||||
|   int xinput_opcode; | ||||
|   int xinput_event_base; | ||||
|   int xinput_error_base; | ||||
|   Time latest_evtime; | ||||
| }; | ||||
| typedef struct _MetaBackendX11Private MetaBackendX11Private; | ||||
|  | ||||
| @@ -71,6 +72,7 @@ static void | ||||
| translate_device_event (MetaBackendX11 *x11, | ||||
|                         XIDeviceEvent  *device_event) | ||||
| { | ||||
|   MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); | ||||
|   Window stage_window = meta_backend_x11_get_xwindow (x11); | ||||
|  | ||||
|   if (device_event->event != stage_window) | ||||
| @@ -88,6 +90,21 @@ translate_device_event (MetaBackendX11 *x11, | ||||
|       device_event->event_x = device_event->root_x; | ||||
|       device_event->event_y = device_event->root_y; | ||||
|     } | ||||
|  | ||||
|   if (!device_event->send_event && device_event->time != CurrentTime) | ||||
|     { | ||||
|       if (device_event->time < priv->latest_evtime) | ||||
|         { | ||||
|           /* Emulated pointer events received after XIRejectTouch is received | ||||
|            * on a passive touch grab will contain older timestamps, update those | ||||
|            * so we dont get InvalidTime at grabs. | ||||
|            */ | ||||
|           device_event->time = priv->latest_evtime; | ||||
|         } | ||||
|  | ||||
|       /* Update the internal latest evtime, for any possible later use */ | ||||
|       priv->latest_evtime = device_event->time; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Clutter makes the assumption that there is only one X window | ||||
| @@ -117,6 +134,9 @@ maybe_spoof_event_as_stage_event (MetaBackendX11 *x11, | ||||
|         case XI_ButtonRelease: | ||||
|         case XI_KeyPress: | ||||
|         case XI_KeyRelease: | ||||
|         case XI_TouchBegin: | ||||
|         case XI_TouchUpdate: | ||||
|         case XI_TouchEnd: | ||||
|           translate_device_event (x11, (XIDeviceEvent *) input_event); | ||||
|           break; | ||||
|         default: | ||||
| @@ -142,17 +162,14 @@ handle_host_xevent (MetaBackend *backend, | ||||
|     MetaMonitorManager *manager = meta_backend_get_monitor_manager (backend); | ||||
|     if (META_IS_MONITOR_MANAGER_XRANDR (manager) && | ||||
|         meta_monitor_manager_xrandr_handle_xevent (META_MONITOR_MANAGER_XRANDR (manager), event)) | ||||
|       { | ||||
|         bypass_clutter = TRUE; | ||||
|         goto out; | ||||
|       } | ||||
|       bypass_clutter = TRUE; | ||||
|   } | ||||
|  | ||||
|   maybe_spoof_event_as_stage_event (x11, event); | ||||
|  | ||||
|  out: | ||||
|   if (!bypass_clutter) | ||||
|     clutter_x11_handle_event (event); | ||||
|     { | ||||
|       maybe_spoof_event_as_stage_event (x11, event); | ||||
|       clutter_x11_handle_event (event); | ||||
|     } | ||||
|  | ||||
|   XFreeEventData (priv->xdisplay, &event->xcookie); | ||||
| } | ||||
| @@ -313,6 +330,9 @@ meta_backend_x11_grab_device (MetaBackend *backend, | ||||
|   XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; | ||||
|   int ret; | ||||
|  | ||||
|   if (timestamp != CurrentTime) | ||||
|     timestamp = MAX (timestamp, priv->latest_evtime); | ||||
|  | ||||
|   XISetMask (mask.mask, XI_ButtonPress); | ||||
|   XISetMask (mask.mask, XI_ButtonRelease); | ||||
|   XISetMask (mask.mask, XI_Enter); | ||||
|   | ||||
| @@ -385,6 +385,10 @@ meta_begin_modal_for_plugin (MetaCompositor   *compositor, | ||||
|   display->grab_have_pointer = TRUE; | ||||
|   display->grab_have_keyboard = TRUE; | ||||
|  | ||||
|   g_signal_emit_by_name (display, "grab-op-begin", | ||||
|                          meta_plugin_get_screen (plugin), | ||||
|                          display->grab_window, display->grab_op); | ||||
|  | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     meta_display_sync_wayland_input_focus (display); | ||||
|  | ||||
| @@ -401,6 +405,10 @@ meta_end_modal_for_plugin (MetaCompositor *compositor, | ||||
|  | ||||
|   g_return_if_fail (is_modal (display)); | ||||
|  | ||||
|   g_signal_emit_by_name (display, "grab-op-end", | ||||
|                          meta_plugin_get_screen (plugin), | ||||
|                          display->grab_window, display->grab_op); | ||||
|  | ||||
|   display->grab_op = META_GRAB_OP_NONE; | ||||
|   display->grab_window = NULL; | ||||
|   display->grab_have_pointer = FALSE; | ||||
|   | ||||
| @@ -35,6 +35,7 @@ | ||||
| #include <meta/boxes.h> | ||||
| #include <meta/display.h> | ||||
| #include "keybindings-private.h" | ||||
| #include "gesture-tracker-private.h" | ||||
| #include <meta/prefs.h> | ||||
| #include <meta/barrier.h> | ||||
| #include <clutter/clutter.h> | ||||
| @@ -253,6 +254,8 @@ struct _MetaDisplay | ||||
|   /* Managed by compositor.c */ | ||||
|   MetaCompositor *compositor; | ||||
|  | ||||
|   MetaGestureTracker *gesture_tracker; | ||||
|  | ||||
|   int composite_event_base; | ||||
|   int composite_error_base; | ||||
|   int composite_major_version; | ||||
| @@ -442,4 +445,6 @@ void meta_display_sanity_check_timestamps (MetaDisplay *display, | ||||
| gboolean meta_display_timestamp_too_old (MetaDisplay *display, | ||||
|                                          guint32     *timestamp); | ||||
|  | ||||
| MetaGestureTracker * meta_display_get_gesture_tracker (MetaDisplay *display); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -51,6 +51,8 @@ | ||||
| #include "meta-cursor-tracker-private.h" | ||||
| #include "meta-backend.h" | ||||
| #include "backends/x11/meta-backend-x11.h" | ||||
| #include <clutter/x11/clutter-x11.h> | ||||
| #include "compositor-private.h" | ||||
|  | ||||
| #ifdef HAVE_RANDR | ||||
| #include <X11/extensions/Xrandr.h> | ||||
| @@ -146,6 +148,11 @@ static void    update_window_grab_modifiers (MetaDisplay *display); | ||||
| static void    prefs_changed_callback    (MetaPreference pref, | ||||
|                                           void          *data); | ||||
|  | ||||
| static void meta_display_grab_window_touch   (MetaDisplay *display, | ||||
|                                               Window       xwindow); | ||||
| static void meta_display_ungrab_window_touch (MetaDisplay *display, | ||||
|                                               Window       xwindow); | ||||
|  | ||||
| static void | ||||
| meta_display_get_property(GObject         *object, | ||||
|                           guint            prop_id, | ||||
| @@ -413,6 +420,28 @@ meta_set_gnome_wm_keybindings (const char *wm_keybindings) | ||||
|   gnome_wm_keybindings = wm_keybindings; | ||||
| } | ||||
|  | ||||
| static void | ||||
| gesture_tracker_state_changed (MetaGestureTracker   *tracker, | ||||
|                                ClutterEventSequence *sequence, | ||||
|                                MetaSequenceState     state, | ||||
|                                MetaDisplay          *display) | ||||
| { | ||||
|   MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); | ||||
|   int event_mode; | ||||
|  | ||||
|   if (state == META_SEQUENCE_ACCEPTED) | ||||
|     event_mode = XIAcceptTouch; | ||||
|   else if (state == META_SEQUENCE_REJECTED) | ||||
|     event_mode = XIRejectTouch; | ||||
|   else | ||||
|     return; | ||||
|  | ||||
|   XIAllowTouchEvents (meta_backend_x11_get_xdisplay (backend), | ||||
|                       META_VIRTUAL_CORE_POINTER_ID, | ||||
|                       clutter_x11_event_sequence_get_touch_detail (sequence), | ||||
|                       DefaultRootWindow (display->xdisplay), event_mode); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_display_open: | ||||
|  * | ||||
| @@ -808,6 +837,13 @@ meta_display_open (void) | ||||
|   meta_screen_init_workspaces (screen); | ||||
|   meta_screen_create_guard_window (screen); | ||||
|  | ||||
|   /* Set up touch support */ | ||||
|   the_display->gesture_tracker = meta_gesture_tracker_new (0); | ||||
|   g_signal_connect (the_display->gesture_tracker, "state-changed", | ||||
|                     G_CALLBACK (gesture_tracker_state_changed), the_display); | ||||
|   meta_display_grab_window_touch (the_display, | ||||
|                                   DefaultRootWindow (the_display->xdisplay)); | ||||
|  | ||||
|   /* We know that if mutter is running as a Wayland compositor, | ||||
|    * we start out with no windows. | ||||
|    */ | ||||
| @@ -977,6 +1013,10 @@ meta_display_close (MetaDisplay *display, | ||||
|  | ||||
|   meta_display_remove_autoraise_callback (display); | ||||
|  | ||||
|   meta_display_ungrab_window_touch (display, | ||||
|                                     DefaultRootWindow (display->xdisplay)); | ||||
|   g_clear_object (&display->gesture_tracker); | ||||
|  | ||||
|   if (display->focus_timeout_id) | ||||
|     g_source_remove (display->focus_timeout_id); | ||||
|   display->focus_timeout_id = 0; | ||||
| @@ -2055,6 +2095,35 @@ meta_display_ungrab_window_buttons  (MetaDisplay *display, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_display_grab_window_touch (MetaDisplay *display, | ||||
|                                 Window       xwindow) | ||||
| { | ||||
|   MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); | ||||
|   unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; | ||||
|   XIEventMask mask = { META_VIRTUAL_CORE_POINTER_ID, sizeof (mask_bits), mask_bits }; | ||||
|   XIGrabModifiers mods = { XIAnyModifier, 0 }; | ||||
|  | ||||
|   XISetMask (mask.mask, XI_TouchBegin); | ||||
|   XISetMask (mask.mask, XI_TouchUpdate); | ||||
|   XISetMask (mask.mask, XI_TouchEnd); | ||||
|  | ||||
|   XIGrabTouchBegin (meta_backend_x11_get_xdisplay (backend), | ||||
|                     META_VIRTUAL_CORE_POINTER_ID, | ||||
|                     xwindow, False, &mask, 1, &mods); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_display_ungrab_window_touch (MetaDisplay *display, | ||||
|                                   Window       xwindow) | ||||
| { | ||||
|   MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); | ||||
|   XIGrabModifiers mods = { XIAnyModifier, 0 }; | ||||
|  | ||||
|   XIUngrabTouchBegin (meta_backend_x11_get_xdisplay (backend), | ||||
|                       META_VIRTUAL_CORE_POINTER_ID, xwindow, 1, &mods); | ||||
| } | ||||
|  | ||||
| /* Grab buttons we only grab while unfocused in click-to-focus mode */ | ||||
| #define MAX_FOCUS_BUTTON 4 | ||||
| void | ||||
| @@ -3152,3 +3221,9 @@ meta_display_create_x_cursor (MetaDisplay *display, | ||||
| { | ||||
|   return meta_cursor_create_x_cursor (display->xdisplay, cursor); | ||||
| } | ||||
|  | ||||
| MetaGestureTracker * | ||||
| meta_display_get_gesture_tracker (MetaDisplay *display) | ||||
| { | ||||
|   return display->gesture_tracker; | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
| #include "config.h" | ||||
| #include "events.h" | ||||
|  | ||||
| #include "core.h" | ||||
| #include "display-private.h" | ||||
| #include "window-private.h" | ||||
| #include "backends/meta-backend.h" | ||||
| @@ -106,6 +107,7 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|   MetaWindow *window; | ||||
|   gboolean bypass_clutter = FALSE, bypass_wayland = FALSE; | ||||
|   MetaWaylandCompositor *compositor = NULL; | ||||
|   MetaGestureTracker *tracker; | ||||
|  | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     { | ||||
| @@ -120,7 +122,9 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|   display->current_time = event->any.time; | ||||
|  | ||||
|   if (window && !window->override_redirect && | ||||
|       (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_BUTTON_PRESS)) | ||||
|       (event->type == CLUTTER_KEY_PRESS || | ||||
|        event->type == CLUTTER_BUTTON_PRESS || | ||||
|        event->type == CLUTTER_TOUCH_BEGIN)) | ||||
|     { | ||||
|       if (CurrentTime == display->current_time) | ||||
|         { | ||||
| @@ -139,6 +143,15 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   tracker = meta_display_get_gesture_tracker (display); | ||||
|  | ||||
|   if (meta_gesture_tracker_handle_event (tracker, event)) | ||||
|     { | ||||
|       bypass_wayland = TRUE; | ||||
|       bypass_clutter = meta_gesture_tracker_consumes_event (tracker, event); | ||||
|       goto out; | ||||
|     } | ||||
|  | ||||
|   if (display->grab_window == window && | ||||
|       meta_grab_op_is_moving_or_resizing (display->grab_op)) | ||||
|     { | ||||
|   | ||||
| @@ -109,6 +109,19 @@ meta_window_ensure_frame (MetaWindow *window) | ||||
|   XChangeWindowAttributes (window->display->xdisplay, | ||||
| 			   frame->xwindow, CWEventMask, &attrs); | ||||
|  | ||||
|   { | ||||
|     unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; | ||||
|     XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; | ||||
|  | ||||
|     XISetMask (mask.mask, XI_ButtonPress); | ||||
|     XISetMask (mask.mask, XI_ButtonRelease); | ||||
|     XISetMask (mask.mask, XI_Motion); | ||||
|     XISetMask (mask.mask, XI_Enter); | ||||
|     XISetMask (mask.mask, XI_Leave); | ||||
|  | ||||
|     XISelectEvents (window->display->xdisplay, frame->xwindow, &mask, 1); | ||||
|   } | ||||
|  | ||||
|   meta_display_register_x_window (window->display, &frame->xwindow, window); | ||||
|  | ||||
|   meta_error_trap_push (window->display); | ||||
|   | ||||
							
								
								
									
										83
									
								
								src/core/gesture-tracker-private.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/core/gesture-tracker-private.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | ||||
|  | ||||
| /** | ||||
|  * \file gesture-tracker-private.h  Manages gestures on windows/desktop | ||||
|  * | ||||
|  * Forwards touch events to clutter actors, and accepts/rejects touch sequences | ||||
|  * based on the outcome of those. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * | ||||
|  * Author: Carlos Garnacho <carlosg@gnome.org> | ||||
|  */ | ||||
|  | ||||
| #ifndef META_GESTURE_TRACKER_PRIVATE_H | ||||
| #define META_GESTURE_TRACKER_PRIVATE_H | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include <clutter/clutter.h> | ||||
| #include <meta/window.h> | ||||
|  | ||||
| #define META_TYPE_GESTURE_TRACKER            (meta_gesture_tracker_get_type ()) | ||||
| #define META_GESTURE_TRACKER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_GESTURE_TRACKER, MetaGestureTracker)) | ||||
| #define META_GESTURE_TRACKER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) | ||||
| #define META_IS_GESTURE_TRACKER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_GESTURE_TRACKER)) | ||||
| #define META_IS_GESTURE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  META_TYPE_GESTURE_TRACKER)) | ||||
| #define META_GESTURE_TRACKER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) | ||||
|  | ||||
| typedef struct _MetaGestureTracker MetaGestureTracker; | ||||
| typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass; | ||||
|  | ||||
| typedef enum { | ||||
|   META_SEQUENCE_NONE, | ||||
|   META_SEQUENCE_ACCEPTED, | ||||
|   META_SEQUENCE_REJECTED, | ||||
|   META_SEQUENCE_PENDING_END | ||||
| } MetaSequenceState; | ||||
|  | ||||
| struct _MetaGestureTracker | ||||
| { | ||||
|   GObject parent_instance; | ||||
| }; | ||||
|  | ||||
| struct _MetaGestureTrackerClass | ||||
| { | ||||
|   GObjectClass parent_class; | ||||
|  | ||||
|   void (* state_changed) (MetaGestureTracker   *tracker, | ||||
|                           ClutterEventSequence *sequence, | ||||
|                           MetaSequenceState     state); | ||||
| }; | ||||
|  | ||||
| GType                meta_gesture_tracker_get_type           (void) G_GNUC_CONST; | ||||
|  | ||||
| MetaGestureTracker * meta_gesture_tracker_new                (guint                 autodeny_timeout); | ||||
|  | ||||
| gboolean             meta_gesture_tracker_handle_event       (MetaGestureTracker   *tracker, | ||||
|                                                               const ClutterEvent   *event); | ||||
| gboolean             meta_gesture_tracker_set_sequence_state (MetaGestureTracker   *tracker, | ||||
|                                                               ClutterEventSequence *sequence, | ||||
|                                                               MetaSequenceState     state); | ||||
| MetaSequenceState    meta_gesture_tracker_get_sequence_state (MetaGestureTracker   *tracker, | ||||
|                                                               ClutterEventSequence *sequence); | ||||
| gboolean             meta_gesture_tracker_consumes_event     (MetaGestureTracker   *tracker, | ||||
|                                                               const ClutterEvent   *event); | ||||
|  | ||||
| #endif /* META_GESTURE_TRACKER_PRIVATE_H */ | ||||
							
								
								
									
										555
									
								
								src/core/gesture-tracker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								src/core/gesture-tracker.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,555 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * | ||||
|  * Author: Carlos Garnacho <carlosg@gnome.org> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include "gesture-tracker-private.h" | ||||
| #include "meta-surface-actor.h" | ||||
|  | ||||
| #define DISTANCE_THRESHOLD 30 | ||||
|  | ||||
| typedef struct _MetaGestureTrackerPrivate MetaGestureTrackerPrivate; | ||||
| typedef struct _GestureActionData GestureActionData; | ||||
| typedef struct _MetaSequenceInfo MetaSequenceInfo; | ||||
|  | ||||
| struct _MetaSequenceInfo | ||||
| { | ||||
|   MetaGestureTracker *tracker; | ||||
|   ClutterEventSequence *sequence; | ||||
|   MetaSequenceState state; | ||||
|   guint autodeny_timeout_id; | ||||
|   gfloat start_x; | ||||
|   gfloat start_y; | ||||
| }; | ||||
|  | ||||
| struct _GestureActionData | ||||
| { | ||||
|   ClutterGestureAction *gesture; | ||||
|   MetaSequenceState state; | ||||
|   guint gesture_begin_id; | ||||
|   guint gesture_end_id; | ||||
|   guint gesture_cancel_id; | ||||
| }; | ||||
|  | ||||
| struct _MetaGestureTrackerPrivate | ||||
| { | ||||
|   GHashTable *sequences; /* Hashtable of ClutterEventSequence->MetaSequenceInfo */ | ||||
|  | ||||
|   MetaSequenceState stage_state; | ||||
|   GArray *stage_gestures; /* Array of GestureActionData */ | ||||
|   GList *listeners; /* List of ClutterGestureAction */ | ||||
|   guint autodeny_timeout; | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|   PROP_AUTODENY_TIMEOUT = 1 | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|   STATE_CHANGED, | ||||
|   N_SIGNALS | ||||
| }; | ||||
|  | ||||
| #define DEFAULT_AUTODENY_TIMEOUT 150 | ||||
|  | ||||
| static guint signals[N_SIGNALS] = { 0 }; | ||||
|  | ||||
| static void meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker); | ||||
|  | ||||
| G_DEFINE_TYPE_WITH_PRIVATE (MetaGestureTracker, meta_gesture_tracker, G_TYPE_OBJECT) | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_finalize (GObject *object) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); | ||||
|  | ||||
|   g_hash_table_destroy (priv->sequences); | ||||
|   g_array_free (priv->stage_gestures, TRUE); | ||||
|   g_list_free (priv->listeners); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_gesture_tracker_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_set_property (GObject      *object, | ||||
|                                    guint         prop_id, | ||||
|                                    const GValue *value, | ||||
|                                    GParamSpec   *pspec) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_AUTODENY_TIMEOUT: | ||||
|       priv->autodeny_timeout = g_value_get_uint (value); | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_get_property (GObject    *object, | ||||
|                                    guint       prop_id, | ||||
|                                    GValue     *value, | ||||
|                                    GParamSpec *pspec) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_AUTODENY_TIMEOUT: | ||||
|       g_value_set_uint (value, priv->autodeny_timeout); | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_class_init (MetaGestureTrackerClass *klass) | ||||
| { | ||||
|   GObjectClass *object_class = G_OBJECT_CLASS (klass); | ||||
|  | ||||
|   object_class->finalize = meta_gesture_tracker_finalize; | ||||
|   object_class->set_property = meta_gesture_tracker_set_property; | ||||
|   object_class->get_property = meta_gesture_tracker_get_property; | ||||
|  | ||||
|   g_object_class_install_property (object_class, | ||||
|                                    PROP_AUTODENY_TIMEOUT, | ||||
|                                    g_param_spec_uint ("autodeny-timeout", | ||||
|                                                       "Auto-deny timeout", | ||||
|                                                       "Auto-deny timeout", | ||||
|                                                       0, G_MAXUINT, 0, | ||||
|                                                       G_PARAM_READWRITE | | ||||
|                                                       G_PARAM_CONSTRUCT_ONLY)); | ||||
|   signals[STATE_CHANGED] = | ||||
|     g_signal_new ("state-changed", | ||||
|                   G_TYPE_FROM_CLASS (klass), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   G_STRUCT_OFFSET (MetaGestureTrackerClass, state_changed), | ||||
|                   NULL, NULL, NULL, | ||||
|                   G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| autodeny_sequence (gpointer user_data) | ||||
| { | ||||
|   MetaSequenceInfo *info = user_data; | ||||
|  | ||||
|   /* Deny the sequence automatically after the given timeout */ | ||||
|   if (info->state == META_SEQUENCE_NONE) | ||||
|     meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, | ||||
|                                              META_SEQUENCE_REJECTED); | ||||
|  | ||||
|   info->autodeny_timeout_id = 0; | ||||
|   return G_SOURCE_REMOVE; | ||||
| } | ||||
|  | ||||
| static MetaSequenceInfo * | ||||
| meta_sequence_info_new (MetaGestureTracker *tracker, | ||||
|                         const ClutterEvent *event) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   MetaSequenceInfo *info; | ||||
|   guint ms; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   ms = (priv->autodeny_timeout) ? | ||||
|     priv->autodeny_timeout : DEFAULT_AUTODENY_TIMEOUT; | ||||
|  | ||||
|   info = g_slice_new0 (MetaSequenceInfo); | ||||
|   info->tracker = tracker; | ||||
|   info->sequence = event->touch.sequence; | ||||
|   info->state = META_SEQUENCE_NONE; | ||||
|   info->autodeny_timeout_id = g_timeout_add (ms, autodeny_sequence, info); | ||||
|  | ||||
|   clutter_event_get_coords (event, &info->start_x, &info->start_y); | ||||
|  | ||||
|   return info; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_sequence_info_free (MetaSequenceInfo *info) | ||||
| { | ||||
|   if (info->autodeny_timeout_id) | ||||
|     g_source_remove (info->autodeny_timeout_id); | ||||
|  | ||||
|   if (info->state == META_SEQUENCE_NONE) | ||||
|     meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, | ||||
|                                              META_SEQUENCE_REJECTED); | ||||
|   g_slice_free (MetaSequenceInfo, info); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| state_is_applicable (MetaSequenceState prev_state, | ||||
|                      MetaSequenceState state) | ||||
| { | ||||
|   if (prev_state == META_SEQUENCE_PENDING_END) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Don't allow reverting to none */ | ||||
|   if (state == META_SEQUENCE_NONE) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* PENDING_END state is final */ | ||||
|   if (prev_state == META_SEQUENCE_PENDING_END) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Sequences must be accepted/denied before PENDING_END */ | ||||
|   if (prev_state == META_SEQUENCE_NONE && | ||||
|       state == META_SEQUENCE_PENDING_END) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Make sequences stick to their accepted/denied state */ | ||||
|   if (state != META_SEQUENCE_PENDING_END && | ||||
|       prev_state != META_SEQUENCE_NONE) | ||||
|     return FALSE; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_gesture_tracker_set_state (MetaGestureTracker *tracker, | ||||
|                                 MetaSequenceState   state) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   ClutterEventSequence *sequence; | ||||
|   GHashTableIter iter; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|  | ||||
|   if (priv->stage_state != state && | ||||
|       !state_is_applicable (priv->stage_state, state)) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_hash_table_iter_init (&iter, priv->sequences); | ||||
|   priv->stage_state = state; | ||||
|  | ||||
|   while (g_hash_table_iter_next (&iter, (gpointer*) &sequence, NULL)) | ||||
|     meta_gesture_tracker_set_sequence_state (tracker, sequence, state); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| gesture_begin_cb (ClutterGestureAction *gesture, | ||||
|                   ClutterActor         *actor, | ||||
|                   MetaGestureTracker   *tracker) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|  | ||||
|   if (!g_list_find (priv->listeners, gesture) && | ||||
|       meta_gesture_tracker_set_state (tracker, META_SEQUENCE_ACCEPTED)) | ||||
|     priv->listeners = g_list_prepend (priv->listeners, gesture); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| gesture_end_cb (ClutterGestureAction *gesture, | ||||
|                 ClutterActor         *actor, | ||||
|                 MetaGestureTracker   *tracker) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   priv->listeners = g_list_remove (priv->listeners, gesture); | ||||
|  | ||||
|   if (!priv->listeners) | ||||
|     meta_gesture_tracker_untrack_stage (tracker); | ||||
| } | ||||
|  | ||||
| static void | ||||
| gesture_cancel_cb (ClutterGestureAction *gesture, | ||||
|                    ClutterActor         *actor, | ||||
|                    MetaGestureTracker   *tracker) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|  | ||||
|   if (g_list_find (priv->listeners, gesture)) | ||||
|     { | ||||
|       priv->listeners = g_list_remove (priv->listeners, gesture); | ||||
|  | ||||
|       if (!priv->listeners) | ||||
|         meta_gesture_tracker_set_state (tracker, META_SEQUENCE_PENDING_END); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| cancel_and_unref_gesture_cb (ClutterGestureAction *action) | ||||
| { | ||||
|   clutter_gesture_action_cancel (action); | ||||
|   g_object_unref (action); | ||||
|   return G_SOURCE_REMOVE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| clear_gesture_data (GestureActionData *data) | ||||
| { | ||||
|   g_signal_handler_disconnect (data->gesture, data->gesture_begin_id); | ||||
|   g_signal_handler_disconnect (data->gesture, data->gesture_end_id); | ||||
|   g_signal_handler_disconnect (data->gesture, data->gesture_cancel_id); | ||||
|  | ||||
|   /* Defer cancellation to an idle, as it may happen within event handling */ | ||||
|   g_idle_add ((GSourceFunc) cancel_and_unref_gesture_cb, data->gesture); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_init (MetaGestureTracker *tracker) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   priv->sequences = g_hash_table_new_full (NULL, NULL, NULL, | ||||
|                                            (GDestroyNotify) meta_sequence_info_free); | ||||
|   priv->stage_gestures = g_array_new (FALSE, FALSE, sizeof (GestureActionData)); | ||||
|   g_array_set_clear_func (priv->stage_gestures, (GDestroyNotify) clear_gesture_data); | ||||
| } | ||||
|  | ||||
| MetaGestureTracker * | ||||
| meta_gesture_tracker_new (guint autodeny_timeout) | ||||
| { | ||||
|   return g_object_new (META_TYPE_GESTURE_TRACKER, | ||||
|                        "autodeny-timeout", autodeny_timeout, | ||||
|                        NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_track_stage (MetaGestureTracker *tracker, | ||||
|                                   ClutterActor       *stage) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   GList *actions, *l; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   actions = clutter_actor_get_actions (stage); | ||||
|  | ||||
|   for (l = actions; l; l = l->next) | ||||
|     { | ||||
|       GestureActionData data; | ||||
|  | ||||
|       if (!CLUTTER_IS_GESTURE_ACTION (l->data)) | ||||
|         continue; | ||||
|  | ||||
|       data.gesture = g_object_ref (l->data); | ||||
|       data.state = META_SEQUENCE_NONE; | ||||
|       data.gesture_begin_id = | ||||
|         g_signal_connect (data.gesture, "gesture-begin", | ||||
|                           G_CALLBACK (gesture_begin_cb), tracker); | ||||
|       data.gesture_end_id = | ||||
|         g_signal_connect (data.gesture, "gesture-end", | ||||
|                           G_CALLBACK (gesture_end_cb), tracker); | ||||
|       data.gesture_cancel_id = | ||||
|         g_signal_connect (data.gesture, "gesture-cancel", | ||||
|                           G_CALLBACK (gesture_cancel_cb), tracker); | ||||
|       g_array_append_val (priv->stage_gestures, data); | ||||
|     } | ||||
|  | ||||
|   g_list_free (actions); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   priv->stage_state = META_SEQUENCE_NONE; | ||||
|  | ||||
|   g_hash_table_remove_all (priv->sequences); | ||||
|  | ||||
|   if (priv->stage_gestures->len > 0) | ||||
|     g_array_remove_range (priv->stage_gestures, 0, priv->stage_gestures->len); | ||||
|  | ||||
|   g_list_free (priv->listeners); | ||||
|   priv->listeners = NULL; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_gesture_tracker_handle_event (MetaGestureTracker *tracker, | ||||
| 				   const ClutterEvent *event) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   ClutterEventSequence *sequence; | ||||
|   MetaSequenceInfo *info; | ||||
|   ClutterActor *stage; | ||||
|   gfloat x, y; | ||||
|  | ||||
|   sequence = clutter_event_get_event_sequence (event); | ||||
|  | ||||
|   if (!sequence) | ||||
|     return FALSE; | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   stage = CLUTTER_ACTOR (clutter_event_get_stage (event)); | ||||
|  | ||||
|   switch (event->type) | ||||
|     { | ||||
|     case CLUTTER_TOUCH_BEGIN: | ||||
|       if (g_hash_table_size (priv->sequences) == 0) | ||||
|         meta_gesture_tracker_track_stage (tracker, stage); | ||||
|  | ||||
|       info = meta_sequence_info_new (tracker, event); | ||||
|       g_hash_table_insert (priv->sequences, sequence, info); | ||||
|  | ||||
|       if (priv->stage_gestures->len == 0) | ||||
|         { | ||||
|           /* If no gestures are attached, reject the sequence right away */ | ||||
|           meta_gesture_tracker_set_sequence_state (tracker, sequence, | ||||
|                                                    META_SEQUENCE_REJECTED); | ||||
|         } | ||||
|       else if (priv->stage_state != META_SEQUENCE_NONE) | ||||
|         { | ||||
|           /* Make the sequence state match the general state */ | ||||
|           meta_gesture_tracker_set_sequence_state (tracker, sequence, | ||||
|                                                    priv->stage_state); | ||||
|         } | ||||
|       break; | ||||
|     case CLUTTER_TOUCH_END: | ||||
|       info = g_hash_table_lookup (priv->sequences, sequence); | ||||
|  | ||||
|       if (!info) | ||||
|         return FALSE; | ||||
|  | ||||
|       /* If nothing was done yet about the sequence, reject it so X11 | ||||
|        * clients may see it | ||||
|        */ | ||||
|       if (info->state == META_SEQUENCE_NONE) | ||||
|         meta_gesture_tracker_set_sequence_state (tracker, sequence, | ||||
|                                                  META_SEQUENCE_REJECTED); | ||||
|  | ||||
|       g_hash_table_remove (priv->sequences, sequence); | ||||
|  | ||||
|       if (g_hash_table_size (priv->sequences) == 0) | ||||
|         meta_gesture_tracker_untrack_stage (tracker); | ||||
|       break; | ||||
|     case CLUTTER_TOUCH_UPDATE: | ||||
|       info = g_hash_table_lookup (priv->sequences, sequence); | ||||
|  | ||||
|       if (!info) | ||||
|         return FALSE; | ||||
|  | ||||
|       clutter_event_get_coords (event, &x, &y); | ||||
|  | ||||
|       if (info->state == META_SEQUENCE_NONE && | ||||
|           (ABS (info->start_x - x) > DISTANCE_THRESHOLD || | ||||
|            ABS (info->start_y - y) > DISTANCE_THRESHOLD)) | ||||
|         meta_gesture_tracker_set_sequence_state (tracker, sequence, | ||||
|                                                  META_SEQUENCE_REJECTED); | ||||
|       break; | ||||
|     default: | ||||
|       return FALSE; | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_gesture_tracker_set_sequence_state (MetaGestureTracker   *tracker, | ||||
|                                          ClutterEventSequence *sequence, | ||||
|                                          MetaSequenceState     state) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   MetaSequenceInfo *info; | ||||
|  | ||||
|   g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), FALSE); | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   info = g_hash_table_lookup (priv->sequences, sequence); | ||||
|  | ||||
|   if (!info) | ||||
|     return FALSE; | ||||
|   else if (state == info->state) | ||||
|     return TRUE; | ||||
|  | ||||
|   if (!state_is_applicable (info->state, state)) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Unset autodeny timeout */ | ||||
|   if (info->autodeny_timeout_id) | ||||
|     { | ||||
|       g_source_remove (info->autodeny_timeout_id); | ||||
|       info->autodeny_timeout_id = 0; | ||||
|     } | ||||
|  | ||||
|   info->state = state; | ||||
|   g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); | ||||
|  | ||||
|   /* If the sequence was denied, set immediately to PENDING_END after emission */ | ||||
|   if (state == META_SEQUENCE_REJECTED) | ||||
|     { | ||||
|       info->state = META_SEQUENCE_PENDING_END; | ||||
|       g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| MetaSequenceState | ||||
| meta_gesture_tracker_get_sequence_state (MetaGestureTracker   *tracker, | ||||
|                                          ClutterEventSequence *sequence) | ||||
| { | ||||
|   MetaGestureTrackerPrivate *priv; | ||||
|   MetaSequenceInfo *info; | ||||
|  | ||||
|   g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), META_SEQUENCE_PENDING_END); | ||||
|  | ||||
|   priv = meta_gesture_tracker_get_instance_private (tracker); | ||||
|   info = g_hash_table_lookup (priv->sequences, sequence); | ||||
|  | ||||
|   if (!info) | ||||
|     return META_SEQUENCE_PENDING_END; | ||||
|  | ||||
|   return info->state; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_gesture_tracker_consumes_event (MetaGestureTracker *tracker, | ||||
|                                      const ClutterEvent *event) | ||||
| { | ||||
|   ClutterEventSequence *sequence; | ||||
|   MetaSequenceState state; | ||||
|  | ||||
|   g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), FALSE); | ||||
|  | ||||
|   sequence = clutter_event_get_event_sequence (event); | ||||
|  | ||||
|   if (!sequence) | ||||
|     return FALSE; | ||||
|  | ||||
|   state = meta_gesture_tracker_get_sequence_state (tracker, sequence); | ||||
|  | ||||
|   return (event->type != CLUTTER_TOUCH_END && | ||||
|           (state == META_SEQUENCE_REJECTED || state == META_SEQUENCE_PENDING_END)); | ||||
| } | ||||
| @@ -1136,6 +1136,64 @@ meta_frame_right_click_event(MetaUIFrame     *frame, | ||||
|   return meta_frame_titlebar_event (frame, event, action); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_frames_try_grab_op (MetaFrames  *frames, | ||||
|                          MetaUIFrame *frame, | ||||
|                          MetaGrabOp   op, | ||||
|                          gdouble      grab_x, | ||||
|                          gdouble      grab_y, | ||||
|                          guint32      time) | ||||
| { | ||||
|   Display *display; | ||||
|   gboolean ret; | ||||
|  | ||||
|   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); | ||||
|   ret = meta_core_begin_grab_op (display, | ||||
|                                  frame->xwindow, | ||||
|                                  op, | ||||
|                                  FALSE, | ||||
|                                  TRUE, | ||||
|                                  frame->grab_button, | ||||
|                                  0, | ||||
|                                  time, | ||||
|                                  grab_x, grab_y); | ||||
|   if (!ret) | ||||
|     { | ||||
|       frames->current_grab_op = op; | ||||
|       frames->grab_frame = frame; | ||||
|       frames->grab_x = grab_x; | ||||
|       frames->grab_y = grab_y; | ||||
|     } | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_frames_retry_grab_op (MetaFrames *frames, | ||||
|                            guint       time) | ||||
| { | ||||
|   Display *display; | ||||
|   MetaGrabOp op; | ||||
|  | ||||
|   if (frames->current_grab_op == META_GRAB_OP_NONE) | ||||
|     return TRUE; | ||||
|  | ||||
|   op = frames->current_grab_op; | ||||
|   frames->current_grab_op = META_GRAB_OP_NONE; | ||||
|   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); | ||||
|  | ||||
|   return meta_core_begin_grab_op (display, | ||||
|                                   frames->grab_frame->xwindow, | ||||
|                                   op, | ||||
|                                   FALSE, | ||||
|                                   TRUE, | ||||
|                                   frames->grab_frame->grab_button, | ||||
|                                   0, | ||||
|                                   time, | ||||
|                                   frames->grab_x, | ||||
|                                   frames->grab_y); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_frames_button_press_event (GtkWidget      *widget, | ||||
|                                 GdkEventButton *event) | ||||
| @@ -1190,6 +1248,8 @@ meta_frames_button_press_event (GtkWidget      *widget, | ||||
|   if (meta_core_get_grab_op (display) != META_GRAB_OP_NONE) | ||||
|     return FALSE; /* already up to something */ | ||||
|  | ||||
|   frame->grab_button = event->button; | ||||
|  | ||||
|   if (event->button == 1 && | ||||
|       (control == META_FRAME_CONTROL_MAXIMIZE || | ||||
|        control == META_FRAME_CONTROL_UNMAXIMIZE || | ||||
| @@ -1293,16 +1353,9 @@ meta_frames_button_press_event (GtkWidget      *widget, | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|       meta_core_begin_grab_op (display, | ||||
|                                frame->xwindow, | ||||
|                                op, | ||||
|                                TRUE, | ||||
|                                TRUE, | ||||
|                                event->button, | ||||
|                                0, | ||||
|                                event->time, | ||||
|                                event->x_root, | ||||
|                                event->y_root); | ||||
|       meta_frames_try_grab_op (frames, frame, op, | ||||
|                                event->x_root, event->y_root, | ||||
|                                event->time); | ||||
|     } | ||||
|   else if (control == META_FRAME_CONTROL_TITLE && | ||||
|            event->button == 1) | ||||
| @@ -1315,16 +1368,10 @@ meta_frames_button_press_event (GtkWidget      *widget, | ||||
|  | ||||
|       if (flags & META_FRAME_ALLOWS_MOVE) | ||||
|         { | ||||
|           meta_core_begin_grab_op (display, | ||||
|                                    frame->xwindow, | ||||
|           meta_frames_try_grab_op (frames, frame, | ||||
|                                    META_GRAB_OP_MOVING, | ||||
|                                    TRUE, | ||||
|                                    TRUE, | ||||
|                                    event->button, | ||||
|                                    0, | ||||
|                                    event->time, | ||||
|                                    event->x_root, | ||||
|                                    event->y_root); | ||||
|                                    event->x_root, event->y_root, | ||||
|                                    event->time); | ||||
|         } | ||||
|     } | ||||
|   else if (event->button == 2) | ||||
| @@ -1349,6 +1396,7 @@ meta_frames_button_release_event    (GtkWidget           *widget, | ||||
|  | ||||
|   frames = META_FRAMES (widget); | ||||
|   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); | ||||
|   frames->current_grab_op = META_GRAB_OP_NONE; | ||||
|  | ||||
|   frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window)); | ||||
|   if (frame == NULL) | ||||
| @@ -1560,6 +1608,10 @@ meta_frames_motion_notify_event     (GtkWidget           *widget, | ||||
|       meta_frames_update_prelit_control (frames, frame, control); | ||||
|     } | ||||
|  | ||||
|   if ((event->state & GDK_BUTTON1_MASK) && | ||||
|       frames->current_grab_op != META_GRAB_OP_NONE) | ||||
|     meta_frames_retry_grab_op (frames, event->time); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -100,6 +100,12 @@ struct _MetaFrames | ||||
|   GtkStyleContext *normal_style; | ||||
|   GHashTable *style_variants; | ||||
|  | ||||
|   MetaGrabOp current_grab_op; | ||||
|   MetaUIFrame *grab_frame; | ||||
|   guint grab_button; | ||||
|   gdouble grab_x; | ||||
|   gdouble grab_y; | ||||
|  | ||||
|   Window grab_xwindow; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										37
									
								
								src/ui/ui.c
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/ui/ui.c
									
									
									
									
									
								
							| @@ -26,6 +26,7 @@ | ||||
| #include <meta/util.h> | ||||
| #include "core.h" | ||||
| #include "theme-private.h" | ||||
| #include "x11/events.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| @@ -119,7 +120,6 @@ maybe_redirect_mouse_event (XEvent *xevent) | ||||
|  | ||||
|   switch (xev->evtype) | ||||
|     { | ||||
|     case XI_TouchBegin: | ||||
|     case XI_ButtonPress: | ||||
|     case XI_ButtonRelease: | ||||
|     case XI_Motion: | ||||
| @@ -147,19 +147,11 @@ maybe_redirect_mouse_event (XEvent *xevent) | ||||
|   gmanager = gdk_display_get_device_manager (gdisplay); | ||||
|   gdevice = gdk_x11_device_manager_lookup (gmanager, META_VIRTUAL_CORE_POINTER_ID); | ||||
|  | ||||
|   /* If GDK already thinks it has a grab, we better let it see events; this | ||||
|    * is the menu-navigation case and events need to get sent to the appropriate | ||||
|    * (client-side) subwindow for individual menu items. | ||||
|    */ | ||||
|   if (gdk_display_device_is_grabbed (gdisplay, gdevice)) | ||||
|     return FALSE; | ||||
|  | ||||
|   switch (xev->evtype) | ||||
|     { | ||||
|     case XI_TouchBegin: | ||||
|     case XI_ButtonPress: | ||||
|     case XI_ButtonRelease: | ||||
|       if (xev_d->evtype == XI_ButtonPress || xev_d->evtype == XI_TouchBegin) | ||||
|       if (xev_d->evtype == XI_ButtonPress) | ||||
|         { | ||||
|           GtkSettings *settings = gtk_settings_get_default (); | ||||
|           int double_click_time; | ||||
| @@ -171,10 +163,7 @@ maybe_redirect_mouse_event (XEvent *xevent) | ||||
|                         "gtk-double-click-distance", &double_click_distance, | ||||
|                         NULL); | ||||
|  | ||||
|           if (xev->evtype == XI_TouchBegin) | ||||
|             button = 1; | ||||
|           else | ||||
|             button = xev_d->detail; | ||||
|           button = xev_d->detail; | ||||
|  | ||||
|           if (button == ui->button_click_number && | ||||
|               xev_d->event == ui->button_click_window && | ||||
| @@ -216,6 +205,12 @@ maybe_redirect_mouse_event (XEvent *xevent) | ||||
|       gevent = gdk_event_new (GDK_MOTION_NOTIFY); | ||||
|       gevent->motion.type = GDK_MOTION_NOTIFY; | ||||
|       gevent->motion.window = g_object_ref (gdk_window); | ||||
|       gevent->motion.time = xev_d->time; | ||||
|       gevent->motion.x_root = xev_d->root_x; | ||||
|       gevent->motion.y_root = xev_d->root_y; | ||||
|  | ||||
|       if (XIMaskIsSet (xev_d->buttons.mask, 1)) | ||||
|         gevent->motion.state |= GDK_BUTTON1_MASK; | ||||
|       break; | ||||
|     case XI_Enter: | ||||
|     case XI_Leave: | ||||
| @@ -237,15 +232,11 @@ maybe_redirect_mouse_event (XEvent *xevent) | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static GdkFilterReturn | ||||
| ui_filter_func (GdkXEvent *xevent, | ||||
|                 GdkEvent *event, | ||||
| static void | ||||
| ui_filter_func (gpointer xevent, | ||||
|                 gpointer data) | ||||
| { | ||||
|   if (maybe_redirect_mouse_event (xevent)) | ||||
|     return GDK_FILTER_REMOVE; | ||||
|   else | ||||
|     return GDK_FILTER_CONTINUE; | ||||
|   maybe_redirect_mouse_event (xevent); | ||||
| } | ||||
|  | ||||
| MetaUI* | ||||
| @@ -271,7 +262,7 @@ meta_ui_new (Display *xdisplay, | ||||
|    */ | ||||
|   gtk_widget_show (GTK_WIDGET (ui->frames)); | ||||
|  | ||||
|   gdk_window_add_filter (NULL, ui_filter_func, NULL); | ||||
|   meta_display_events_x11_add_func (ui_filter_func, NULL); | ||||
|  | ||||
|   g_object_set_data (G_OBJECT (gdisplay), "meta-ui", ui); | ||||
|  | ||||
| @@ -288,7 +279,7 @@ meta_ui_free (MetaUI *ui) | ||||
|   gdisplay = gdk_x11_lookup_xdisplay (ui->xdisplay); | ||||
|   g_object_set_data (G_OBJECT (gdisplay), "meta-ui", NULL); | ||||
|  | ||||
|   gdk_window_remove_filter (NULL, ui_filter_func, NULL); | ||||
|   meta_display_events_x11_add_func (ui_filter_func, NULL); | ||||
|  | ||||
|   g_free (ui); | ||||
| } | ||||
|   | ||||
| @@ -444,29 +444,6 @@ touch_info_free (MetaWaylandTouchInfo *touch_info) | ||||
|   g_free (touch_info); | ||||
| } | ||||
|  | ||||
| static void | ||||
| touch_handle_cancel_event (MetaWaylandTouch      *touch, | ||||
|                            struct libinput_event *event) | ||||
| { | ||||
|   GList *surfaces, *s; | ||||
|  | ||||
|   surfaces = s = touch_get_surfaces (touch, FALSE); | ||||
|  | ||||
|   while (s) | ||||
|     { | ||||
|       MetaWaylandTouchSurface *touch_surface = s->data; | ||||
|       struct wl_resource *resource; | ||||
|       struct wl_list *l; | ||||
|  | ||||
|       l = &touch_surface->resource_list; | ||||
|       wl_resource_for_each(resource, l) | ||||
|         wl_touch_send_cancel (resource); | ||||
|     } | ||||
|  | ||||
|   g_hash_table_remove_all (touch->touches); | ||||
|   g_list_free (surfaces); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| evdev_filter_func (struct libinput_event *event, | ||||
|                    gpointer               data) | ||||
| @@ -495,7 +472,7 @@ evdev_filter_func (struct libinput_event *event, | ||||
|        * which are not so useful when sending a global signal as the protocol | ||||
|        * requires. | ||||
|        */ | ||||
|       touch_handle_cancel_event (touch, event); | ||||
|       meta_wayland_touch_cancel (touch); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
| @@ -546,3 +523,25 @@ meta_wayland_touch_create_new_resource (MetaWaylandTouch   *touch, | ||||
|   wl_resource_set_implementation (cr, NULL, touch, unbind_resource); | ||||
|   wl_list_insert (&touch->resource_list, wl_resource_get_link (cr)); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_touch_cancel (MetaWaylandTouch *touch) | ||||
| { | ||||
|   GList *surfaces, *s; | ||||
|  | ||||
|   surfaces = s = touch_get_surfaces (touch, FALSE); | ||||
|  | ||||
|   while (s) | ||||
|     { | ||||
|       MetaWaylandTouchSurface *touch_surface = s->data; | ||||
|       struct wl_resource *resource; | ||||
|       struct wl_list *l; | ||||
|  | ||||
|       l = &touch_surface->resource_list; | ||||
|       wl_resource_for_each(resource, l) | ||||
|         wl_touch_send_cancel (resource); | ||||
|     } | ||||
|  | ||||
|   g_hash_table_remove_all (touch->touches); | ||||
|   g_list_free (surfaces); | ||||
| } | ||||
|   | ||||
| @@ -58,5 +58,7 @@ void meta_wayland_touch_create_new_resource (MetaWaylandTouch   *touch, | ||||
|                                              struct wl_client   *client, | ||||
|                                              struct wl_resource *seat_resource, | ||||
|                                              uint32_t            id); | ||||
| void meta_wayland_touch_cancel (MetaWaylandTouch *touch); | ||||
|  | ||||
|  | ||||
| #endif /* META_WAYLAND_TOUCH_H */ | ||||
|   | ||||
| @@ -38,6 +38,16 @@ | ||||
| #include "wayland/meta-xwayland.h" | ||||
| #include "wayland/meta-wayland-private.h" | ||||
|  | ||||
| typedef struct _EventFuncData EventFuncData; | ||||
|  | ||||
| struct _EventFuncData | ||||
| { | ||||
|   GFunc func; | ||||
|   gpointer data; | ||||
| }; | ||||
|  | ||||
| static GList *event_funcs = NULL; | ||||
|  | ||||
| static XIEvent * | ||||
| get_input_event (MetaDisplay *display, | ||||
|                  XEvent      *event) | ||||
| @@ -1801,11 +1811,37 @@ xevent_filter (GdkXEvent *xevent, | ||||
|                gpointer   data) | ||||
| { | ||||
|   MetaDisplay *display = data; | ||||
|   EventFuncData *event_data; | ||||
|   XIEvent *input_event; | ||||
|   GList *l; | ||||
|  | ||||
|   if (meta_display_handle_xevent (display, xevent)) | ||||
|     return GDK_FILTER_REMOVE; | ||||
|   else | ||||
|   meta_display_handle_xevent (display, xevent); | ||||
|  | ||||
|   for (l = event_funcs; l; l = l->next) | ||||
|     { | ||||
|       event_data = l->data; | ||||
|       event_data->func (xevent, event_data->data); | ||||
|     } | ||||
|  | ||||
|   input_event = get_input_event (display, xevent); | ||||
|  | ||||
|   if (!input_event) | ||||
|     return GDK_FILTER_CONTINUE; | ||||
|  | ||||
|   /* Filter all pointer and touch events, those are emulated | ||||
|    * above by the filters, and Gdk is bypassed there on purpose. | ||||
|    */ | ||||
|   if (input_event->evtype == XI_ButtonPress || | ||||
|       input_event->evtype == XI_ButtonRelease || | ||||
|       input_event->evtype == XI_Motion || | ||||
|       input_event->evtype == XI_Enter || | ||||
|       input_event->evtype == XI_Leave || | ||||
|       input_event->evtype == XI_TouchBegin || | ||||
|       input_event->evtype == XI_TouchUpdate || | ||||
|       input_event->evtype == XI_TouchEnd) | ||||
|     return GDK_FILTER_REMOVE; | ||||
|  | ||||
|   return GDK_FILTER_CONTINUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -1819,3 +1855,37 @@ meta_display_free_events_x11 (MetaDisplay *display) | ||||
| { | ||||
|   gdk_window_remove_filter (NULL, xevent_filter, display); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_events_x11_add_func (GFunc    func, | ||||
|                                   gpointer user_data) | ||||
| { | ||||
|   EventFuncData *data; | ||||
|  | ||||
|   data = g_slice_new0 (EventFuncData); | ||||
|   data->func = func; | ||||
|   data->data = user_data; | ||||
|   event_funcs = g_list_prepend (event_funcs, data); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_events_x11_remove_func (GFunc    func, | ||||
|                                      gpointer user_data) | ||||
| { | ||||
|   EventFuncData *data; | ||||
|   GList *l; | ||||
|  | ||||
|   data = g_slice_new0 (EventFuncData); | ||||
|  | ||||
|   for (l = event_funcs; l; l = l->next) | ||||
|     { | ||||
|       data = l->data; | ||||
|  | ||||
|       if (data->func != func || data->data != user_data) | ||||
|         continue; | ||||
|  | ||||
|       event_funcs = g_list_delete_link (event_funcs, l); | ||||
|       g_slice_free (EventFuncData, data); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,4 +28,9 @@ | ||||
| void meta_display_init_events_x11 (MetaDisplay *display); | ||||
| void meta_display_free_events_x11 (MetaDisplay *display); | ||||
|  | ||||
| void meta_display_events_x11_add_func    (GFunc    func, | ||||
|                                           gpointer user_data); | ||||
| void meta_display_events_x11_remove_func (GFunc    func, | ||||
|                                           gpointer user_data); | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user