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