gtk2/testsuite/gtk/gestures.c
Emmanuele Bassi f28aa1ba02 Restructure the GdkEvent type hierarchy
GdkEvent has been a "I-can't-believe-this-is-not-OOP" type for ages,
using a union of sub-types. This has always been problematic when it
comes to implementing accessor functions: either you get generic API
that takes a GdkEvent and uses a massive switch() to determine which
event types have the data you're looking for; or you create namespaced
accessors, but break language bindings horribly, as boxed types cannot
have derived types.

The recent conversion of GskRenderNode (which had similar issues) to
GTypeInstance, and the fact that GdkEvent is now a completely opaque
type, provide us with the chance of moving GdkEvent to GTypeInstance,
and have sub-types for GdkEvent.

The change from boxed type to GTypeInstance is pretty small, all things
considered, but ends up cascading to a larger commit, as we still have
backends and code in GTK trying to access GdkEvent structures directly.
Additionally, the naming of the public getter functions requires
renaming all the data structures to conform to the namespace/type-name
pattern.
2020-04-16 19:54:02 +01:00

1253 lines
36 KiB
C

#include <gtk/gtk.h>
#define GTK_COMPILATION
#include "gdk/gdkeventsprivate.h"
typedef struct {
GtkWidget *widget;
gint x;
gint y;
guint state;
guint pressed : 1;
} PointState;
static PointState mouse_state;
static PointState touch_state[10]; /* touchpoint 0 gets pointer emulation,
* use it first in tests for consistency.
*/
#define EVENT_SEQUENCE(point) (GdkEventSequence*) ((point) - touch_state + 1)
static void
inject_event (GdkEvent *event)
{
gboolean handled;
g_signal_emit_by_name (event->surface, "event", event, &handled);
}
static void
point_press (PointState *point,
GtkWidget *widget,
guint button)
{
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
GdkSurface *surface;
GdkEvent *ev;
display = gtk_widget_get_display (widget);
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
surface = gtk_native_get_surface (gtk_widget_get_native (widget));
if (point == &mouse_state)
{
ev = gdk_button_event_new (GDK_BUTTON_PRESS,
surface,
device,
device,
NULL,
GDK_CURRENT_TIME,
point->x,
point->y,
button,
point->state);
point->state |= GDK_BUTTON1_MASK << (button - 1);
}
else
{
ev = gdk_touch_event_new (GDK_TOUCH_BEGIN,
EVENT_SEQUENCE (point),
surface,
device,
device,
GDK_CURRENT_TIME,
point->state,
point->x,
point->y,
point == &touch_state[0]);
}
inject_event (ev);
g_object_unref (ev);
point->widget = widget;
}
static void
point_update (PointState *point,
GtkWidget *widget,
gdouble x,
gdouble y)
{
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
GdkSurface *surface;
GdkEvent *ev;
display = gtk_widget_get_display (widget);
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
surface = gtk_native_get_surface (gtk_widget_get_native (widget));
point->x = x;
point->y = y;
if (point == &mouse_state)
{
ev = gdk_motion_event_new (surface,
device,
device,
NULL,
GDK_CURRENT_TIME,
point->state,
point->x,
point->y);
}
else
{
if (!point->widget || widget != point->widget)
return;
ev = gdk_touch_event_new (GDK_TOUCH_UPDATE,
EVENT_SEQUENCE (point),
surface,
device,
device,
GDK_CURRENT_TIME,
point->state,
point->x,
point->y,
point == &touch_state[0]);
}
inject_event (ev);
g_object_unref (ev);
}
static void
point_release (PointState *point,
guint button)
{
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
GdkSurface *surface;
GdkEvent *ev;
if (point->widget == NULL)
return;
display = gtk_widget_get_display (point->widget);
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
surface = gtk_native_get_surface (gtk_widget_get_native (point->widget));
if (!point->widget)
return;
if (point == &mouse_state)
{
if ((point->state & (GDK_BUTTON1_MASK << (button - 1))) == 0)
return;
ev = gdk_button_event_new (GDK_BUTTON_RELEASE,
surface,
device,
device,
NULL,
GDK_CURRENT_TIME,
point->x,
point->y,
button,
point->state);
point->state &= ~(GDK_BUTTON1_MASK << (button - 1));
}
else
{
ev = gdk_touch_event_new (GDK_TOUCH_END,
EVENT_SEQUENCE (point),
surface,
device,
device,
GDK_CURRENT_TIME,
point->state,
point->x,
point->y,
point == &touch_state[0]);
}
inject_event (ev);
g_object_unref (ev);
}
static const gchar *
phase_nick (GtkPropagationPhase phase)
{
GTypeClass *class;
GEnumValue *value;
class = g_type_class_ref (GTK_TYPE_PROPAGATION_PHASE);
value = g_enum_get_value ((GEnumClass*)class, phase);
g_type_class_unref (class);
return value->value_nick;
}
static const gchar *
state_nick (GtkEventSequenceState state)
{
GTypeClass *class;
GEnumValue *value;
class = g_type_class_ref (GTK_TYPE_EVENT_SEQUENCE_STATE);
value = g_enum_get_value ((GEnumClass*)class, state);
g_type_class_unref (class);
return value->value_nick;
}
typedef struct {
GtkEventController *controller;
GString *str;
gboolean exit;
} LegacyData;
static gboolean
legacy_cb (GtkEventControllerLegacy *c, GdkEvent *button, gpointer data)
{
if (gdk_event_get_event_type (button) == GDK_BUTTON_PRESS)
{
LegacyData *ld = data;
GtkWidget *w;
w = gtk_event_controller_get_widget (ld->controller);
if (ld->str->len > 0)
g_string_append (ld->str, ", ");
g_string_append_printf (ld->str, "legacy %s", gtk_widget_get_name (w));
return ld->exit;
}
return GDK_EVENT_PROPAGATE;
}
typedef struct {
GString *str;
GtkEventSequenceState state;
} GestureData;
static void
press_cb (GtkGesture *g, gint n_press, gdouble x, gdouble y, gpointer data)
{
GtkEventController *c = GTK_EVENT_CONTROLLER (g);
GdkEventSequence *sequence;
GtkPropagationPhase phase;
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
phase = gtk_event_controller_get_propagation_phase (c);
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s %s", phase_nick (phase), name);
sequence = gtk_gesture_get_last_updated_sequence (g);
if (sequence)
g_string_append_printf (gd->str, " (%x)", GPOINTER_TO_UINT (sequence));
if (gd->state != GTK_EVENT_SEQUENCE_NONE)
gtk_gesture_set_state (g, gd->state);
}
static void
cancel_cb (GtkGesture *g, GdkEventSequence *sequence, gpointer data)
{
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s cancelled", name);
}
static void
begin_cb (GtkGesture *g, GdkEventSequence *sequence, gpointer data)
{
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s began", name);
if (gd->state != GTK_EVENT_SEQUENCE_NONE)
gtk_gesture_set_state (g, gd->state);
}
static void
end_cb (GtkGesture *g, GdkEventSequence *sequence, gpointer data)
{
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s ended", name);
}
static void
update_cb (GtkGesture *g, GdkEventSequence *sequence, gpointer data)
{
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s updated", name);
}
static void
state_changed_cb (GtkGesture *g, GdkEventSequence *sequence, GtkEventSequenceState state, gpointer data)
{
GestureData *gd = data;
const gchar *name;
name = g_object_get_data (G_OBJECT (g), "name");
if (gd->str->len > 0)
g_string_append (gd->str, ", ");
g_string_append_printf (gd->str, "%s state %s", name, state_nick (state));
if (sequence != NULL)
g_string_append_printf (gd->str, " (%x)", GPOINTER_TO_UINT (sequence));
}
static GtkGesture *
add_gesture (GtkWidget *w, const gchar *name, GtkPropagationPhase phase, GString *str, GtkEventSequenceState state)
{
GtkGesture *g;
GestureData *data;
data = g_new (GestureData, 1);
data->str = str;
data->state = state;
g = gtk_gesture_click_new ();
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (g), FALSE);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (g), 1);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (g), phase);
gtk_widget_add_controller (w, GTK_EVENT_CONTROLLER (g));
g_object_set_data (G_OBJECT (g), "name", (gpointer)name);
g_signal_connect (g, "pressed", G_CALLBACK (press_cb), data);
g_signal_connect (g, "cancel", G_CALLBACK (cancel_cb), data);
g_signal_connect (g, "update", G_CALLBACK (update_cb), data);
g_signal_connect (g, "sequence-state-changed", G_CALLBACK (state_changed_cb), data);
return g;
}
static GtkGesture *
add_mt_gesture (GtkWidget *w, const gchar *name, GtkPropagationPhase phase, GString *str, GtkEventSequenceState state)
{
GtkGesture *g;
GestureData *data;
data = g_new (GestureData, 1);
data->str = str;
data->state = state;
g = gtk_gesture_rotate_new ();
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (g), phase);
gtk_widget_add_controller (w, GTK_EVENT_CONTROLLER (g));
g_object_set_data (G_OBJECT (g), "name", (gpointer)name);
g_signal_connect (g, "begin", G_CALLBACK (begin_cb), data);
g_signal_connect (g, "update", G_CALLBACK (update_cb), data);
g_signal_connect (g, "end", G_CALLBACK (end_cb), data);
g_signal_connect (g, "sequence-state-changed", G_CALLBACK (state_changed_cb), data);
return g;
}
static void
add_legacy (GtkWidget *w, GString *str, gboolean exit)
{
LegacyData *data;
data = g_new (LegacyData, 1);
data->controller = gtk_event_controller_legacy_new ();
data->str = str;
data->exit = exit;
gtk_event_controller_set_propagation_phase (data->controller, GTK_PHASE_BUBBLE);
gtk_widget_add_controller (w, data->controller);
g_signal_connect (data->controller, "event", G_CALLBACK (legacy_cb), data);
}
static void
test_phases (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"bubble c3, "
"bubble b3, "
"bubble a3");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_mixed (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_legacy (A, str, GDK_EVENT_PROPAGATE);
add_legacy (B, str, GDK_EVENT_PROPAGATE);
add_legacy (C, str, GDK_EVENT_PROPAGATE);
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"bubble c3, "
"legacy C, "
"bubble b3, "
"legacy B, "
"bubble a3, "
"legacy A");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_early_exit (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_legacy (A, str, GDK_EVENT_PROPAGATE);
add_legacy (B, str, GDK_EVENT_STOP);
add_legacy (C, str, GDK_EVENT_PROPAGATE);
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"bubble c3, "
"legacy C, "
"bubble b3, "
"legacy B");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_claim_capture (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"c1 state claimed");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_claim_target (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c2 state claimed");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_claim_bubble (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"bubble c3, "
"bubble b3, "
"c3 cancelled, "
"c2 cancelled, "
"c1 cancelled, "
"b3 state claimed"
);
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_early_claim_capture (void)
{
GtkWidget *A, *B, *C;
GtkGesture *g;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
g = add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"b1 state claimed");
/* Reset the string */
g_string_erase (str, 0, str->len);
gtk_gesture_set_state (g, GTK_EVENT_SEQUENCE_DENIED);
g_assert_cmpstr (str->str, ==,
"capture c1, "
"c1 state claimed, "
"b1 state denied");
point_release (&mouse_state, 1);
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_late_claim_capture (void)
{
GtkWidget *A, *B, *C;
GtkGesture *g;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
g = add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c2 state claimed");
/* Reset the string */
g_string_erase (str, 0, str->len);
gtk_gesture_set_state (g, GTK_EVENT_SEQUENCE_CLAIMED);
g_assert_cmpstr (str->str, ==,
"c2 cancelled, "
"c1 cancelled, "
"b1 state claimed");
point_release (&mouse_state, 1);
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_group (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkGesture *g1, *g2;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
g1 = add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_NONE);
g2 = add_gesture (C, "c3", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_CLAIMED);
gtk_gesture_group (g1, g2);
add_gesture (A, "a3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b3", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c4", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c3, "
"c3 state claimed, "
"c2 state claimed, "
"target c2");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_gestures_outside_grab (void)
{
GtkWidget *A, *B, *C, *D;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
D = gtk_window_new ();
gtk_widget_show (D);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (B, "b2", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a2", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c2 state claimed");
/* Set a grab on another window */
g_string_erase (str, 0, str->len);
gtk_grab_add (D);
g_assert_cmpstr (str->str, ==,
"c1 cancelled, "
"c2 cancelled, "
"b1 cancelled, "
"a1 cancelled");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
gtk_widget_destroy (D);
}
static void
test_gestures_inside_grab (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (C, "c2", GTK_PHASE_TARGET, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_gesture (B, "b2", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (A, "a2", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_NONE);
gtk_widget_get_allocation (B, &allocation);
point_update (&mouse_state, A, allocation.x, allocation.y);
point_press (&mouse_state, A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c2 state claimed");
/* Set a grab on B */
g_string_erase (str, 0, str->len);
gtk_grab_add (B);
g_assert_cmpstr (str->str, ==,
"a1 cancelled");
/* Update with the grab under effect */
g_string_erase (str, 0, str->len);
point_update (&mouse_state, A, allocation.x, allocation.y);
g_assert_cmpstr (str->str, ==,
"b1 updated, "
"c1 updated, "
"c2 updated");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_multitouch_on_single (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_NONE);
add_gesture (B, "b1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_CLAIMED);
gtk_widget_get_allocation (B, &allocation);
/* First touch down */
point_update (&touch_state[0], A, allocation.x, allocation.y);
point_press (&touch_state[0], A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1 (1), "
"capture b1 (1), "
"b1 state claimed (1)");
/* Second touch down */
g_string_erase (str, 0, str->len);
point_update (&touch_state[1], A, allocation.x, allocation.y);
point_press (&touch_state[1], A, 1);
g_assert_cmpstr (str->str, ==,
"a1 state denied (2), "
"b1 state denied (2)");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_multitouch_activation (void)
{
GtkWidget *A, *B, *C;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
add_mt_gesture (C, "c1", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_CLAIMED);
gtk_widget_get_allocation (B, &allocation);
/* First touch down */
point_update (&touch_state[0], A, allocation.x, allocation.y);
point_press (&touch_state[0], A, 1);
g_assert_cmpstr (str->str, ==, "");
/* Second touch down */
point_update (&touch_state[1], A, allocation.x, allocation.y);
point_press (&touch_state[1], A, 1);
g_assert_cmpstr (str->str, ==,
"c1 began, "
"c1 state claimed (2), "
"c1 state claimed (1)");
/* First touch up */
g_string_erase (str, 0, str->len);
point_release (&touch_state[0], 1);
g_assert_cmpstr (str->str, ==,
"c1 ended");
/* A third touch down triggering again action */
g_string_erase (str, 0, str->len);
point_update (&touch_state[2], A, allocation.x, allocation.y);
point_press (&touch_state[2], A, 1);
g_assert_cmpstr (str->str, ==,
"c1 began, "
"c1 state claimed (3)");
/* One touch up, gesture is finished again */
g_string_erase (str, 0, str->len);
point_release (&touch_state[2], 1);
g_assert_cmpstr (str->str, ==,
"c1 ended");
/* Another touch up, gesture remains inactive */
g_string_erase (str, 0, str->len);
point_release (&touch_state[1], 1);
g_assert_cmpstr (str->str, ==, "");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
static void
test_multitouch_interaction (void)
{
GtkWidget *A, *B, *C;
GtkGesture *g;
GString *str;
GtkAllocation allocation;
A = gtk_window_new ();
gtk_widget_set_name (A, "A");
B = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name (B, "B");
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE);
gtk_widget_set_vexpand (C, TRUE);
gtk_widget_set_name (C, "C");
gtk_container_add (GTK_CONTAINER (A), B);
gtk_container_add (GTK_CONTAINER (B), C);
gtk_widget_show (A);
str = g_string_new ("");
g = add_gesture (A, "a1", GTK_PHASE_CAPTURE, str, GTK_EVENT_SEQUENCE_CLAIMED);
add_mt_gesture (C, "c1", GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_CLAIMED);
gtk_widget_get_allocation (B, &allocation);
/* First touch down, a1 claims the sequence */
point_update (&touch_state[0], A, allocation.x, allocation.y);
point_press (&touch_state[0], A, 1);
g_assert_cmpstr (str->str, ==,
"capture a1 (1), "
"a1 state claimed (1)");
/* Second touch down, a1 denies and c1 takes over */
g_string_erase (str, 0, str->len);
point_update (&touch_state[1], A, allocation.x, allocation.y);
point_press (&touch_state[1], A, 1);
/* Denying sequences in touch-excess situation is a responsibility of the caller */
gtk_gesture_set_state (g, GTK_EVENT_SEQUENCE_DENIED);
g_assert_cmpstr (str->str, ==,
"a1 state denied (2), "
"c1 began, "
"c1 state claimed (1), "
"c1 state claimed (2), "
"a1 state denied (1)");
/* Move first point, only c1 should update */
g_string_erase (str, 0, str->len);
point_update (&touch_state[0], A, allocation.x, allocation.y);
g_assert_cmpstr (str->str, ==,
"c1 updated");
/* First touch up */
g_string_erase (str, 0, str->len);
point_release (&touch_state[0], 1);
g_assert_cmpstr (str->str, ==,
"c1 ended");
/* A third touch down triggering again action on c1 */
g_string_erase (str, 0, str->len);
point_update (&touch_state[2], A, allocation.x, allocation.y);
point_press (&touch_state[2], A, 1);
g_assert_cmpstr (str->str, ==,
"a1 state denied (3), "
"c1 began, "
"c1 state claimed (3)");
/* One touch up, gesture is finished again */
g_string_erase (str, 0, str->len);
point_release (&touch_state[2], 1);
g_assert_cmpstr (str->str, ==,
"c1 ended");
/* Another touch up, gesture remains inactive */
g_string_erase (str, 0, str->len);
point_release (&touch_state[1], 1);
g_assert_cmpstr (str->str, ==, "");
g_string_free (str, TRUE);
gtk_widget_destroy (A);
}
int
main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv);
g_test_add_func ("/gestures/propagation/phases", test_phases);
g_test_add_func ("/gestures/propagation/mixed", test_mixed);
g_test_add_func ("/gestures/propagation/early-exit", test_early_exit);
g_test_add_func ("/gestures/claim/capture", test_claim_capture);
g_test_add_func ("/gestures/claim/target", test_claim_target);
g_test_add_func ("/gestures/claim/bubble", test_claim_bubble);
g_test_add_func ("/gestures/claim/early-capture", test_early_claim_capture);
g_test_add_func ("/gestures/claim/late-capture", test_late_claim_capture);
g_test_add_func ("/gestures/group", test_group);
g_test_add_func ("/gestures/grabs/gestures-outside-grab", test_gestures_outside_grab);
g_test_add_func ("/gestures/grabs/gestures-inside-grab", test_gestures_inside_grab);
g_test_add_func ("/gestures/multitouch/gesture-single", test_multitouch_on_single);
g_test_add_func ("/gestures/multitouch/multitouch-activation", test_multitouch_activation);
g_test_add_func ("/gestures/multitouch/interaction", test_multitouch_interaction);
return g_test_run ();
}