Compare commits

...

20 Commits

Author SHA1 Message Date
Carlos Garnacho
6b25d07342 wayland: Implement touchpad gesture events
On wayland, the gestures protocol defines a wl_pointer_gestures global
object, that will match in number with wl_seats, swipe and pinch
interfaces can be obtained from it, which events are translated into
GdkEventTouchpadSwipe/Pinch events.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
cc963ba9ba wayland: Add touchpad gestures protocol 2015-08-09 17:52:52 +02:00
Carlos Garnacho
548a9d2d69 gtkgesture: Add note to gtk_gesture_get_bounding_box()
This is the only function that's will be semantically confusing
with touchpad gesture events, explain what's to be expected there.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
11cf38df32 gtkgesture: Add touchpad gesture event bit to the controller evmask 2015-08-09 17:52:52 +02:00
Carlos Garnacho
b0e2269c36 gtkgesturedrag: Handle touchpad swipe events
These will only trigger the gesture if it's been created with
the same GtkGesture::n-points than n_fingers in the event.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
2fc8ecd13b gtkgestureswipe: Handle touchpad swipe events
These will only trigger the gesture if it's been created with
the same GtkGesture::n-points than n_fingers in the event.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
3f0024f238 gtkgesturerotate: Handle touchpad pinch events
We let these through in GtkEventController::filter, and handle
these especially on GtkGesture::update.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
b9afafa792 gtkgesturezoom: Handle touchpad pinch events
We let these through in GtkEventController::filter, and handle
these especially on GtkGesture::update.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
e86f078a54 gtkgesture: Add docs blurb about touchpad gestures
Not too long though, the precautions to take are minimal.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
6f4633846f gtkgesture: Accumulate touchpad events' dx/dy in point data
This will make the gesture "center" the pointer position, plus
the accumulated dx/dy throughout the gesture progress.
2015-08-09 17:52:52 +02:00
Carlos Garnacho
a02e5575bf gtkgesture: Handle touchpad events
These will be mutually exclusive with touch events, so it won't
be possible to trigger gestures through mixed input and whatnot.

The accounting of touchpad events is slightly different, there
will be a single internal PointData struct, stored in the hashtable
with the NULL event sequence/key (same than pointer events in
this regard), just that the events stored will be GdkEventTouchpad*,
so will hold information about all fingers at once.

But this difference is just internal, the GtkGesture API doesn't
make explicit assumptions about the number of points (the closest
to a per-point query API is gtk_gesture_get_sequences()). All
signals emitted just contain the last changed GdkEventSequence,
and API takes GdkEventSequences, so everything is consistent with
sequence=NULL for touchpad events.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
8d13607216 gtkgesture: Refactor n-points querying into a single function
Along the code, we're basically asking for 1) the total count of
touchpoints, and 2) the number of active touchpoints (not denied
nor ended).

Wrap both usecases into a _gtk_gesture_get_n_physical_touchpoints(),
and replace all occurrences.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
89647fd4cd gtkgesture: Filter out touchpad events by default.
The gestures that don't want touchpad gesture events are majority,
even those that want such events will only listen to subsets (eg.
pinch, swipe,...).

So it makes sense to ignore touchpad events by default, and let
subclasses opt those in.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
d7aeefb764 gtkgesture: Refactor gtk_gesture_handle_event()
We'll be dealing with event types and touchpad gesture phases,
replace the switch by a battery of if/elses. Otherwise, it's
just an style change.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
61c205d3ae eventcontroller: Add private ::filter method
This will be used right before handle_event() in order to filter
out events, useful to make the previous "no touchpad events" behavior
the default, and have gesture subclasses include manually the touchpad
events they handle.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
7a288fb3e2 gtkwidget: Ensure touchpad events trigger the bubbling phase
For all other events, we run the bubble phase deep in the specific
::motion/button-press/release/touch handlers.

For touchpad events, it doesn't make sense to use GtkWidgetClass
slots if the intended way to deal with these are gestures, so we
run the bubble phase directly from gtk_widget_event_internal().
2015-08-09 17:52:51 +02:00
Carlos Garnacho
5d6e1e5a49 gtkmain: Handle rewriting of event fields during grabs for touchpad events
The window/coordinates will need translation in the case of touchpad gesture
events, same than with other pointer events.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
221d650a03 gdk: Proxy touchpad events through the client-side window hierarchy
This includes poking the deepmost window, checking for the
GDK_TOUCHPAD_GESTURE_MASK bit set, and translating coordinates in events
accordingly.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
e753ffd2ff gdk: Add GDK_TOUCHPAD_GESTURE_MASK to GdkEventMask
Users will need to manually select on this in order to receive gesture
events.
2015-08-09 17:52:51 +02:00
Carlos Garnacho
1320c17bdc gdk: Add touchpad gesture events and event types.
Each gesture type has its separate GdkEvent struct, and begin/update/
end/cancel event types.

There is support for multi-finger swipe (3-4 fingers), and 2-finger
rotate/pinch gestures.
2015-08-09 17:52:51 +02:00
19 changed files with 1106 additions and 55 deletions

View File

@@ -883,6 +883,8 @@ GdkEventWindowState
GdkEventSetting
GdkEventOwnerChange
GdkEventGrabBroken
GdkEventTouchpadSwipe
GdkEventTouchpadPinch
<SUBSECTION>
GdkScrollDirection

View File

@@ -570,6 +570,24 @@ gdk_event_new (GdkEventType type)
new_event->crossing.x_root = 0.;
new_event->crossing.y_root = 0.;
break;
case GDK_TOUCHPAD_SWIPE:
new_event->touchpad_swipe.x = 0;
new_event->touchpad_swipe.y = 0;
new_event->touchpad_swipe.dx = 0;
new_event->touchpad_swipe.dy = 0;
new_event->touchpad_swipe.x_root = 0;
new_event->touchpad_swipe.y_root = 0;
break;
case GDK_TOUCHPAD_PINCH:
new_event->touchpad_pinch.x = 0;
new_event->touchpad_pinch.y = 0;
new_event->touchpad_pinch.dx = 0;
new_event->touchpad_pinch.dy = 0;
new_event->touchpad_pinch.angle_delta = 0;
new_event->touchpad_pinch.scale = 0;
new_event->touchpad_pinch.x_root = 0;
new_event->touchpad_pinch.y_root = 0;
break;
default:
break;
}
@@ -863,6 +881,10 @@ gdk_event_get_time (const GdkEvent *event)
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
return event->touch.time;
case GDK_TOUCHPAD_SWIPE:
return event->touchpad_swipe.time;
case GDK_TOUCHPAD_PINCH:
return event->touchpad_pinch.time;
case GDK_SCROLL:
return event->scroll.time;
case GDK_KEY_PRESS:
@@ -946,6 +968,12 @@ gdk_event_get_state (const GdkEvent *event,
case GDK_TOUCH_CANCEL:
*state = event->touch.state;
return TRUE;
case GDK_TOUCHPAD_SWIPE:
*state = event->touchpad_swipe.state;
return TRUE;
case GDK_TOUCHPAD_PINCH:
*state = event->touchpad_pinch.state;
return TRUE;
case GDK_SCROLL:
*state = event->scroll.state;
return TRUE;
@@ -1046,6 +1074,14 @@ gdk_event_get_coords (const GdkEvent *event,
x = event->motion.x;
y = event->motion.y;
break;
case GDK_TOUCHPAD_SWIPE:
x = event->touchpad_swipe.x;
y = event->touchpad_swipe.y;
break;
case GDK_TOUCHPAD_PINCH:
x = event->touchpad_pinch.x;
y = event->touchpad_pinch.y;
break;
default:
fetched = FALSE;
break;
@@ -1117,6 +1153,14 @@ gdk_event_get_root_coords (const GdkEvent *event,
x = event->dnd.x_root;
y = event->dnd.y_root;
break;
case GDK_TOUCHPAD_SWIPE:
x = event->touchpad_swipe.x_root;
y = event->touchpad_swipe.y_root;
break;
case GDK_TOUCHPAD_PINCH:
x = event->touchpad_pinch.x_root;
y = event->touchpad_pinch.y_root;
break;
default:
fetched = FALSE;
break;

View File

@@ -139,6 +139,8 @@ typedef struct _GdkEventDND GdkEventDND;
typedef struct _GdkEventWindowState GdkEventWindowState;
typedef struct _GdkEventSetting GdkEventSetting;
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
typedef struct _GdkEventTouchpadSwipe GdkEventTouchpadSwipe;
typedef struct _GdkEventTouchpadPinch GdkEventTouchpadPinch;
typedef struct _GdkEventSequence GdkEventSequence;
@@ -271,6 +273,10 @@ typedef GdkFilterReturn (*GdkFilterFunc) (GdkXEvent *xevent,
* was added in 3.4.
* @GDK_TOUCH_CANCEL: A touch event sequence has been canceled. This event type
* was added in 3.4.
* @GDK_TOUCHPAD_SWIPE: A touchpad swipe gesture event, the current state
* is determined by its phase field. This event type was added in 3.18.
* @GDK_TOUCHPAD_PINCH: A touchpad pinch gesture event, the current state
* is determined by its phase field. This event type was added in 3.18.
* @GDK_EVENT_LAST: marks the end of the GdkEventType enumeration. Added in 2.18
*
* Specifies the type of the event.
@@ -331,6 +337,8 @@ typedef enum
GDK_TOUCH_UPDATE = 38,
GDK_TOUCH_END = 39,
GDK_TOUCH_CANCEL = 40,
GDK_TOUCHPAD_SWIPE = 41,
GDK_TOUCHPAD_PINCH = 42,
GDK_EVENT_LAST /* helper variable for decls */
} GdkEventType;
@@ -349,6 +357,43 @@ typedef enum
GDK_VISIBILITY_FULLY_OBSCURED
} GdkVisibilityState;
/**
* GdkTouchpadGesturePhase:
* @GDK_TOUCHPAD_GESTURE_PHASE_BEGIN: The gesture has begun.
* @GDK_TOUCHPAD_GESTURE_PHASE_UPDATE: The gesture has been updated.
* @GDK_TOUCHPAD_GESTURE_PHASE_END: The gesture was finished, changes
* should be permanently applied.
* @GDK_TOUCHPAD_GESTURE_PHASE_CANCEL: The gesture was cancelled, all
* changes should be undone.
*
* Specifies the current state of a touchpad gesture. All gestures are
* guaranteed to begin with an event with phase %GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
* followed by 0 or several events with phase %GDK_TOUCHPAD_GESTURE_PHASE_UPDATE.
*
* A finished gesture may have 2 possible outcomes, an event with phase
* %GDK_TOUCHPAD_GESTURE_PHASE_END will be emitted when the gesture is
* considered successful, this should be used as the hint to perform any
* permanent changes.
* Cancelled gestures may be so for a variety of reasons, due to hardware
* or the compositor, or due to the gesture recognition layers hinting the
* gesture did not finish resolutely (eg. a 3rd finger being added during
* a pinch gesture). In these cases, the last event will report the phase
* %GDK_TOUCHPAD_GESTURE_PHASE_CANCEL, this should be used as a hint
* to undo any visible/permanent changes that were done throughout the
* progress of the gesture.
*
* See also #GdkEventTouchpadSwipe and #GdkEventTouchpadPinch.
*
*/
typedef enum
{
GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
GDK_TOUCHPAD_GESTURE_PHASE_UPDATE,
GDK_TOUCHPAD_GESTURE_PHASE_END,
GDK_TOUCHPAD_GESTURE_PHASE_CANCEL
} GdkTouchpadGesturePhase;
/**
* GdkScrollDirection:
* @GDK_SCROLL_UP: the window is scrolled up.
@@ -1113,6 +1158,86 @@ struct _GdkEventDND {
gshort x_root, y_root;
};
/**
* GdkEventTouchpadSwipe:
* @type: the type of the event (%GDK_TOUCHPAD_SWIPE)
* @window: the window which received the event
* @send_event: %TRUE if the event was sent explicitly
* @phase: (type GdkTouchpadGesturePhase): the current phase of the gesture
* @n_fingers: The number of fingers triggering the swipe
* @time: the time of the event in milliseconds
* @x: The X coordinate of the pointer
* @y: The Y coordinate of the pointer
* @dx: Movement delta in the X axis of the swipe focal point
* @dy: Movement delta in the Y axis of the swipe focal point
* @x_root: The X coordinate of the pointer, relative to the
* root of the screen.
* @y_root: The Y coordinate of the pointer, relative to the
* root of the screen.
* @state: (type GdkModifierType): a bit-mask representing the state of
* the modifier keys (e.g. Control, Shift and Alt) and the pointer
* buttons. See #GdkModifierType.
*
* Generated during touchpad swipe gestures.
*/
struct _GdkEventTouchpadSwipe {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
gint8 phase;
gint8 n_fingers;
guint32 time;
gdouble x;
gdouble y;
gdouble dx;
gdouble dy;
gdouble x_root, y_root;
guint state;
};
/**
* GdkEventTouchpadPinch:
* @type: the type of the event (%GDK_TOUCHPAD_PINCH)
* @window: the window which received the event
* @send_event: %TRUE if the event was sent explicitly
* @phase: (type GdkTouchpadGesturePhase): the current phase of the gesture
* @n_fingers: The number of fingers triggering the pinch
* @time: the time of the event in milliseconds
* @x: The X coordinate of the pointer
* @y: The Y coordinate of the pointer
* @dx: Movement delta in the X axis of the swipe focal point
* @dy: Movement delta in the Y axis of the swipe focal point
* @angle_delta: The angle change in radians, negative angles
* denote counter-clockwise movements
* @scale: The current scale, relative to that at the time of
* the corresponding %GDK_TOUCHPAD_GESTURE_PHASE_BEGIN event
* @x_root: The X coordinate of the pointer, relative to the
* root of the screen.
* @y_root: The Y coordinate of the pointer, relative to the
* root of the screen.
* @state: (type GdkModifierType): a bit-mask representing the state of
* the modifier keys (e.g. Control, Shift and Alt) and the pointer
* buttons. See #GdkModifierType.
*
* Generated during touchpad swipe gestures.
*/
struct _GdkEventTouchpadPinch {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
gint8 phase;
gint8 n_fingers;
guint32 time;
gdouble x;
gdouble y;
gdouble dx;
gdouble dy;
gdouble angle_delta;
gdouble scale;
gdouble x_root, y_root;
guint state;
};
/**
* GdkEvent:
* @type: the #GdkEventType
@@ -1189,6 +1314,8 @@ union _GdkEvent
GdkEventWindowState window_state;
GdkEventSetting setting;
GdkEventGrabBroken grab_broken;
GdkEventTouchpadSwipe touchpad_swipe;
GdkEventTouchpadPinch touchpad_pinch;
};
GDK_AVAILABLE_IN_ALL

View File

@@ -440,6 +440,7 @@ typedef enum
GDK_SCROLL_MASK = 1 << 21,
GDK_TOUCH_MASK = 1 << 22,
GDK_SMOOTH_SCROLL_MASK = 1 << 23,
GDK_TOUCHPAD_GESTURE_MASK = 1 << 24,
GDK_ALL_EVENTS_MASK = 0xFFFFFE
} GdkEventMask;

View File

@@ -7565,7 +7565,9 @@ static const guint type_masks[] = {
GDK_TOUCH_MASK, /* GDK_TOUCH_BEGIN = 37 */
GDK_TOUCH_MASK, /* GDK_TOUCH_UPDATE = 38 */
GDK_TOUCH_MASK, /* GDK_TOUCH_END = 39 */
GDK_TOUCH_MASK /* GDK_TOUCH_CANCEL = 40 */
GDK_TOUCH_MASK, /* GDK_TOUCH_CANCEL = 40 */
GDK_TOUCHPAD_GESTURE_MASK, /* GDK_TOUCHPAD_SWIPE = 41 */
GDK_TOUCHPAD_GESTURE_MASK, /* GDK_TOUCHPAD_PINCH = 42 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
@@ -7602,6 +7604,13 @@ is_button_type (GdkEventType type)
type == GDK_SCROLL;
}
static gboolean
is_gesture_type (GdkEventType type)
{
return (type == GDK_TOUCHPAD_SWIPE ||
type == GDK_TOUCHPAD_PINCH);
}
static gboolean
is_motion_type (GdkEventType type)
{
@@ -7743,6 +7752,16 @@ _gdk_make_event (GdkWindow *window,
event->dnd.time = the_time;
break;
case GDK_TOUCHPAD_SWIPE:
event->touchpad_swipe.time = the_time;
event->touchpad_swipe.state = the_state;
break;
case GDK_TOUCHPAD_PINCH:
event->touchpad_pinch.time = the_time;
event->touchpad_pinch.state = the_state;
break;
case GDK_FOCUS_CHANGE:
case GDK_CONFIGURE:
case GDK_MAP:
@@ -9269,6 +9288,85 @@ proxy_button_event (GdkEvent *source_event,
return TRUE; /* Always unlink original, we want to obey the emulated event mask */
}
static gboolean
proxy_gesture_event (GdkEvent *source_event,
gulong serial)
{
GdkWindow *toplevel_window, *pointer_window, *event_win;
GdkDevice *device, *source_device;
gdouble toplevel_x, toplevel_y;
GdkDisplay *display;
GdkEventMask evmask;
GdkEventType evtype;
GdkEvent *event;
guint state;
evtype = source_event->any.type;
gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
gdk_event_get_state (source_event, &state);
device = gdk_event_get_device (source_event);
source_device = gdk_event_get_source_device (source_event);
display = gdk_window_get_display (source_event->any.window);
toplevel_window = convert_native_coords_to_toplevel (source_event->any.window,
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
pointer_window = get_pointer_window (display, toplevel_window, device,
toplevel_x, toplevel_y,
serial);
event_win = get_event_window (display, device, NULL,
pointer_window, evtype, state,
&evmask, FALSE, serial);
if (!event_win)
return TRUE;
if ((evmask & GDK_TOUCHPAD_GESTURE_MASK) == 0)
return TRUE;
event = _gdk_make_event (event_win, evtype, source_event, FALSE);
gdk_event_set_device (event, device);
gdk_event_set_source_device (event, source_device);
switch (evtype)
{
case GDK_TOUCHPAD_SWIPE:
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->touchpad_swipe.x,
&event->touchpad_swipe.y);
gdk_event_get_root_coords (source_event,
&event->touchpad_swipe.x_root,
&event->touchpad_swipe.y_root);
event->touchpad_swipe.dx = source_event->touchpad_swipe.dx;
event->touchpad_swipe.dy = source_event->touchpad_swipe.dy;
event->touchpad_swipe.n_fingers = source_event->touchpad_swipe.n_fingers;
event->touchpad_swipe.phase = source_event->touchpad_swipe.phase;
break;
case GDK_TOUCHPAD_PINCH:
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->touchpad_pinch.x,
&event->touchpad_pinch.y);
gdk_event_get_root_coords (source_event,
&event->touchpad_pinch.x_root,
&event->touchpad_pinch.y_root);
event->touchpad_pinch.dx = source_event->touchpad_pinch.dx;
event->touchpad_pinch.dy = source_event->touchpad_pinch.dy;
event->touchpad_pinch.scale = source_event->touchpad_pinch.scale;
event->touchpad_pinch.angle_delta = source_event->touchpad_pinch.angle_delta;
event->touchpad_pinch.n_fingers = source_event->touchpad_pinch.n_fingers;
event->touchpad_pinch.phase = source_event->touchpad_pinch.phase;
break;
default:
break;
}
return TRUE;
}
#ifdef DEBUG_WINDOW_PRINTING
#ifdef GDK_WINDOWING_X11
@@ -9417,7 +9515,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
}
if (!(is_button_type (event->type) ||
is_motion_type (event->type)) ||
is_motion_type (event->type) ||
is_gesture_type (event->type)) ||
event_window->window_type == GDK_WINDOW_ROOT)
goto out;
@@ -9516,6 +9615,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
unlink_event = proxy_pointer_event (display, event, serial);
else if (is_button_type (event->type))
unlink_event = proxy_button_event (event, serial);
else if (is_gesture_type (event->type))
unlink_event = proxy_gesture_event (event, serial);
if ((event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_END) &&

View File

@@ -20,6 +20,8 @@ noinst_LTLIBRARIES = \
libgdk-wayland.la
BUILT_SOURCES = \
pointer-gestures-client-protocol.h \
pointer-gestures-protocol.c \
xdg-shell-client-protocol.h \
xdg-shell-protocol.c \
gtk-shell-client-protocol.h \
@@ -70,6 +72,7 @@ libgdkwaylandinclude_HEADERS = \
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
EXTRA_DIST += \
protocol/pointer-gestures.xml \
protocol/xdg-shell.xml \
protocol/gtk-shell.xml

View File

@@ -29,6 +29,7 @@
#include "gdkkeysyms.h"
#include "gdkdeviceprivate.h"
#include "gdkdevicemanagerprivate.h"
#include "pointer-gestures-client-protocol.h"
#include <xkbcommon/xkbcommon.h>
@@ -54,6 +55,8 @@ struct _GdkWaylandDeviceData
struct wl_pointer *wl_pointer;
struct wl_keyboard *wl_keyboard;
struct wl_touch *wl_touch;
struct _wl_pointer_gesture_swipe *wl_pointer_gesture_swipe;
struct _wl_pointer_gesture_pinch *wl_pointer_gesture_pinch;
GdkDisplay *display;
GdkDeviceManager *device_manager;
@@ -99,6 +102,10 @@ struct _GdkWaylandDeviceData
/* Source/dest for non-local dnd */
GdkWindow *foreign_dnd_window;
/* Some tracking on gesture events */
guint gesture_n_fingers;
gdouble gesture_scale;
};
struct _GdkWaylandDevice
@@ -1653,6 +1660,213 @@ touch_handle_cancel (void *data,
GDK_NOTE (EVENTS, g_message ("touch cancel"));
}
static void
emit_gesture_swipe_event (GdkWaylandDeviceData *device,
GdkTouchpadGesturePhase phase,
guint32 _time,
guint32 n_fingers,
gdouble dx,
gdouble dy)
{
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
if (!device->pointer_focus)
return;
device->time = _time;
event = gdk_event_new (GDK_TOUCHPAD_SWIPE);
event->touchpad_swipe.phase = phase;
event->touchpad_swipe.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
event->touchpad_swipe.time = _time;
event->touchpad_swipe.state = device->button_modifiers | device->key_modifiers;
gdk_event_set_screen (event, display->screen);
event->touchpad_swipe.dx = dx;
event->touchpad_swipe.dy = dy;
event->touchpad_swipe.n_fingers = n_fingers;
get_coordinates (device,
&event->touchpad_swipe.x,
&event->touchpad_swipe.y,
&event->touchpad_swipe.x_root,
&event->touchpad_swipe.y_root);
GDK_NOTE (EVENTS,
g_message ("swipe event %d, coords: %f %f, device %p state %d",
event->type, event->touchpad_swipe.x,
event->touchpad_swipe.y, device,
event->touchpad_swipe.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
gesture_swipe_begin (void *data,
struct _wl_pointer_gesture_swipe *swipe,
uint32_t serial,
uint32_t time,
struct wl_surface *surface,
uint32_t fingers)
{
GdkWaylandDeviceData *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
_gdk_wayland_display_update_serial (display, serial);
emit_gesture_swipe_event (device,
GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
time, fingers, 0, 0);
device->gesture_n_fingers = fingers;
}
static void
gesture_swipe_update (void *data,
struct _wl_pointer_gesture_swipe *swipe,
uint32_t time,
wl_fixed_t dx,
wl_fixed_t dy)
{
GdkWaylandDeviceData *device = data;
emit_gesture_swipe_event (device,
GDK_TOUCHPAD_GESTURE_PHASE_UPDATE,
time,
device->gesture_n_fingers,
wl_fixed_to_double (dx),
wl_fixed_to_double (dy));
}
static void
gesture_swipe_end (void *data,
struct _wl_pointer_gesture_swipe *swipe,
uint32_t serial,
uint32_t time,
int32_t cancelled)
{
GdkWaylandDeviceData *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkTouchpadGesturePhase phase;
_gdk_wayland_display_update_serial (display, serial);
phase = (cancelled) ?
GDK_TOUCHPAD_GESTURE_PHASE_CANCEL :
GDK_TOUCHPAD_GESTURE_PHASE_END;
emit_gesture_swipe_event (device, phase, time,
device->gesture_n_fingers, 0, 0);
}
static void
emit_gesture_pinch_event (GdkWaylandDeviceData *device,
GdkTouchpadGesturePhase phase,
guint32 _time,
guint n_fingers,
gdouble dx,
gdouble dy,
gdouble scale,
gdouble angle_delta)
{
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
if (!device->pointer_focus)
return;
device->time = _time;
event = gdk_event_new (GDK_TOUCHPAD_PINCH);
event->touchpad_pinch.phase = phase;
event->touchpad_pinch.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
event->touchpad_pinch.time = _time;
event->touchpad_pinch.state = device->button_modifiers | device->key_modifiers;
gdk_event_set_screen (event, display->screen);
event->touchpad_pinch.dx = dx;
event->touchpad_pinch.dy = dy;
event->touchpad_pinch.scale = scale;
event->touchpad_pinch.angle_delta = angle_delta * G_PI / 180;
event->touchpad_pinch.n_fingers = n_fingers;
get_coordinates (device,
&event->touchpad_pinch.x,
&event->touchpad_pinch.y,
&event->touchpad_pinch.x_root,
&event->touchpad_pinch.y_root);
GDK_NOTE (EVENTS,
g_message ("pinch event %d, coords: %f %f, device %p state %d",
event->type, event->touchpad_pinch.x,
event->touchpad_pinch.y, device,
event->touchpad_pinch.state));
_gdk_wayland_display_deliver_event (device->display, event);
}
static void
gesture_pinch_begin (void *data,
struct _wl_pointer_gesture_pinch *pinch,
uint32_t serial,
uint32_t time,
struct wl_surface *surface,
uint32_t fingers)
{
GdkWaylandDeviceData *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
_gdk_wayland_display_update_serial (display, serial);
emit_gesture_pinch_event (device,
GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
time, fingers, 0, 0, 1, 0);
device->gesture_n_fingers = fingers;
}
static void
gesture_pinch_update (void *data,
struct _wl_pointer_gesture_pinch *pinch,
uint32_t time,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t scale,
wl_fixed_t rotation)
{
GdkWaylandDeviceData *device = data;
emit_gesture_pinch_event (device,
GDK_TOUCHPAD_GESTURE_PHASE_UPDATE, time,
device->gesture_n_fingers,
wl_fixed_to_double (dx),
wl_fixed_to_double (dy),
wl_fixed_to_double (scale),
wl_fixed_to_double (rotation));
}
static void
gesture_pinch_end (void *data,
struct _wl_pointer_gesture_pinch *pinch,
uint32_t serial,
uint32_t time,
int32_t cancelled)
{
GdkWaylandDeviceData *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkTouchpadGesturePhase phase;
_gdk_wayland_display_update_serial (display, serial);
phase = (cancelled) ?
GDK_TOUCHPAD_GESTURE_PHASE_CANCEL :
GDK_TOUCHPAD_GESTURE_PHASE_END;
emit_gesture_pinch_event (device, phase,
time, device->gesture_n_fingers,
0, 0, 1, 0);
}
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
pointer_handle_leave,
@@ -1678,6 +1892,18 @@ static const struct wl_touch_listener touch_listener = {
touch_handle_cancel
};
static const struct _wl_pointer_gesture_swipe_listener gesture_swipe_listener = {
gesture_swipe_begin,
gesture_swipe_update,
gesture_swipe_end
};
static const struct _wl_pointer_gesture_pinch_listener gesture_pinch_listener = {
gesture_pinch_begin,
gesture_pinch_update,
gesture_pinch_end
};
static void
seat_handle_capabilities (void *data,
struct wl_seat *seat,
@@ -1685,6 +1911,7 @@ seat_handle_capabilities (void *data,
{
GdkWaylandDeviceData *device = data;
GdkWaylandDeviceManager *device_manager = GDK_WAYLAND_DEVICE_MANAGER (device->device_manager);
GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (device->display);
GDK_NOTE (MISC,
g_message ("seat %p with %s%s%s", seat,
@@ -1715,6 +1942,24 @@ seat_handle_capabilities (void *data,
device->drop_context = _gdk_wayland_drop_context_new (device->master_pointer,
device->data_device);
if (wayland_display->pointer_gestures)
{
device->wl_pointer_gesture_swipe =
_wl_pointer_gestures_get_swipe_gesture (wayland_display->pointer_gestures,
device->wl_pointer);
_wl_pointer_gesture_swipe_set_user_data (device->wl_pointer_gesture_swipe,
device);
_wl_pointer_gesture_swipe_add_listener (device->wl_pointer_gesture_swipe,
&gesture_swipe_listener, device);
device->wl_pointer_gesture_pinch =
_wl_pointer_gestures_get_pinch_gesture (wayland_display->pointer_gestures,
device->wl_pointer);
_wl_pointer_gesture_pinch_set_user_data (device->wl_pointer_gesture_pinch,
device);
_wl_pointer_gesture_pinch_add_listener (device->wl_pointer_gesture_pinch,
&gesture_pinch_listener, device);
}
g_signal_emit_by_name (device_manager, "device-added", device->pointer);
}

View File

@@ -35,6 +35,7 @@
#include "gdkkeysprivate.h"
#include "gdkprivate-wayland.h"
#include "gdkglcontext-wayland.h"
#include "pointer-gestures-client-protocol.h"
/**
* SECTION:wayland_interaction
@@ -357,6 +358,11 @@ gdk_registry_handle_global (void *data,
display_wayland->subcompositor =
wl_registry_bind (display_wayland->wl_registry, id, &wl_subcompositor_interface, 1);
}
else if (strcmp (interface, "_wl_pointer_gestures") == 0)
{
display_wayland->pointer_gestures =
wl_registry_bind (display_wayland->wl_registry, id, &_wl_pointer_gestures_interface, 1);
}
else
handled = FALSE;

View File

@@ -71,6 +71,7 @@ struct _GdkWaylandDisplay
struct wl_input_device *input_device;
struct wl_data_device_manager *data_device_manager;
struct wl_subcompositor *subcompositor;
struct _wl_pointer_gestures *pointer_gestures;
GList *async_roundtrips;

View File

@@ -0,0 +1,172 @@
<protocol name="pointer_gestures">
<interface name="_wl_pointer_gestures" version="1">
<description summary="touchpad gestures">
A global interface to provide semantic touchpad gestures for a given
pointer.
Two gestures are currently supported: swipe and zoom/rotate.
All gestures follow a three-stage cycle: begin, update, end and
are identified by a unique id.
Warning! The protocol described in this file is experimental. Each
version of this protocol should be considered incompatible with any
other version, and a client binding to a version different to the one
advertised will be terminated. Once the protocol is declared stable,
compatibility is guaranteed, the '_' prefix will be removed from the
name and the version will be reset to 1.
</description>
<request name="get_swipe_gesture">
<description summary="get swipe gesture">
Create a swipe gesture object. See the
wl_pointer_gesture_swipe interface for details.
</description>
<arg name="id" type="new_id" interface="_wl_pointer_gesture_swipe"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
<request name="get_pinch_gesture">
<description summary="get pinch gesture">
Create a pinch gesture object. See the
wl_pointer_gesture_pinch interface for details.
</description>
<arg name="id" type="new_id" interface="_wl_pointer_gesture_pinch"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
</interface>
<interface name="_wl_pointer_gesture_swipe" version="1">
<description summary="a swipe gesture object">
A swipe gesture object notifies a client about a multi-finger swipe
gesture detected on an indirect input device such as a touchpad.
The gesture is usually initiated by multiple fingers moving in the
same direction but once initiated the direction may change.
The precise conditions of when such a gesture is detected are
implementation-dependent.
A gesture consists of three stages: begin, update (optional) and end.
There cannot be multiple simultaneous pinch or swipe gestures on a
same pointer/seat, how compositors prevent these situations is
implementation-dependent.
A gesture may be cancelled by the compositor or the hardware.
Clients should not consider performing permanent or irreversible
actions until the end of a gesture has been received.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the pointer swipe gesture object"/>
</request>
<event name="begin">
<description summary="multi-finger swipe begin">
This event is sent when a multi-finger swipe gesture is detected
on the device.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="fingers" type="uint" summary="number of fingers"/>
</event>
<event name="update">
<description summary="multi-finger swipe motion">
This event is sent when a multi-finger swipe gesture changes the
position of the logical center.
The dx and dy coordinates are relative coordinates of the logical
center of the gesture compared to the previous event.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="delta x coordinate in surface coordinate space"/>
<arg name="dy" type="fixed" summary="delta y coordinate in surface coordinate space"/>
</event>
<event name="end">
<description summary="multi-finger swipe end">
This event is sent when a multi-finger swipe gesture ceases to
be valid. This may happen when one or more finger is lifted or
the gesture is cancelled.
When a gesture is cancelled, the client should undo state changes
caused by this gesture. What causes a gesture to be cancelled is
implementation-dependent.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="cancelled" type="int" summary="1 if the gesture was cancelled, 0 otherwise"/>
</event>
</interface>
<interface name="_wl_pointer_gesture_pinch" version="1">
<description summary="a pinch gesture object">
A pinch gesture object notifies a client about a multi-finger pinch
gesture detected on an indirect input device such as a touchpad.
The gesture is usually initiated by multiple fingers moving towards
each other or away from each other, or by two or more fingers rotating
around a logical center of gravity. The precise conditions of when
such a gesture is detected are implementation-dependent.
A gesture consists of three stages: begin, update (optional) and end.
There cannot be multiple simultaneous pinch or swipe gestures on a
same pointer/seat, how compositors prevent these situations is
implementation-dependent.
A gesture may be cancelled by the compositor or the hardware.
Clients should not consider performing permanent or irreversible
actions until the end of a gesture has been received.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the pinch gesture object"/>
</request>
<event name="begin">
<description summary="multi-finger pinch begin">
This event is sent when a multi-finger pinch gesture is detected
on the device.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="fingers" type="uint" summary="number of fingers"/>
</event>
<event name="update">
<description summary="multi-finger pinch motion">
This event is sent when a multi-finger pinch gesture changes the
position of the logical center, the rotation or the relative scale.
The dx and dy coordinates are relative coordinates in the
surface coordinate space of the logical center of the gesture.
The scale factor is an absolute scale compared to the
pointer_gesture_pinch.begin event, e.g. a scale of 2 means the fingers
are now twice as far apart as on pointer_gesture_pinch.begin.
The rotation is the relative angle in degrees clockwise compared to the previous
pointer_gesture_pinch.begin or pointer_gesture_pinch.update event.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="delta x coordinate in surface coordinate space"/>
<arg name="dy" type="fixed" summary="delta y coordinate in surface coordinate space"/>
<arg name="scale" type="fixed" summary="scale relative to the initial finger position"/>
<arg name="rotation" type="fixed" summary="angle in degrees cw relative to the previous event"/>
</event>
<event name="end">
<description summary="multi-finger pinch end">
This event is sent when a multi-finger pinch gesture ceases to
be valid. This may happen when one or more finger is lifted or
the gesture is cancelled.
When a gesture is cancelled, the client should undo state changes
caused by this gesture. What causes a gesture to be cancelled is
implementation-dependent.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="cancelled" type="int" summary="1 if the gesture was cancelled, 0 otherwise"/>
</event>
</interface>
</protocol>

View File

@@ -145,6 +145,7 @@ gtk_event_controller_class_init (GtkEventControllerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
klass->filter_event = gtk_event_controller_handle_event_default;
klass->handle_event = gtk_event_controller_handle_event_default;
object_class->set_property = gtk_event_controller_set_property;
@@ -218,6 +219,9 @@ gtk_event_controller_handle_event (GtkEventController *controller,
controller_class = GTK_EVENT_CONTROLLER_GET_CLASS (controller);
if (controller_class->filter_event (controller, event))
return retval;
if (controller_class->handle_event)
{
g_object_ref (controller);

View File

@@ -36,6 +36,9 @@ struct _GtkEventControllerClass
void (* reset) (GtkEventController *controller);
/*<private>*/
gboolean (* filter_event) (GtkEventController *controller,
const GdkEvent *event);
gpointer padding[10];
};

View File

@@ -104,6 +104,15 @@
*
* Sequence states can't be changed freely, see gtk_gesture_set_sequence_state()
* to know about the possible lifetimes of a #GdkEventSequence.
*
* ## Touchpad gestures
*
* On the platforms that support it, #GtkGesture will handle transparently
* touchpad gesture events. The only precautions users of #GtkGesture should do
* to enable this support are:
* - Enabling %GDK_TOUCHPAD_GESTURE_MASK on their #GdkWindows
* - If the gesture has %GTK_PHASE_NONE, ensuring events of type
* %GDK_TOUCHPAD_SWIPE and %GDK_TOUCHPAD_PINCH are handled by the #GtkGesture
*/
#include "config.h"
@@ -138,6 +147,11 @@ struct _PointData
GdkEvent *event;
gdouble widget_x;
gdouble widget_y;
/* Acummulators for touchpad events */
gdouble accum_dx;
gdouble accum_dy;
guint press_handled : 1;
guint state : 2;
};
@@ -152,12 +166,16 @@ struct _GtkGesturePrivate
GList *group_link;
guint n_points;
guint recognized : 1;
guint touchpad : 1;
};
static guint signals[N_SIGNALS] = { 0 };
#define BUTTONS_MASK (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)
#define EVENT_IS_TOUCHPAD_GESTURE(e) ((e)->type == GDK_TOUCHPAD_SWIPE || \
(e)->type == GDK_TOUCHPAD_PINCH)
GList * _gtk_gesture_get_group_link (GtkGesture *gesture);
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkGesture, gtk_gesture, GTK_TYPE_EVENT_CONTROLLER)
@@ -220,7 +238,44 @@ gtk_gesture_finalize (GObject *object)
}
static guint
_gtk_gesture_effective_n_points (GtkGesture *gesture)
_gtk_gesture_get_n_touchpad_points (GtkGesture *gesture,
gboolean only_active)
{
GtkGesturePrivate *priv;
PointData *data;
priv = gtk_gesture_get_instance_private (gesture);
if (!priv->touchpad)
return 0;
data = g_hash_table_lookup (priv->points, NULL);
if (!data)
return 0;
if (only_active &&
(data->state == GTK_EVENT_SEQUENCE_DENIED ||
(data->event->type == GDK_TOUCHPAD_SWIPE &&
data->event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_END) ||
(data->event->type == GDK_TOUCHPAD_PINCH &&
data->event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END)))
return 0;
switch (data->event->type)
{
case GDK_TOUCHPAD_SWIPE:
return data->event->touchpad_swipe.n_fingers;
case GDK_TOUCHPAD_PINCH:
return data->event->touchpad_pinch.n_fingers;
default:
return 0;
}
}
static guint
_gtk_gesture_get_n_touch_points (GtkGesture *gesture,
gboolean only_active)
{
GtkGesturePrivate *priv;
GHashTableIter iter;
@@ -232,9 +287,10 @@ _gtk_gesture_effective_n_points (GtkGesture *gesture)
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
{
if (data->state == GTK_EVENT_SEQUENCE_DENIED ||
data->event->type == GDK_TOUCH_END ||
data->event->type == GDK_BUTTON_RELEASE)
if (only_active &&
(data->state == GTK_EVENT_SEQUENCE_DENIED ||
data->event->type == GDK_TOUCH_END ||
data->event->type == GDK_BUTTON_RELEASE))
continue;
n_points++;
@@ -243,6 +299,20 @@ _gtk_gesture_effective_n_points (GtkGesture *gesture)
return n_points;
}
static guint
_gtk_gesture_get_n_physical_points (GtkGesture *gesture,
gboolean only_active)
{
GtkGesturePrivate *priv;
priv = gtk_gesture_get_instance_private (gesture);
if (priv->touchpad)
return _gtk_gesture_get_n_touchpad_points (gesture, only_active);
else
return _gtk_gesture_get_n_touch_points (gesture, only_active);
}
static gboolean
gtk_gesture_check_impl (GtkGesture *gesture)
{
@@ -250,7 +320,7 @@ gtk_gesture_check_impl (GtkGesture *gesture)
guint n_points;
priv = gtk_gesture_get_instance_private (gesture);
n_points = _gtk_gesture_effective_n_points (gesture);
n_points = _gtk_gesture_get_n_physical_points (gesture, TRUE);
return n_points == priv->n_points;
}
@@ -294,12 +364,13 @@ static gboolean
_gtk_gesture_has_matching_touchpoints (GtkGesture *gesture)
{
GtkGesturePrivate *priv = gtk_gesture_get_instance_private (gesture);
guint current_n_points;
guint active_n_points, current_n_points;
current_n_points = _gtk_gesture_effective_n_points (gesture);
current_n_points = _gtk_gesture_get_n_physical_points (gesture, FALSE);
active_n_points = _gtk_gesture_get_n_physical_points (gesture, TRUE);
return (current_n_points == priv->n_points &&
g_hash_table_size (priv->points) == priv->n_points);
return (active_n_points == priv->n_points &&
current_n_points == priv->n_points);
}
static gboolean
@@ -343,6 +414,55 @@ _find_widget_window (GtkGesture *gesture,
return NULL;
}
static void
_update_touchpad_deltas (PointData *data)
{
GdkEvent *event = data->event;
if (!event)
return;
if (event->type == GDK_TOUCHPAD_SWIPE)
{
if (event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
data->accum_dx = data->accum_dy = 0;
else if (event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE)
{
data->accum_dx += event->touchpad_swipe.dx;
data->accum_dy += event->touchpad_swipe.dy;
}
}
else if (event->type == GDK_TOUCHPAD_PINCH)
{
if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
data->accum_dx = data->accum_dy = 0;
else if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE)
{
data->accum_dx += event->touchpad_pinch.dx;
data->accum_dy += event->touchpad_pinch.dy;
}
}
}
static void
_get_event_coordinates (PointData *data,
gdouble *x,
gdouble *y)
{
gdouble event_x, event_y;
g_assert (data->event != NULL);
gdk_event_get_coords (data->event, &event_x, &event_y);
event_x += data->accum_dx;
event_y += data->accum_dy;
if (x)
*x = event_x;
if (y)
*y = event_y;
}
static void
_update_widget_coordinates (GtkGesture *gesture,
PointData *data)
@@ -356,7 +476,7 @@ _update_widget_coordinates (GtkGesture *gesture,
event_widget = gtk_get_event_widget (data->event);
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
event_widget_window = gtk_widget_get_window (event_widget);
gdk_event_get_coords (data->event, &event_x, &event_y);
_get_event_coordinates (data, &event_x, &event_y);
window = data->event->any.window;
while (window && window != event_widget_window)
@@ -415,7 +535,7 @@ _gtk_gesture_update_point (GtkGesture *gesture,
GdkWindow *widget_window;
GtkGesturePrivate *priv;
GdkDevice *device;
gboolean existed;
gboolean existed, touchpad;
PointData *data;
if (!gdk_event_get_coords (event, NULL, NULL))
@@ -432,6 +552,8 @@ _gtk_gesture_update_point (GtkGesture *gesture,
if (!widget_window)
return FALSE;
touchpad = EVENT_IS_TOUCHPAD_GESTURE (event);
if (add)
{
/* If the event happens with the wrong device, or
@@ -443,6 +565,12 @@ _gtk_gesture_update_point (GtkGesture *gesture,
return FALSE;
if (priv->user_window && priv->user_window != widget_window)
return FALSE;
/* Make touchpad and touchscreen gestures mutually exclusive */
if (touchpad && g_hash_table_size (priv->points) > 0)
return FALSE;
else if (!touchpad && priv->touchpad)
return FALSE;
}
else if (!priv->device || !priv->window)
return FALSE;
@@ -461,6 +589,7 @@ _gtk_gesture_update_point (GtkGesture *gesture,
{
priv->window = widget_window;
priv->device = device;
priv->touchpad = touchpad;
}
data = g_new0 (PointData, 1);
@@ -474,13 +603,14 @@ _gtk_gesture_update_point (GtkGesture *gesture,
gdk_event_free (data->event);
data->event = gdk_event_copy (event);
_update_touchpad_deltas (data);
_update_widget_coordinates (gesture, data);
/* Deny the sequence right away if the expected
* number of points is exceeded, so this sequence
* can be tracked with gtk_gesture_handles_sequence().
*/
if (!existed && g_hash_table_size (priv->points) > priv->n_points)
if (!existed && _gtk_gesture_get_n_physical_points (gesture, FALSE) > priv->n_points)
gtk_gesture_set_sequence_state (gesture, sequence,
GTK_EVENT_SEQUENCE_DENIED);
@@ -498,6 +628,7 @@ _gtk_gesture_check_empty (GtkGesture *gesture)
{
priv->window = NULL;
priv->device = NULL;
priv->touchpad = FALSE;
}
}
@@ -561,6 +692,18 @@ gesture_within_window (GtkGesture *gesture,
return FALSE;
}
static gboolean
gtk_gesture_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
/* Even though GtkGesture handles these events, we want
* touchpad gestures disabled by default, it will be
* subclasses which punch the holes in for the events
* they can possibly handle.
*/
return EVENT_IS_TOUCHPAD_GESTURE (event);
}
static gboolean
gtk_gesture_handle_event (GtkEventController *controller,
const GdkEvent *event)
@@ -583,10 +726,13 @@ gtk_gesture_handle_event (GtkEventController *controller,
if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_DENIED)
priv->last_sequence = sequence;
switch (event->type)
if (event->type == GDK_BUTTON_PRESS ||
event->type == GDK_TOUCH_BEGIN ||
(event->type == GDK_TOUCHPAD_SWIPE &&
event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN) ||
(event->type == GDK_TOUCHPAD_PINCH &&
event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN))
{
case GDK_BUTTON_PRESS:
case GDK_TOUCH_BEGIN:
if (_gtk_gesture_update_point (gesture, event, TRUE))
{
gboolean triggered_recognition;
@@ -613,10 +759,14 @@ gtk_gesture_handle_event (GtkEventController *controller,
return TRUE;
}
}
break;
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_END:
}
else if (event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_END ||
(event->type == GDK_TOUCHPAD_SWIPE &&
event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_END) ||
(event->type == GDK_TOUCHPAD_PINCH &&
event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END))
{
if (_gtk_gesture_update_point (gesture, event, FALSE))
{
if (was_recognized &&
@@ -625,30 +775,50 @@ gtk_gesture_handle_event (GtkEventController *controller,
_gtk_gesture_remove_point (gesture, event);
}
break;
case GDK_MOTION_NOTIFY:
if ((event->motion.state & BUTTONS_MASK) == 0)
break;
}
else if (event->type == GDK_MOTION_NOTIFY ||
event->type == GDK_TOUCH_UPDATE ||
(event->type == GDK_TOUCHPAD_SWIPE &&
event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE) ||
(event->type == GDK_TOUCHPAD_PINCH &&
event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE))
{
if (event->type == GDK_MOTION_NOTIFY)
{
if ((event->motion.state & BUTTONS_MASK) == 0)
return FALSE;
if (event->motion.is_hint)
gdk_event_request_motions (&event->motion);
if (event->motion.is_hint)
gdk_event_request_motions (&event->motion);
}
/* Fall through */
case GDK_TOUCH_UPDATE:
if (_gtk_gesture_update_point (gesture, event, FALSE) &&
_gtk_gesture_check_recognized (gesture, sequence))
g_signal_emit (gesture, signals[UPDATE], 0, sequence);
break;
case GDK_TOUCH_CANCEL:
_gtk_gesture_cancel_sequence (gesture, sequence);
break;
case GDK_GRAB_BROKEN:
}
else if (event->type == GDK_TOUCH_CANCEL)
{
if (!priv->touchpad)
_gtk_gesture_cancel_sequence (gesture, sequence);
}
else if ((event->type == GDK_TOUCHPAD_SWIPE &&
event->touchpad_swipe.phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL) ||
(event->type == GDK_TOUCHPAD_PINCH &&
event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL))
{
if (priv->touchpad)
_gtk_gesture_cancel_sequence (gesture, sequence);
}
else if (event->type == GDK_GRAB_BROKEN)
{
if (!event->grab_broken.grab_window ||
!gesture_within_window (gesture, event->grab_broken.grab_window))
_gtk_gesture_cancel_all (gesture);
return FALSE;
default:
}
else
{
/* Unhandled event */
return FALSE;
}
@@ -675,6 +845,7 @@ gtk_gesture_class_init (GtkGestureClass *klass)
object_class->set_property = gtk_gesture_set_property;
object_class->finalize = gtk_gesture_finalize;
controller_class->filter_event = gtk_gesture_filter_event;
controller_class->handle_event = gtk_gesture_handle_event;
controller_class->reset = gtk_gesture_reset;
@@ -827,7 +998,8 @@ gtk_gesture_init (GtkGesture *gesture)
priv->points = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) g_free);
gtk_event_controller_set_event_mask (GTK_EVENT_CONTROLLER (gesture),
GDK_TOUCH_MASK);
GDK_TOUCH_MASK |
GDK_TOUCHPAD_GESTURE_MASK);
priv->group_link = g_list_prepend (NULL, gesture);
}
@@ -1180,6 +1352,12 @@ _gtk_gesture_get_last_update_time (GtkGesture *gesture,
* box containing all active touches. Otherwise, %FALSE will be
* returned.
*
* Note: This function will yield unexpected results on touchpad
* gestures. Since there is no correlation between physical and
* pixel distances, these will look as if constrained in an
* infinitely small area, @rect width and height will thus be 0
* regardless of the number of touchpoints.
*
* Returns: %TRUE if there are active touches, %FALSE otherwise
*
* Since: 3.14
@@ -1283,7 +1461,7 @@ gtk_gesture_is_active (GtkGesture *gesture)
{
g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
return _gtk_gesture_effective_n_points (gesture) != 0;
return _gtk_gesture_get_n_physical_points (gesture, TRUE) != 0;
}
/**

View File

@@ -56,6 +56,26 @@ static guint signals[N_SIGNALS] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureDrag, gtk_gesture_drag, GTK_TYPE_GESTURE_SINGLE)
static gboolean
gtk_gesture_drag_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
/* Let touchpad swipe events go through, only if they match n-points */
if (event->type == GDK_TOUCHPAD_SWIPE)
{
guint n_points;
g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
if (event->touchpad_swipe.n_fingers == n_points)
return FALSE;
else
return TRUE;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_drag_parent_class)->filter_event (controller, event);
}
static void
gtk_gesture_drag_begin (GtkGesture *gesture,
GdkEventSequence *sequence)
@@ -110,6 +130,9 @@ static void
gtk_gesture_drag_class_init (GtkGestureDragClass *klass)
{
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
event_controller_class->filter_event = gtk_gesture_drag_filter_event;
gesture_class->begin = gtk_gesture_drag_begin;
gesture_class->update = gtk_gesture_drag_update;

View File

@@ -45,6 +45,7 @@ enum {
struct _GtkGestureRotatePrivate
{
gdouble initial_angle;
gdouble accum_touchpad_angle;
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -75,33 +76,52 @@ static gboolean
_gtk_gesture_rotate_get_angle (GtkGestureRotate *rotate,
gdouble *angle)
{
GtkGestureRotatePrivate *priv;
const GdkEvent *last_event;
gdouble x1, y1, x2, y2;
GtkGesture *gesture;
gdouble dx, dy;
GList *sequences;
gesture = GTK_GESTURE (rotate);
priv = gtk_gesture_rotate_get_instance_private (rotate);
if (!gtk_gesture_is_recognized (gesture))
return FALSE;
sequences = gtk_gesture_get_sequences (gesture);
g_assert (sequences && sequences->next);
if (!sequences)
return FALSE;
gtk_gesture_get_point (gesture, sequences->data, &x1, &y1);
gtk_gesture_get_point (gesture, sequences->next->data, &x2, &y2);
g_list_free (sequences);
last_event = gtk_gesture_get_last_event (gesture, sequences->data);
dx = x1 - x2;
dy = y1 - y2;
if (last_event->type == GDK_TOUCHPAD_PINCH &&
(last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE ||
last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END))
{
*angle = priv->accum_touchpad_angle;
}
else
{
if (!sequences->next)
return FALSE;
*angle = atan2 (dx, dy);
gtk_gesture_get_point (gesture, sequences->data, &x1, &y1);
gtk_gesture_get_point (gesture, sequences->next->data, &x2, &y2);
g_list_free (sequences);
/* Invert angle */
*angle = (2 * G_PI) - *angle;
dx = x1 - x2;
dy = y1 - y2;
/* And constraint it to 0°-360° */
*angle = fmod (*angle, 2 * G_PI);
*angle = atan2 (dx, dy);
/* Invert angle */
*angle = (2 * G_PI) - *angle;
/* And constraint it to 0°-360° */
*angle = fmod (*angle, 2 * G_PI);
}
return TRUE;
}
@@ -143,14 +163,55 @@ gtk_gesture_rotate_update (GtkGesture *gesture,
_gtk_gesture_rotate_check_emit (GTK_GESTURE_ROTATE (gesture));
}
static gboolean
gtk_gesture_rotate_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
/* Let 2-finger touchpad pinch events go through */
if (event->type == GDK_TOUCHPAD_PINCH)
{
if (event->touchpad_pinch.n_fingers == 2)
return FALSE;
else
return TRUE;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_rotate_parent_class)->filter_event (controller, event);
}
static gboolean
gtk_gesture_rotate_handle_event (GtkEventController *controller,
const GdkEvent *event)
{
GtkGestureRotate *rotate = GTK_GESTURE_ROTATE (controller);
GtkGestureRotatePrivate *priv;
priv = gtk_gesture_rotate_get_instance_private (rotate);
if (event->type == GDK_TOUCHPAD_PINCH)
{
if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END)
priv->accum_touchpad_angle = 0;
else if (event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE)
priv->accum_touchpad_angle += event->touchpad_pinch.angle_delta;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_rotate_parent_class)->handle_event (controller, event);
}
static void
gtk_gesture_rotate_class_init (GtkGestureRotateClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
object_class->constructor = gtk_gesture_rotate_constructor;
event_controller_class->filter_event = gtk_gesture_rotate_filter_event;
event_controller_class->handle_event = gtk_gesture_rotate_handle_event;
gesture_class->begin = gtk_gesture_rotate_begin;
gesture_class->update = gtk_gesture_rotate_update;

View File

@@ -77,6 +77,26 @@ gtk_gesture_swipe_finalize (GObject *object)
G_OBJECT_CLASS (gtk_gesture_swipe_parent_class)->finalize (object);
}
static gboolean
gtk_gesture_swipe_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
/* Let touchpad swipe events go through, only if they match n-points */
if (event->type == GDK_TOUCHPAD_SWIPE)
{
guint n_points;
g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
if (event->touchpad_swipe.n_fingers == n_points)
return FALSE;
else
return TRUE;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_swipe_parent_class)->filter_event (controller, event);
}
static void
_gtk_gesture_swipe_clear_backlog (GtkGestureSwipe *gesture,
guint32 evtime)
@@ -188,10 +208,13 @@ static void
gtk_gesture_swipe_class_init (GtkGestureSwipeClass *klass)
{
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gtk_gesture_swipe_finalize;
event_controller_class->filter_event = gtk_gesture_swipe_filter_event;
gesture_class->update = gtk_gesture_swipe_update;
gesture_class->end = gtk_gesture_swipe_end;

View File

@@ -75,6 +75,7 @@ static gboolean
_gtk_gesture_zoom_get_distance (GtkGestureZoom *zoom,
gdouble *distance)
{
const GdkEvent *last_event;
gdouble x1, y1, x2, y2;
GtkGesture *gesture;
GList *sequences;
@@ -86,15 +87,32 @@ _gtk_gesture_zoom_get_distance (GtkGestureZoom *zoom,
return FALSE;
sequences = gtk_gesture_get_sequences (gesture);
g_assert (sequences && sequences->next);
if (!sequences)
return FALSE;
gtk_gesture_get_point (gesture, sequences->data, &x1, &y1);
gtk_gesture_get_point (gesture, sequences->next->data, &x2, &y2);
g_list_free (sequences);
last_event = gtk_gesture_get_last_event (gesture, sequences->data);
dx = x1 - x2;
dy = y1 - y2;;
*distance = sqrt ((dx * dx) + (dy * dy));
if (last_event->type == GDK_TOUCHPAD_PINCH &&
(last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE ||
last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END))
{
/* Touchpad pinch */
*distance = last_event->touchpad_pinch.scale;
}
else
{
if (!sequences->next)
return FALSE;
gtk_gesture_get_point (gesture, sequences->data, &x1, &y1);
gtk_gesture_get_point (gesture, sequences->next->data, &x2, &y2);
g_list_free (sequences);
dx = x1 - x2;
dy = y1 - y2;;
*distance = sqrt ((dx * dx) + (dy * dy));
}
return TRUE;
}
@@ -119,6 +137,22 @@ _gtk_gesture_zoom_check_emit (GtkGestureZoom *gesture)
return TRUE;
}
static gboolean
gtk_gesture_zoom_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
/* Let 2-finger touchpad pinch events go through */
if (event->type == GDK_TOUCHPAD_PINCH)
{
if (event->touchpad_pinch.n_fingers == 2)
return FALSE;
else
return TRUE;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_zoom_parent_class)->filter_event (controller, event);
}
static void
gtk_gesture_zoom_begin (GtkGesture *gesture,
GdkEventSequence *sequence)
@@ -141,10 +175,13 @@ static void
gtk_gesture_zoom_class_init (GtkGestureZoomClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
object_class->constructor = gtk_gesture_zoom_constructor;
event_controller_class->filter_event = gtk_gesture_zoom_filter_event;
gesture_class->begin = gtk_gesture_zoom_begin;
gesture_class->update = gtk_gesture_zoom_update;

View File

@@ -1411,6 +1411,18 @@ rewrite_event_for_window (GdkEvent *event,
new_window,
&event->touch.x, &event->touch.y);
break;
case GDK_TOUCHPAD_SWIPE:
rewrite_events_translate (event->any.window,
new_window,
&event->touchpad_swipe.x,
&event->touchpad_swipe.y);
break;
case GDK_TOUCHPAD_PINCH:
rewrite_events_translate (event->any.window,
new_window,
&event->touchpad_pinch.x,
&event->touchpad_pinch.y);
break;
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
case GDK_PROXIMITY_IN:
@@ -1460,6 +1472,8 @@ rewrite_event_for_grabs (GdkEvent *event)
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
case GDK_TOUCHPAD_SWIPE:
case GDK_TOUCHPAD_PINCH:
display = gdk_window_get_display (event->any.window);
device = gdk_event_get_device (event);
@@ -1819,6 +1833,8 @@ gtk_main_do_event (GdkEvent *event)
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
case GDK_TOUCHPAD_SWIPE:
case GDK_TOUCHPAD_PINCH:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;

View File

@@ -7717,6 +7717,10 @@ gtk_widget_event_internal (GtkWidget *widget,
switch (event->type)
{
case GDK_TOUCHPAD_SWIPE:
case GDK_TOUCHPAD_PINCH:
return_val |= _gtk_widget_run_controllers (widget, event, GTK_PHASE_BUBBLE);
/* Fall through */
case GDK_EXPOSE:
case GDK_NOTHING:
signal_num = -1;