#include <gtk/gtk.h>
#define GTK_COMPILATION
#include "gdk/gdkeventsprivate.h"
typedef struct {
GtkWidget *widget;
int x;
int 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
show_toplevel (GtkWindow *window)
{
GdkSurface *surface;
gtk_window_present (window);
surface = gtk_native_get_surface (GTK_NATIVE (window));
while (gdk_surface_get_width (surface) <= 1 ||
gdk_surface_get_height (surface) <= 1 )
g_main_context_iteration (NULL, TRUE );
}
static void
show_popover (GtkPopover *popover)
{
GdkSurface *surface;
gtk_popover_popup (popover);
surface = gtk_native_get_surface (GTK_NATIVE (popover));
while (gdk_surface_get_width (surface) <= 1 ||
gdk_surface_get_height (surface) <= 1 )
g_main_context_iteration (NULL, TRUE );
}
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)
{
GdkButtonEvent *button_event;
button_event = (GdkButtonEvent *) g_type_create_instance (GDK_TYPE_BUTTON_EVENT);
ev = (GdkEvent *) button_event;
ev->event_type = GDK_BUTTON_PRESS;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
button_event->button = button;
button_event->state = point->state;
button_event->x = point->x;
button_event->y = point->y;
point->state |= GDK_BUTTON1_MASK << (button - 1 );
}
else
{
GdkTouchEvent *touch_event;
touch_event = (GdkTouchEvent *) g_type_create_instance (GDK_TYPE_TOUCH_EVENT);
ev = (GdkEvent *) touch_event;
ev->event_type = GDK_TOUCH_BEGIN;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
touch_event->sequence = EVENT_SEQUENCE (point);
touch_event->state = point->state;
touch_event->x = point->x;
touch_event->y = point->y;
touch_event->touch_emulating = (point == &touch_state[0 ]);
}
inject_event (ev);
gdk_event_unref (ev);
point->widget = widget;
}
static void
point_update (PointState *point,
GtkWidget *widget,
double x,
double 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)
{
GdkMotionEvent *motion_event;
motion_event = (GdkMotionEvent *) g_type_create_instance (GDK_TYPE_MOTION_EVENT);
ev = (GdkEvent *) motion_event;
ev->event_type = GDK_MOTION_NOTIFY;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
motion_event->state = point->state;
motion_event->x = point->x;
motion_event->y = point->y;
}
else
{
GdkTouchEvent *touch_event;
if (!point->widget || widget != point->widget)
return ;
touch_event = (GdkTouchEvent *) g_type_create_instance (GDK_TYPE_TOUCH_EVENT);
ev = (GdkEvent *) touch_event;
ev->event_type = GDK_TOUCH_UPDATE;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
touch_event->sequence = EVENT_SEQUENCE (point);
touch_event->state = point->state;
touch_event->x = point->x;
touch_event->y = point->y;
touch_event->touch_emulating = (point == &touch_state[0 ]);
}
inject_event (ev);
gdk_event_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)
{
GdkButtonEvent *button_event;
if ((point->state & (GDK_BUTTON1_MASK << (button - 1 ))) == 0 )
return ;
button_event = (GdkButtonEvent *) g_type_create_instance (GDK_TYPE_BUTTON_EVENT);
ev = (GdkEvent *) button_event;
ev->event_type = GDK_BUTTON_RELEASE;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
button_event->button = button;
button_event->state = point->state;
button_event->x = point->x;
button_event->y = point->y;
point->state &= ~(GDK_BUTTON1_MASK << (button - 1 ));
}
else
{
GdkTouchEvent *touch_event;
touch_event = (GdkTouchEvent *) g_type_create_instance (GDK_TYPE_TOUCH_EVENT);
ev = (GdkEvent *) touch_event;
ev->event_type = GDK_TOUCH_END;
ev->surface = g_object_ref (surface);
ev->device = g_object_ref (device);
ev->time = GDK_CURRENT_TIME;
touch_event->sequence = EVENT_SEQUENCE (point);
touch_event->state = point->state;
touch_event->x = point->x;
touch_event->y = point->y;
touch_event->touch_emulating = (point == &touch_state[0 ]);
}
inject_event (ev);
gdk_event_unref (ev);
}
static const char *
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 char *
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, int n_press, double x, double y, gpointer data)
{
GtkEventController *c = GTK_EVENT_CONTROLLER (g);
GdkEventSequence *sequence;
GtkPropagationPhase phase;
GestureData *gd = data;
const char *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 char *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 char *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 char *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 char *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 char *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 char *name, GtkPropagationPhase phase, GString *str, GtkEventSequenceState state)
{
GtkGesture *g;
GestureData *data;
GList *gestures;
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);
gestures = g_object_steal_data (G_OBJECT (w), "gestures" );
gestures = g_list_prepend (gestures, g);
g_object_set_data_full (G_OBJECT (w), "gestures" , (gpointer) gestures,
(GDestroyNotify) g_list_free);
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 char *name, GtkPropagationPhase phase, GString *str, GtkEventSequenceState state)
{
GtkGesture *g;
GestureData *data;
GList *gestures;
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);
gestures = g_object_get_data (G_OBJECT (w), "gestures" );
gestures = g_list_prepend (gestures, g);
g_object_set_data_full (G_OBJECT (w), "gestures" , (gpointer) gestures,
(GDestroyNotify) g_list_free);
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
assert_gesture_state (GtkWidget *w, const char *name, GdkEventSequence *seq, GtkEventSequenceState state)
{
GList *gestures, *l;
gestures = g_object_get_data (G_OBJECT (w), "gestures" );
for (l = gestures; l; l = l->next)
{
const char *n;
n = g_object_get_data (l->data, "name" );
if (g_strcmp0 (n, name) != 0 )
continue ;
g_assert_cmpint (gtk_gesture_get_sequence_state (l->data, seq), ==, state);
return ;
}
g_assert_not_reached ();
}
static void
test_phases (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_mixed (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_early_exit (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_claim_capture (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"b1 cancelled, "
"a1 cancelled, "
"c1 state claimed" );
assert_gesture_state (A, "a1" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (B, "b1" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (C, "c1" , NULL, GTK_EVENT_SEQUENCE_CLAIMED);
assert_gesture_state (C, "c2" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (C, "c3" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (B, "b3" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (A, "a3" , NULL, GTK_EVENT_SEQUENCE_NONE);
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_claim_target (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c1 state denied, "
"b1 state denied, "
"a1 state denied, "
"c2 state claimed" );
assert_gesture_state (A, "a1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (B, "b1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (C, "c1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (C, "c2" , NULL, GTK_EVENT_SEQUENCE_CLAIMED);
assert_gesture_state (C, "c3" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (B, "b3" , NULL, GTK_EVENT_SEQUENCE_NONE);
assert_gesture_state (A, "a3" , NULL, GTK_EVENT_SEQUENCE_NONE);
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_claim_bubble (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"bubble c3, "
"bubble b3, "
"b1 state denied, "
"c3 state denied, "
"c2 state denied, "
"c1 state denied, "
"a1 state denied, "
"b3 state claimed" );
assert_gesture_state (A, "a1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (B, "b1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (C, "c1" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (C, "c2" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (C, "c3" , NULL, GTK_EVENT_SEQUENCE_DENIED);
assert_gesture_state (B, "b3" , NULL, GTK_EVENT_SEQUENCE_CLAIMED);
assert_gesture_state (A, "a3" , NULL, GTK_EVENT_SEQUENCE_NONE);
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_early_claim_capture (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GtkGesture *g;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"a1 cancelled, "
"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 a1, "
"capture c1, "
"b1 cancelled, "
"a1 cancelled, "
"c1 state claimed, "
"b1 state denied" );
point_release (&mouse_state, 1 );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_late_claim_capture (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GtkGesture *g;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c1 state denied, "
"b1 state denied, "
"a1 state denied, "
"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, ==, "" );
point_release (&mouse_state, 1 );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
static void
test_group (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
GtkGesture *g1, *g2;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c3, "
"c1 state denied, "
"b1 state denied, "
"a1 state denied, "
"c3 state claimed, "
"c2 state claimed, "
"target c2" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
G_GNUC_UNUSED static void
test_gestures_outside_grab (void )
{
GtkWidget *A, *B, *C, *D, *E;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (A));
D = gtk_popover_new ();
gtk_widget_set_name (D, "D" );
gtk_widget_set_parent (D, C);
E = gtk_image_new ();
gtk_widget_set_name (E, "E" );
gtk_popover_set_child (GTK_POPOVER (D), E);
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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c1 state denied, "
"c2 state claimed" );
/* Set a grab on another window */
g_string_erase (str, 0 , str->len);
show_popover (GTK_POPOVER (D));
g_assert_cmpstr (str->str, ==,
"c1 cancelled, "
"c2 cancelled, "
"b1 cancelled, "
"a1 cancelled" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
G_GNUC_UNUSED static void
test_gestures_inside_grab (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
gtk_widget_set_name (A, "A" );
B = gtk_popover_new ();
gtk_widget_set_name (B, "B" );
gtk_widget_set_parent (B, A);
C = gtk_image_new ();
gtk_widget_set_hexpand (C, TRUE );
gtk_widget_set_vexpand (C, TRUE );
gtk_widget_set_name (C, "C" );
gtk_popover_set_child (GTK_POPOVER (B), C);
gtk_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
show_toplevel (GTK_WINDOW (A));
show_popover (GTK_POPOVER (B));
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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
point_update (&mouse_state, A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&mouse_state, A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1, "
"capture b1, "
"capture c1, "
"target c2, "
"c2 state claimed" );
g_string_erase (str, 0 , str->len);
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.origin.x, allocation.origin.y);
g_assert_cmpstr (str->str, ==,
"b1 updated, "
"c1 updated, "
"c2 updated" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
G_GNUC_UNUSED static void
test_multitouch_on_single (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
/* First touch down */
point_update (&touch_state[0 ], A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&touch_state[0 ], A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1 (1), "
"capture b1 (1), "
"a1 cancelled, "
"b1 state claimed (1)" );
/* Second touch down */
g_string_erase (str, 0 , str->len);
point_update (&touch_state[1 ], A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&touch_state[1 ], A, 1 );
g_assert_cmpstr (str->str, ==,
"capture a1 (2), "
"b1 state denied (2)" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
G_GNUC_UNUSED static void
test_multitouch_activation (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (A));
str = g_string_new ("" );
add_mt_gesture (C, "c1" , GTK_PHASE_BUBBLE, str, GTK_EVENT_SEQUENCE_CLAIMED);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
/* First touch down */
point_update (&touch_state[0 ], A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
point_press (&touch_state[0 ], A, 1 );
g_assert_cmpstr (str->str, ==, "" );
/* Second touch down */
point_update (&touch_state[1 ], A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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, ==, "" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
G_GNUC_UNUSED static void
test_multitouch_interaction (void )
{
GtkWidget *A, *B, *C;
GdkDisplay *display;
GtkGesture *g;
GString *str;
graphene_rect_t allocation;
A = gtk_window_new ();
display = gtk_widget_get_display (A);
if (gdk_display_get_default_seat (display) == NULL)
{
g_test_skip ("Display has no seat" );
return ;
}
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_window_set_default_size (GTK_WINDOW (A), 400 , 400 );
gtk_window_set_child (GTK_WINDOW (A), B);
gtk_box_append (GTK_BOX (B), C);
show_toplevel (GTK_WINDOW (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);
g_assert_true (gtk_widget_compute_bounds (B, B, &allocation));
/* First touch down, a1 claims the sequence */
point_update (&touch_state[0 ], A,
allocation.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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.origin.x, allocation.origin.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.origin.x + allocation.size.width / 2 ,
allocation.origin.y + allocation.size.height / 2 );
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, ==, "" );
gtk_window_destroy (GTK_WINDOW (A));
g_string_free (str, TRUE );
}
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);
#if 0
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);
#endif
return g_test_run ();
}
Messung V0.5 in Prozent C=100 H=91 G=95
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland