Compare commits
	
		
			16 Commits
		
	
	
		
			wip/carlos
			...
			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