[gtk+/wip/events: 9/11] gtk: Add event trackers to event handling code
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/events: 9/11] gtk: Add event trackers to event handling code
- Date: Fri, 9 Mar 2012 03:45:13 +0000 (UTC)
commit 54b4c038d3d43a9a3d1883797a11242f209ba61a
Author: Benjamin Otte <otte redhat com>
Date: Tue Mar 6 23:35:54 2012 +0100
gtk: Add event trackers to event handling code
Event trackers run before any widget event handling functions. Event
recognizers run per-widget before any widget event handlers.
gtk/Makefile.am | 2 +
gtk/gtkmain.c | 16 +++
gtk/gtksequencetracker.c | 259 +++++++++++++++++++++++++++++++++++++++
gtk/gtksequencetrackerprivate.h | 52 ++++++++
gtk/gtkwidget.c | 27 ++++
5 files changed, 356 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index feb92e5..b3a33fc 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -487,6 +487,7 @@ gtk_private_h_sources = \
gtksearchengine.h \
gtksearchenginesimple.h \
gtkselectionprivate.h \
+ gtksequencetrackerprivate.h \
gtksettingsprivate.h \
gtkshadowprivate.h \
gtksizegroup-private.h \
@@ -747,6 +748,7 @@ gtk_base_c_sources = \
gtkseparator.c \
gtkseparatormenuitem.c \
gtkseparatortoolitem.c \
+ gtksequencetracker.c \
gtksettings.c \
gtksizegroup.c \
gtksizerequest.c \
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 65df934..847c4d4 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -114,6 +114,7 @@
#include "gtkclipboard.h"
#include "gtkdebug.h"
#include "gtkdnd.h"
+#include "gtkeventtrackerprivate.h"
#include "gtkmain.h"
#include "gtkmenu.h"
#include "gtkmodules.h"
@@ -1647,6 +1648,9 @@ gtk_main_do_event (GdkEvent *event)
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
+ if (_gtk_event_trackers_invoke (event))
+ break;
+
if (!_gtk_widget_captured_event (event_widget, event))
gtk_widget_event (event_widget, event);
break;
@@ -1656,6 +1660,9 @@ gtk_main_do_event (GdkEvent *event)
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_TOUCH_BEGIN:
+ if (_gtk_event_trackers_invoke (event))
+ break;
+
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
@@ -1709,11 +1716,17 @@ gtk_main_do_event (GdkEvent *event)
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
+ if (_gtk_event_trackers_invoke (event))
+ break;
+
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
case GDK_ENTER_NOTIFY:
+ if (_gtk_event_trackers_invoke (event))
+ break;
+
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
_gtk_widget_set_device_window (event_widget,
@@ -1725,6 +1738,9 @@ gtk_main_do_event (GdkEvent *event)
break;
case GDK_LEAVE_NOTIFY:
+ if (_gtk_event_trackers_invoke (event))
+ break;
+
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
_gtk_widget_set_device_window (event_widget,
diff --git a/gtk/gtksequencetracker.c b/gtk/gtksequencetracker.c
new file mode 100644
index 0000000..bfc5f64
--- /dev/null
+++ b/gtk/gtksequencetracker.c
@@ -0,0 +1,259 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Benjamin Otte <otte gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtksequencetrackerprivate.h"
+
+#include <math.h>
+
+#define GTK_SEQUENCE_TRACKER_HISTORY_SIZE 8
+
+typedef struct _GtkSequenceTrackerClass GtkSequenceTrackerClass;
+struct _GtkSequenceTrackerClass {
+ gboolean (* update) (GtkSequenceTracker *tracker,
+ GdkEvent *event,
+ double *x,
+ double *y);
+};
+
+struct _GtkSequenceTracker {
+ GtkSequenceTrackerClass *klass;
+
+ struct {
+ guint time;
+ GtkMovementDirection dir;
+ double x; /* x position in pixels - will be compared to start_x */
+ double y; /* y position in pixels - will be compared to start_y */
+ } history[GTK_SEQUENCE_TRACKER_HISTORY_SIZE];
+ guint history_index; /* current item */
+
+ GdkEventSequence *sequence; /* sequence we're tracking */
+ double start_x; /* NOT screen location, but in device coordinates */
+ double start_y; /* NOT screen location, but in device coordinates */
+};
+
+/* MOUSE */
+
+/* FIXME */
+
+/* TOUCHSCREEN */
+
+/* FIXME */
+
+/* TOUCHPAD */
+
+static gboolean
+gtk_sequence_tracker_touchpad_update (GtkSequenceTracker *tracker,
+ GdkEvent *event,
+ double *x,
+ double *y)
+{
+ GdkDevice *device = gdk_event_get_device (event);
+
+ switch (event->type)
+ {
+ case GDK_TOUCH_BEGIN:
+ tracker->sequence = gdk_event_get_event_sequence (event);
+ /* fall through */
+ case GDK_TOUCH_UPDATE:
+ case GDK_TOUCH_END:
+ if (tracker->sequence != gdk_event_get_event_sequence (event))
+ return FALSE;
+
+ if (gdk_device_get_axis (device,
+ event->touch.axes,
+ GDK_AXIS_X_RELATIVE,
+ x))
+ {
+ *x *= gdk_screen_get_width (gdk_display_get_default_screen (gdk_device_get_display (device)));
+ }
+ else
+ {
+ g_warning ("Could not query X value");
+ *x = 0;
+ }
+
+ if (gdk_device_get_axis (device,
+ event->touch.axes,
+ GDK_AXIS_Y_RELATIVE,
+ y))
+ {
+ *y *= gdk_screen_get_height (gdk_display_get_default_screen (gdk_device_get_display (device)));
+ }
+ else
+ {
+ g_warning ("Could not query Y value");
+ *y = 0;
+ }
+
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+GtkSequenceTrackerClass GTK_SEQUENCE_TRACKER_CLASS_TOUCHPAD = {
+ gtk_sequence_tracker_touchpad_update
+};
+
+/* API */
+
+GtkSequenceTracker *
+_gtk_sequence_tracker_new (GdkEvent *event)
+{
+ GtkSequenceTrackerClass *klass;
+ GtkSequenceTracker *tracker;
+ guint i, time;
+
+ switch (event->type)
+ {
+ case GDK_TOUCH_BEGIN:
+ if (gdk_device_get_source (gdk_event_get_source_device (event)) == GDK_SOURCE_TOUCHPAD)
+ klass = >K_SEQUENCE_TRACKER_CLASS_TOUCHPAD;
+ else
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+
+ tracker = g_slice_new0 (GtkSequenceTracker);
+ tracker->klass = klass;
+
+ if (!tracker->klass->update (tracker, event, &tracker->start_x, &tracker->start_y))
+ {
+ g_assert_not_reached ();
+ return NULL;
+ }
+ time = gdk_event_get_time (event);
+
+ for (i = 0; i < GTK_SEQUENCE_TRACKER_HISTORY_SIZE; i++)
+ {
+ tracker->history[i].time = time;
+ tracker->history[i].x = tracker->start_x;
+ tracker->history[i].y = tracker->start_y;
+ tracker->history[i].dir = GTK_DIR_ANY;
+ }
+ tracker->history_index = 0;
+
+ return tracker;
+}
+
+void
+_gtk_sequence_tracker_free (GtkSequenceTracker *tracker)
+{
+ g_return_if_fail (tracker != NULL);
+
+ g_slice_free (GtkSequenceTracker, tracker);
+}
+
+static GtkMovementDirection
+gtk_sequence_tracker_compute_direction (double dx,
+ double dy)
+{
+ double r;
+ int i1, i2;
+
+ if (fabs (dx) < 2 && fabs (dy) < 2)
+ {
+ GtkMovementDirection dir = GTK_DIR_ANY;
+
+ if (dx <= 1)
+ dir &= GTK_DIR_SOUTH | GTK_DIR_SOUTH_WEST | GTK_DIR_WEST | GTK_DIR_NORTH_WEST | GTK_DIR_NORTH;
+ else if (dx >= 1)
+ dir &= GTK_DIR_SOUTH | GTK_DIR_SOUTH_EAST | GTK_DIR_EAST | GTK_DIR_NORTH_EAST | GTK_DIR_NORTH;
+
+ if (dy <= 1)
+ dir &= GTK_DIR_WEST | GTK_DIR_NORTH_WEST | GTK_DIR_NORTH | GTK_DIR_NORTH_EAST | GTK_DIR_EAST;
+ else
+ dir &= GTK_DIR_WEST | GTK_DIR_SOUTH_WEST | GTK_DIR_SOUTH | GTK_DIR_SOUTH_EAST | GTK_DIR_EAST;
+
+ return dir;
+ }
+
+ r = atan2(dy, dx);
+
+ /* Add 360Â to avoid r become negative since C has no well-defined
+ * modulo for such cases. */
+ r += 2 * G_PI;
+
+ /* Divide by 45Â to get the octant number, e.g.
+ * 0 <= r <= 1 is [0-45]Â
+ * 1 <= r <= 2 is [45-90]Â
+ * etc. */
+ r /= G_PI / 4;
+
+ /* This intends to flag 2 directions (45 degrees),
+ * except on very well-aligned coordinates. */
+ i1 = (int) (r + 0.1) % 8;
+ i2 = (int) (r + 0.9) % 8;
+ if (i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
+ return GTK_DIR_ANY;
+
+ return (1 << i1 | 1 << i2);
+}
+
+gboolean
+_gtk_sequence_tracker_update (GtkSequenceTracker *tracker,
+ GdkEvent *event)
+{
+ double x, y, dx, dy;
+
+ g_return_val_if_fail (tracker != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (!tracker->klass->update (tracker, event, &x, &y))
+ return FALSE;
+
+ dx = x - tracker->history[tracker->history_index].x;
+ dy = y - tracker->history[tracker->history_index].y;
+
+ tracker->history_index = (tracker->history_index + 1) % GTK_SEQUENCE_TRACKER_HISTORY_SIZE;
+ tracker->history[tracker->history_index].time = gdk_event_get_time (event);
+ tracker->history[tracker->history_index].x = x;
+ tracker->history[tracker->history_index].y = y;
+ tracker->history[tracker->history_index].dir = gtk_sequence_tracker_compute_direction (dx, dy);
+
+ return TRUE;
+}
+
+double
+_gtk_sequence_tracker_get_x_offset (GtkSequenceTracker *tracker)
+{
+ g_return_val_if_fail (tracker != NULL, 0);
+
+ return tracker->history[tracker->history_index].x
+ - tracker->start_x;
+}
+
+double
+_gtk_sequence_tracker_get_y_offset (GtkSequenceTracker *tracker)
+{
+ g_return_val_if_fail (tracker != NULL, 0);
+
+ return tracker->history[tracker->history_index].y
+ - tracker->start_y;
+}
+
+GtkMovementDirection
+_gtk_sequence_tracker_get_direction (GtkSequenceTracker *tracker)
+{
+ g_return_val_if_fail (tracker != NULL, GTK_DIR_ANY);
+
+ return tracker->history[tracker->history_index].dir;
+}
diff --git a/gtk/gtksequencetrackerprivate.h b/gtk/gtksequencetrackerprivate.h
new file mode 100644
index 0000000..5ad632f
--- /dev/null
+++ b/gtk/gtksequencetrackerprivate.h
@@ -0,0 +1,52 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Benjamin Otte <otte gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SEQUENCE_TRACKER_PRIVATE_H__
+#define __GTK_SEQUENCE_TRACKER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GTK_DIR_EAST = (1 << 0),
+ GTK_DIR_SOUTH_EAST = (1 << 1),
+ GTK_DIR_SOUTH = (1 << 2),
+ GTK_DIR_SOUTH_WEST = (1 << 3),
+ GTK_DIR_WEST = (1 << 4),
+ GTK_DIR_NORTH_WEST = (1 << 5),
+ GTK_DIR_NORTH = (1 << 6),
+ GTK_DIR_NORTH_EAST = (1 << 7),
+ GTK_DIR_ANY = (1 << 8) - 1,
+} GtkMovementDirection;
+
+typedef struct _GtkSequenceTracker GtkSequenceTracker;
+
+GtkSequenceTracker * _gtk_sequence_tracker_new (GdkEvent *event);
+void _gtk_sequence_tracker_free (GtkSequenceTracker *tracker);
+
+gboolean _gtk_sequence_tracker_update (GtkSequenceTracker *tracker,
+ GdkEvent *event);
+
+double _gtk_sequence_tracker_get_x_offset (GtkSequenceTracker *tracker);
+double _gtk_sequence_tracker_get_y_offset (GtkSequenceTracker *tracker);
+
+GtkMovementDirection _gtk_sequence_tracker_get_direction (GtkSequenceTracker *tracker);
+
+G_END_DECLS
+
+#endif /* __GTK_SEQUENCE_TRACKER_PRIVATE_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index a19137c..d89da21 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -6282,6 +6282,31 @@ event_window_is_still_viewable (GdkEvent *event)
}
}
+static gboolean
+gtk_widget_invoke_recognizers (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS (widget);
+ GtkWidgetClassPrivate *priv = klass->priv;
+ guint i;
+ gboolean eat_event = FALSE;
+
+ if (priv->recognizers == NULL)
+ return FALSE;
+
+ g_object_ref (widget);
+
+ for (i = 0; i < priv->recognizers->len; i++)
+ {
+ GtkEventRecognizer *recognizer = g_ptr_array_index (priv->recognizers, i);
+ _gtk_event_recognizer_recognize (recognizer, widget, event);
+ }
+
+ g_object_unref (widget);
+
+ return eat_event;
+}
+
static gint
gtk_widget_event_internal (GtkWidget *widget,
GdkEvent *event)
@@ -6296,6 +6321,8 @@ gtk_widget_event_internal (GtkWidget *widget,
if (!event_window_is_still_viewable (event))
return TRUE;
+ gtk_widget_invoke_recognizers (widget, event);
+
g_object_ref (widget);
g_signal_emit (widget, widget_signals[EVENT], 0, event, &return_val);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]