summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2018-07-18 00:23:37 +0000
committerMatthias Clasen <mclasen@redhat.com>2018-07-18 00:23:37 +0000
commitcc73241c7b681f91587b5606d04688356b1b8a29 (patch)
tree89fdd264b27414ee986ef01d658b2aa3da0a2daf
parentac358d250795ab7ba0586f0a408a38bba4b0c048 (diff)
parent9ee60777e7e1e0e16641746f741dc68d0412e6b1 (diff)
downloadgtk+-cc73241c7b681f91587b5606d04688356b1b8a29.tar.gz
Merge branch 'wip/carlosg/controllers-backport' into 'gtk-3-24'
Wip/carlosg/controllers backport See merge request GNOME/gtk!246
-rw-r--r--demos/gtk-demo/Makefile.am1
-rw-r--r--demos/gtk-demo/demo.gresource.xml1
-rw-r--r--demos/gtk-demo/paint.c255
-rw-r--r--docs/reference/gtk/gtk3.types.in1
-rw-r--r--gtk/Makefile.am9
-rw-r--r--gtk/gtk.h4
-rw-r--r--gtk/gtkeventcontrollerkey.c271
-rw-r--r--gtk/gtkeventcontrollerkey.h63
-rw-r--r--gtk/gtkeventcontrollermotion.c169
-rw-r--r--gtk/gtkeventcontrollermotion.h50
-rw-r--r--gtk/gtkeventcontrollerscroll.c517
-rw-r--r--gtk/gtkeventcontrollerscroll.h80
-rw-r--r--gtk/gtkgesturestylus.c269
-rw-r--r--gtk/gtkgesturestylus.h59
-rw-r--r--gtk/gtkgesturestylusprivate.h51
-rw-r--r--po/POTFILES.in4
16 files changed, 1804 insertions, 0 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index 261d555ae5..64245b4b6e 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -48,6 +48,7 @@ demos_base = \
offscreen_window2.c \
overlay.c \
overlay2.c \
+ paint.c \
panes.c \
pickers.c \
pixbufs.c \
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index a02c4843f9..3fac7342eb 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -177,6 +177,7 @@
<file>offscreen_window2.c</file>
<file>overlay.c</file>
<file>overlay2.c</file>
+ <file>paint.c</file>
<file>pagesetup.c</file>
<file>panes.c</file>
<file>pickers.c</file>
diff --git a/demos/gtk-demo/paint.c b/demos/gtk-demo/paint.c
new file mode 100644
index 0000000000..55232b0565
--- /dev/null
+++ b/demos/gtk-demo/paint.c
@@ -0,0 +1,255 @@
+/* Paint
+ *
+ * Demonstrates practical handling of drawing tablets in a real world
+ * usecase.
+ */
+#include <gtk/gtk.h>
+
+typedef struct
+{
+ GtkEventBox parent_instance;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ GdkRGBA draw_color;
+
+ GtkGesture *stylus_gesture;
+} DrawingArea;
+
+typedef struct
+{
+ GtkEventBoxClass parent_class;
+} DrawingAreaClass;
+
+G_DEFINE_TYPE (DrawingArea, drawing_area, GTK_TYPE_EVENT_BOX)
+
+static void
+drawing_area_ensure_surface (DrawingArea *area,
+ gint width,
+ gint height)
+{
+ if (!area->surface ||
+ cairo_image_surface_get_width (area->surface) != width ||
+ cairo_image_surface_get_height (area->surface) != height)
+ {
+ cairo_surface_t *surface;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ width, height);
+ if (area->surface)
+ {
+ cairo_t *cr;
+
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr, area->surface, 0, 0);
+ cairo_paint (cr);
+
+ cairo_surface_destroy (area->surface);
+ cairo_destroy (area->cr);
+ cairo_destroy (cr);
+ }
+
+ area->surface = surface;
+ area->cr = cairo_create (surface);
+ }
+}
+
+static void
+drawing_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ DrawingArea *area = (DrawingArea *) widget;
+
+ drawing_area_ensure_surface (area, allocation->width, allocation->height);
+
+ GTK_WIDGET_CLASS (drawing_area_parent_class)->size_allocate (widget, allocation);
+}
+
+static void
+drawing_area_map (GtkWidget *widget)
+{
+ GtkAllocation allocation;
+
+ GTK_WIDGET_CLASS (drawing_area_parent_class)->map (widget);
+
+ gdk_window_set_event_compression (gtk_widget_get_window (widget), TRUE);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ drawing_area_ensure_surface ((DrawingArea *) widget,
+ allocation.width, allocation.height);
+}
+
+static void
+drawing_area_unmap (GtkWidget *widget)
+{
+ DrawingArea *area = (DrawingArea *) widget;
+
+ g_clear_pointer (&area->cr, cairo_destroy);
+ g_clear_pointer (&area->surface, cairo_surface_destroy);
+
+ GTK_WIDGET_CLASS (drawing_area_parent_class)->unmap (widget);
+}
+
+static gboolean
+drawing_area_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ DrawingArea *area = (DrawingArea *) widget;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ cairo_set_source_surface (cr, area->surface, 0, 0);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_stroke (cr);
+
+ return TRUE;
+}
+
+static void
+drawing_area_class_init (DrawingAreaClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->size_allocate = drawing_area_size_allocate;
+ widget_class->draw = drawing_area_draw;
+ widget_class->map = drawing_area_map;
+ widget_class->unmap = drawing_area_unmap;
+}
+
+static void
+drawing_area_apply_stroke (DrawingArea *area,
+ GdkDeviceTool *tool,
+ gdouble x,
+ gdouble y,
+ gdouble pressure)
+{
+ if (gdk_device_tool_get_tool_type (tool) == GDK_DEVICE_TOOL_TYPE_ERASER)
+ {
+ cairo_set_line_width (area->cr, 10 * pressure);
+ cairo_set_operator (area->cr, CAIRO_OPERATOR_DEST_OUT);
+ }
+ else
+ {
+ cairo_set_line_width (area->cr, 4 * pressure);
+ cairo_set_operator (area->cr, CAIRO_OPERATOR_SATURATE);
+ }
+
+ cairo_set_source_rgba (area->cr, area->draw_color.red,
+ area->draw_color.green, area->draw_color.blue,
+ area->draw_color.alpha * pressure);
+
+ //cairo_set_source_rgba (area->cr, 0, 0, 0, pressure);
+
+ cairo_line_to (area->cr, x, y);
+ cairo_stroke (area->cr);
+ cairo_move_to (area->cr, x, y);
+}
+
+static void
+stylus_gesture_down (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y,
+ DrawingArea *area)
+{
+ cairo_new_path (area->cr);
+}
+
+static void
+stylus_gesture_motion (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y,
+ DrawingArea *area)
+{
+ GdkDeviceTool *tool;
+ gdouble pressure;
+
+ tool = gtk_gesture_stylus_get_device_tool (gesture);
+
+ if (!gtk_gesture_stylus_get_axis (gesture, GDK_AXIS_PRESSURE, &pressure))
+ pressure = 1;
+
+ drawing_area_apply_stroke (area, tool, x, y, pressure);
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+}
+
+static void
+drawing_area_init (DrawingArea *area)
+{
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (area), TRUE);
+
+ area->stylus_gesture = gtk_gesture_stylus_new (GTK_WIDGET (area));
+ g_signal_connect (area->stylus_gesture, "down",
+ G_CALLBACK (stylus_gesture_down), area);
+ g_signal_connect (area->stylus_gesture, "motion",
+ G_CALLBACK (stylus_gesture_motion), area);
+
+ area->draw_color = (GdkRGBA) { 0, 0, 0, 1 };
+}
+
+GtkWidget *
+drawing_area_new (void)
+{
+ return g_object_new (drawing_area_get_type (), NULL);
+}
+
+void
+drawing_area_set_color (DrawingArea *area,
+ GdkRGBA *color)
+{
+ area->draw_color = *color;
+}
+
+static void
+color_button_color_set (GtkColorButton *button,
+ DrawingArea *draw_area)
+{
+ GdkRGBA color;
+
+ gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &color);
+ drawing_area_set_color (draw_area, &color);
+}
+
+GtkWidget *
+do_paint (GtkWidget *toplevel)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *draw_area, *headerbar, *colorbutton;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ draw_area = drawing_area_new ();
+ gtk_container_add (GTK_CONTAINER (window), draw_area);
+
+ headerbar = gtk_header_bar_new ();
+ gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), "Paint");
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
+
+ colorbutton = gtk_color_button_new ();
+ g_signal_connect (colorbutton, "color-set",
+ G_CALLBACK (color_button_color_set), draw_area);
+ gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (colorbutton),
+ &(GdkRGBA) { 0, 0, 0, 1 });
+
+ gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), colorbutton);
+ gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed), &window);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show_all (window);
+ else
+ gtk_widget_destroy (window);
+
+ return window;
+}
diff --git a/docs/reference/gtk/gtk3.types.in b/docs/reference/gtk/gtk3.types.in
index 9f7d51234e..45b28bc20a 100644
--- a/docs/reference/gtk/gtk3.types.in
+++ b/docs/reference/gtk/gtk3.types.in
@@ -65,6 +65,7 @@ gtk_entry_completion_get_type
gtk_entry_get_type
gtk_event_box_get_type
gtk_event_controller_get_type
+gtk_event_controller_scroll_get_type
gtk_expander_get_type
gtk_file_chooser_button_get_type
gtk_file_chooser_dialog_get_type
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 56b41a1c90..1fdee6f720 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -192,6 +192,9 @@ gtk_public_h_sources = \
gtkenums.h \
gtkeventbox.h \
gtkeventcontroller.h \
+ gtkeventcontrollerkey.h \
+ gtkeventcontrollermotion.h \
+ gtkeventcontrollerscroll.h \
gtkexpander.h \
gtkfilechooser.h \
gtkfilechooserbutton.h \
@@ -213,6 +216,7 @@ gtk_public_h_sources = \
gtkgesturepan.h \
gtkgesturerotate.h \
gtkgesturesingle.h \
+ gtkgesturestylus.h \
gtkgestureswipe.h \
gtkgesturezoom.h \
gtkglarea.h \
@@ -491,6 +495,7 @@ gtk_private_h_sources = \
gtkgesturepanprivate.h \
gtkgesturerotateprivate.h \
gtkgesturesingleprivate.h \
+ gtkgesturestylusprivate.h \
gtkgestureswipeprivate.h \
gtkgesturezoomprivate.h \
gtkheaderbarprivate.h \
@@ -757,6 +762,9 @@ gtk_base_c_sources = \
gtkentrycompletion.c \
gtkeventbox.c \
gtkeventcontroller.c \
+ gtkeventcontrollerkey.c \
+ gtkeventcontrollermotion.c \
+ gtkeventcontrollerscroll.c \
gtkexpander.c \
gtkfilechooser.c \
gtkfilechooserbutton.c \
@@ -786,6 +794,7 @@ gtk_base_c_sources = \
gtkgesturepan.c \
gtkgesturerotate.c \
gtkgesturesingle.c \
+ gtkgesturestylus.c \
gtkgestureswipe.c \
gtkgesturezoom.c \
gtkglarea.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 7b901e1ece..c08454ca1c 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -94,6 +94,9 @@
#include <gtk/gtkenums.h>
#include <gtk/gtkeventbox.h>
#include <gtk/gtkeventcontroller.h>
+#include <gtk/gtkeventcontrollerkey.h>
+#include <gtk/gtkeventcontrollermotion.h>
+#include <gtk/gtkeventcontrollerscroll.h>
#include <gtk/gtkexpander.h>
#include <gtk/gtkfixed.h>
#include <gtk/gtkfilechooser.h>
@@ -115,6 +118,7 @@
#include <gtk/gtkgesturepan.h>
#include <gtk/gtkgesturerotate.h>
#include <gtk/gtkgesturesingle.h>
+#include <gtk/gtkgesturestylus.h>
#include <gtk/gtkgestureswipe.h>
#include <gtk/gtkgesturezoom.h>
#include <gtk/gtkglarea.h>
diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c
new file mode 100644
index 0000000000..70c34bd4e6
--- /dev/null
+++ b/gtk/gtkeventcontrollerkey.c
@@ -0,0 +1,271 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtkwidgetprivate.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkeventcontrollerkey.h"
+#include "gtkbindings.h"
+
+#include <gdk/gdk.h>
+
+struct _GtkEventControllerKey
+{
+ GtkEventController parent_instance;
+ GtkIMContext *im_context;
+ GHashTable *pressed_keys;
+
+ const GdkEvent *current_event;
+};
+
+struct _GtkEventControllerKeyClass
+{
+ GtkEventControllerClass parent_class;
+};
+
+enum {
+ KEY_PRESSED,
+ KEY_RELEASED,
+ MODIFIERS,
+ IM_UPDATE,
+ FOCUS_IN,
+ FOCUS_OUT,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key,
+ GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+gtk_event_controller_finalize (GObject *object)
+{
+ GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object);
+
+ g_hash_table_destroy (key->pressed_keys);
+ g_clear_object (&key->im_context);
+
+ G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
+}
+
+static gboolean
+gtk_event_controller_key_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
+ GdkEventType event_type = gdk_event_get_event_type (event);
+ gboolean handled;
+ GdkModifierType state;
+ guint16 keycode;
+ guint keyval;
+
+ if (event_type == GDK_FOCUS_CHANGE)
+ {
+ if (event->focus_change.in)
+ g_signal_emit (controller, signals[FOCUS_IN], 0);
+ else
+ g_signal_emit (controller, signals[FOCUS_OUT], 0);
+
+ return FALSE;
+ }
+
+ if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE)
+ return FALSE;
+
+ if (key->im_context &&
+ gtk_im_context_filter_keypress (key->im_context, (GdkEventKey *) event))
+ {
+ g_signal_emit (controller, signals[IM_UPDATE], 0);
+ return TRUE;
+ }
+
+ if (!gdk_event_get_state (event, &state) || !event->key.is_modifier)
+ return FALSE;
+
+ key->current_event = event;
+
+ if (event->key.is_modifier)
+ {
+ if (event_type == GDK_KEY_PRESS)
+ g_signal_emit (controller, signals[MODIFIERS], 0, state, &handled);
+ else
+ handled = TRUE;
+
+ if (handled == TRUE)
+ {
+ key->current_event = NULL;
+ return TRUE;
+ }
+ }
+
+ gdk_event_get_keycode (event, &keycode);
+ gdk_event_get_keyval (event, &keyval);
+
+ if (event_type == GDK_KEY_PRESS)
+ {
+ g_signal_emit (controller, signals[KEY_PRESSED], 0,
+ keyval, keycode, state, &handled);
+ if (handled)
+ g_hash_table_add (key->pressed_keys, GUINT_TO_POINTER (keyval));
+ }
+ else if (event_type == GDK_KEY_RELEASE)
+ {
+ g_signal_emit (controller, signals[KEY_RELEASED], 0,
+ keyval, keycode, state);
+
+ handled = g_hash_table_lookup (key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL;
+ g_hash_table_remove (key->pressed_keys, GUINT_TO_POINTER (keyval));
+ }
+ else
+ handled = FALSE;
+
+ key->current_event = NULL;
+
+ return handled;
+}
+
+static void
+gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
+{
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_event_controller_finalize;
+ controller_class->handle_event = gtk_event_controller_key_handle_event;
+
+ signals[KEY_PRESSED] =
+ g_signal_new (I_("key-pressed"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, _gtk_boolean_handled_accumulator, NULL, NULL,
+ G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+ signals[KEY_RELEASED] =
+ g_signal_new (I_("key-released"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+ signals[MODIFIERS] =
+ g_signal_new (I_("modifiers"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_BOOLEAN__FLAGS,
+ G_TYPE_BOOLEAN, 1, GDK_TYPE_MODIFIER_TYPE);
+ signals[IM_UPDATE] =
+ g_signal_new (I_("im-update"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[FOCUS_IN] =
+ g_signal_new (I_("focus-in"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[FOCUS_OUT] =
+ g_signal_new (I_("focus-out"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gtk_event_controller_key_init (GtkEventControllerKey *controller)
+{
+ controller->pressed_keys = g_hash_table_new (NULL, NULL);
+}
+
+GtkEventController *
+gtk_event_controller_key_new (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY,
+ "widget", widget,
+ NULL);
+}
+
+void
+gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
+ GtkIMContext *im_context)
+{
+ g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller));
+ g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context));
+
+ if (controller->im_context)
+ gtk_im_context_reset (controller->im_context);
+
+ g_set_object (&controller->im_context, im_context);
+}
+
+/**
+ * gtk_event_controller_key_get_im_context:
+ * @controller: a #GtkEventControllerKey
+ *
+ * Gets the IM context of a key controller.
+ *
+ * Returns: (transfer none): the IM context
+ *
+ * Since: 3.94
+ **/
+GtkIMContext *
+gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
+
+ return controller->im_context;
+}
+
+gboolean
+gtk_event_controller_key_forward (GtkEventControllerKey *controller,
+ GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (controller->current_event != NULL, FALSE);
+
+ if (!gtk_widget_get_realized (widget))
+ gtk_widget_realize (widget);
+
+ if (_gtk_widget_captured_event (widget, (GdkEvent *) controller->current_event))
+ return TRUE;
+ if (gtk_widget_event (widget, (GdkEvent *) controller->current_event))
+ return TRUE;
+
+ return FALSE;
+}
+
+guint
+gtk_event_controller_key_get_group (GtkEventControllerKey *controller)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
+ g_return_val_if_fail (controller->current_event != NULL, FALSE);
+
+ return controller->current_event->key.group;
+}
diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h
new file mode 100644
index 0000000000..a3756c4ac0
--- /dev/null
+++ b/gtk/gtkeventcontrollerkey.h
@@ -0,0 +1,63 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#ifndef __GTK_EVENT_CONTROLLER_KEY_H__
+#define __GTK_EVENT_CONTROLLER_KEY_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtkeventcontroller.h>
+#include <gtk/gtkimcontext.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EVENT_CONTROLLER_KEY (gtk_event_controller_key_get_type ())
+#define GTK_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKey))
+#define GTK_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
+#define GTK_IS_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_KEY))
+#define GTK_IS_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_KEY))
+#define GTK_EVENT_CONTROLLER_KEY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
+
+typedef struct _GtkEventControllerKey GtkEventControllerKey;
+typedef struct _GtkEventControllerKeyClass GtkEventControllerKeyClass;
+
+GDK_AVAILABLE_IN_3_24
+GType gtk_event_controller_key_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_24
+GtkEventController *gtk_event_controller_key_new (GtkWidget *widget);
+
+GDK_AVAILABLE_IN_3_24
+void gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
+ GtkIMContext *im_context);
+GDK_AVAILABLE_IN_3_24
+GtkIMContext * gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller);
+
+GDK_AVAILABLE_IN_3_24
+gboolean gtk_event_controller_key_forward (GtkEventControllerKey *controller,
+ GtkWidget *widget);
+GDK_AVAILABLE_IN_3_24
+guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller);
+
+G_END_DECLS
+
+#endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */
diff --git a/gtk/gtkeventcontrollermotion.c b/gtk/gtkeventcontrollermotion.c
new file mode 100644
index 0000000000..75c293e205
--- /dev/null
+++ b/gtk/gtkeventcontrollermotion.c
@@ -0,0 +1,169 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Matthias Clasen <mclasen@redhat.com>
+ */
+
+/**
+ * SECTION:gtkeventcontrollermotion
+ * @Short_description: Event controller for motion events
+ * @Title: GtkEventControllerMotion
+ * @See_also: #GtkEventController
+ *
+ * #GtkEventControllerMotion is an event controller meant for situations
+ * where you need to track the position of the pointer.
+ *
+ * This object was added in 3.94.
+ **/
+#include "config.h"
+
+#include "gtkintl.h"
+#include "gtkwidget.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkeventcontrollermotion.h"
+#include "gtktypebuiltins.h"
+#include "gtkmarshalers.h"
+
+struct _GtkEventControllerMotion
+{
+ GtkEventController parent_instance;
+};
+
+struct _GtkEventControllerMotionClass
+{
+ GtkEventControllerClass parent_class;
+};
+
+enum {
+ ENTER,
+ LEAVE,
+ MOTION,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER)
+
+static gboolean
+gtk_event_controller_motion_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GtkEventControllerClass *parent_class;
+ GdkEventType type;
+
+ type = gdk_event_get_event_type (event);
+ if (type == GDK_ENTER_NOTIFY)
+ {
+ double x, y;
+ gdk_event_get_coords (event, &x, &y);
+ g_signal_emit (controller, signals[ENTER], 0, x, y);
+ }
+ else if (type == GDK_LEAVE_NOTIFY)
+ {
+ g_signal_emit (controller, signals[LEAVE], 0);
+ }
+ else if (type == GDK_MOTION_NOTIFY)
+ {
+ double x, y;
+ gdk_event_get_coords (event, &x, &y);
+ g_signal_emit (controller, signals[MOTION], 0, x, y);
+ }
+
+ parent_class = GTK_EVENT_CONTROLLER_CLASS (gtk_event_controller_motion_parent_class);
+
+ return parent_class->handle_event (controller, event);
+}
+
+static void
+gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
+{
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+
+ controller_class->handle_event = gtk_event_controller_motion_handle_event;
+
+ /**
+ * GtkEventControllerMotion::enter:
+ * @controller: The object that received the signal
+ * @x: the x coordinate
+ * @y: the y coordinate
+ *
+ * Signals that the pointer has entered the widget.
+ */
+ signals[ENTER] =
+ g_signal_new (I_("enter"),
+ GTK_TYPE_EVENT_CONTROLLER_MOTION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ /**
+ * GtkEventControllerMotion::leave:
+ * @controller: The object that received the signal
+ *
+ * Signals that pointer has left the widget.
+ */
+ signals[LEAVE] =
+ g_signal_new (I_("leave"),
+ GTK_TYPE_EVENT_CONTROLLER_MOTION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GtkEventControllerMotion::motion:
+ * @controller: The object that received the signal
+ * @x: the x coordinate
+ * @y: the y coordinate
+ *
+ * Emitted when the pointer moves inside the widget.
+ */
+ signals[MOTION] =
+ g_signal_new (I_("motion"),
+ GTK_TYPE_EVENT_CONTROLLER_MOTION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+}
+
+static void
+gtk_event_controller_motion_init (GtkEventControllerMotion *motion)
+{
+}
+
+/**
+ * gtk_event_controller_motion_new:
+ * @widget: a #GtkWidget
+ *
+ * Creates a new event controller that will handle motion events
+ * for the given @widget.
+ *
+ * Returns: a new #GtkEventControllerMotion
+ *
+ * Since: 3.94
+ **/
+GtkEventController *
+gtk_event_controller_motion_new (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return g_object_new (GTK_TYPE_EVENT_CONTROLLER_MOTION,
+ "widget", widget,
+ NULL);
+}
diff --git a/gtk/gtkeventcontrollermotion.h b/gtk/gtkeventcontrollermotion.h
new file mode 100644
index 0000000000..42b6aac508
--- /dev/null
+++ b/gtk/gtkeventcontrollermotion.h
@@ -0,0 +1,50 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Matthias Clasen <mclasen@redhat.com>
+ */
+
+#ifndef __GTK_EVENT_CONTROLLER_MOTION_H__
+#define __GTK_EVENT_CONTROLLER_MOTION_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtkeventcontroller.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EVENT_CONTROLLER_MOTION (gtk_event_controller_motion_get_type ())
+#define GTK_EVENT_CONTROLLER_MOTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotion))
+#define GTK_EVENT_CONTROLLER_MOTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotionClass))
+#define GTK_IS_EVENT_CONTROLLER_MOTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION))
+#define GTK_IS_EVENT_CONTROLLER_MOTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_MOTION))
+#define GTK_EVENT_CONTROLLER_MOTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotionClass))
+
+typedef struct _GtkEventControllerMotion GtkEventControllerMotion;
+typedef struct _GtkEventControllerMotionClass GtkEventControllerMotionClass;
+
+GDK_AVAILABLE_IN_3_24
+GType gtk_event_controller_motion_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_24
+GtkEventController *gtk_event_controller_motion_new (GtkWidget *widget);
+
+G_END_DECLS
+
+#endif /* __GTK_EVENT_CONTROLLER_MOTION_H__ */
diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c
new file mode 100644
index 0000000000..db98d7c51a
--- /dev/null
+++ b/gtk/gtkeventcontrollerscroll.c
@@ -0,0 +1,517 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+/**
+ * SECTION:gtkeventcontrollerscroll
+ * @Short_description: Event controller for scroll events
+ * @Title: GtkEventControllerScroll
+ * @See_also: #GtkEventController
+ *
+ * #GtkEventControllerScroll is an event controller meant to handle
+ * scroll events from mice and touchpads. It is capable of handling
+ * both discrete and continuous scroll events, abstracting them both
+ * on the #GtkEventControllerScroll::scroll signal (deltas in the
+ * discrete case are multiples of 1).
+ *
+ * In the case of continuous scroll events, #GtkEventControllerScroll
+ * encloses all #GtkEventControllerScroll::scroll events between two
+ * #GtkEventControllerScroll::scroll-begin and #GtkEventControllerScroll::scroll-end
+ * signals.
+ *
+ * The behavior of the event controller can be modified by the
+ * flags given at creation time, or modified at a later point through
+ * gtk_event_controller_scroll_set_flags() (e.g. because the scrolling
+ * conditions of the widget changed).
+ *
+ * The controller can be set up to emit motion for either/both vertical
+ * and horizontal scroll events through #GTK_EVENT_CONTROLLER_SCROLL_VERTICAL,
+ * #GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL and #GTK_EVENT_CONTROLLER_SCROLL_BOTH.
+ * If any axis is disabled, the respective #GtkEventControllerScroll::scroll
+ * delta will be 0. Vertical scroll events will be translated to horizontal
+ * motion for the devices incapable of horizontal scrolling.
+ *
+ * The event controller can also be forced to emit discrete events on all devices
+ * through #GTK_EVENT_CONTROLLER_SCROLL_DISCRETE. This can be used to implement
+ * discrete actions triggered through scroll events (e.g. switching across
+ * combobox options).
+ *
+ * The #GTK_EVENT_CONTROLLER_SCROLL_KINETIC flag toggles the emission of the
+ * #GtkEventControllerScroll::decelerate signal, emitted at the end of scrolling
+ * with two X/Y velocity arguments that are consistent with the motion that
+ * was received.
+ *
+ * This object was added in 3.93.
+ **/
+#include "config.h"
+
+#include "math.h"
+
+#include "gtkintl.h"
+#include "gtkwidget.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkeventcontrollerscroll.h"
+#include "gtktypebuiltins.h"
+#include "gtkmarshalers.h"
+#include "gtkprivate.h"
+
+#define SCROLL_CAPTURE_THRESHOLD_MS 150
+
+typedef struct
+{
+ gdouble dx;
+ gdouble dy;
+ guint32 evtime;
+} ScrollHistoryElem;
+
+struct _GtkEventControllerScroll
+{
+ GtkEventController parent_instance;
+ GtkEventControllerScrollFlags flags;
+ GArray *scroll_history;
+
+ /* For discrete event coalescing */
+ gdouble cur_dx;
+ gdouble cur_dy;
+
+ guint active : 1;
+};
+
+struct _GtkEventControllerScrollClass
+{
+ GtkEventControllerClass parent_class;
+};
+
+enum {
+ SCROLL_BEGIN,
+ SCROLL,
+ SCROLL_END,
+ DECELERATE,
+ N_SIGNALS
+};
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ N_PROPS
+};
+
+static GParamSpec *pspecs[N_PROPS] = { NULL };
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (GtkEventControllerScroll, gtk_event_controller_scroll,
+ GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+scroll_history_push (GtkEventControllerScroll *scroll,
+ gdouble delta_x,
+ gdouble delta_y,
+ guint32 evtime)
+{
+ ScrollHistoryElem new_item;
+ guint i;
+
+ for (i = 0; i < scroll->scroll_history->len; i++)
+ {
+ ScrollHistoryElem *elem;
+
+ elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i);
+
+ if (elem->evtime >= evtime - SCROLL_CAPTURE_THRESHOLD_MS)
+ break;
+ }
+
+ if (i > 0)
+ g_array_remove_range (scroll->scroll_history, 0, i);
+
+ new_item.dx = delta_x;
+ new_item.dy = delta_y;
+ new_item.evtime = evtime;
+ g_array_append_val (scroll->scroll_history, new_item);
+}
+
+static void
+scroll_history_reset (GtkEventControllerScroll *scroll)
+{
+ if (scroll->scroll_history->len == 0)
+ return;
+
+ g_array_remove_range (scroll->scroll_history, 0,
+ scroll->scroll_history->len);
+}
+
+static void
+scroll_history_finish (GtkEventControllerScroll *scroll,
+ gdouble *velocity_x,
+ gdouble *velocity_y)
+{
+ gdouble accum_dx = 0, accum_dy = 0;
+ guint32 first = 0, last = 0;
+ guint i;
+
+ *velocity_x = 0;
+ *velocity_y = 0;
+
+ if (scroll->scroll_history->len == 0)
+ return;
+
+ for (i = 0; i < scroll->scroll_history->len; i++)
+ {
+ ScrollHistoryElem *elem;
+
+ elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i);
+ accum_dx += elem->dx;
+ accum_dy += elem->dy;
+ last = elem->evtime;
+
+ if (i == 0)
+ first = elem->evtime;
+ }
+
+ if (last != first)
+ {
+ *velocity_x = (accum_dx * 1000) / (last - first);
+ *velocity_y = (accum_dy * 1000) / (last - first);
+ }
+
+ scroll_history_reset (scroll);
+}
+
+static void
+gtk_event_controller_scroll_finalize (GObject *object)
+{
+ GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object);
+
+ g_array_unref (scroll->scroll_history);
+
+ G_OBJECT_CLASS (gtk_event_controller_scroll_parent_class)->finalize (object);
+}
+
+static void
+gtk_event_controller_scroll_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ gtk_event_controller_scroll_set_flags (scroll, g_value_get_flags (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_event_controller_scroll_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ g_value_set_flags (value, scroll->flags);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gtk_event_controller_scroll_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller);
+ GdkScrollDirection direction = GDK_SCROLL_SMOOTH;
+ gdouble dx = 0, dy = 0;
+
+ if (gdk_event_get_event_type (event) != GDK_SCROLL)
+ return FALSE;
+ if ((scroll->flags & (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL |
+ GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL)) == 0)
+ return FALSE;
+
+ /* FIXME: Handle device changes */
+
+ if (gdk_event_get_scroll_deltas (event, &dx, &dy))
+ {
+ GdkDevice *device = gdk_event_get_source_device (event);
+ GdkInputSource input_source = gdk_device_get_source (device);
+
+ if (!scroll->active &&
+ (input_source == GDK_SOURCE_TRACKPOINT ||
+ input_source == GDK_SOURCE_TOUCHPAD))
+ {
+ g_signal_emit (controller, signals[SCROLL_BEGIN], 0);
+ scroll_history_reset (scroll);
+ scroll->active = TRUE;
+ }
+
+ if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0)
+ dy = 0;
+ if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0)
+ dx = 0;
+
+ if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_DISCRETE)
+ {
+ gint steps;
+
+ scroll->cur_dx += dx;
+ scroll->cur_dy += dy;
+ dx = dy = 0;
+
+ if (ABS (scroll->cur_dx) >= 1)
+ {
+ steps = trunc (scroll->cur_dx);
+ scroll->cur_dx -= steps;
+ dx = steps;
+ }
+
+ if (ABS (scroll->cur_dy) >= 1)
+ {
+ steps = trunc (scroll->cur_dy);
+ scroll->cur_dy -= steps;
+ dy = steps;
+ }
+ }
+ }
+ else if (gdk_event_get_scroll_direction (event, &direction))
+ {
+ switch (direction)
+ {
+ case GDK_SCROLL_UP:
+ dy -= 1;
+ break;
+ case GDK_SCROLL_DOWN:
+ dy += 1;
+ break;
+ case GDK_SCROLL_LEFT:
+ dx -= 1;
+ break;
+ case GDK_SCROLL_RIGHT:
+ dx += 1;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0)
+ dy = 0;
+ if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0)
+ dx = 0;
+ }
+
+ if (dx != 0 || dy != 0)
+ {
+ g_signal_emit (controller, signals[SCROLL], 0, dx, dy);
+
+ if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC)
+ scroll_history_push (scroll, dx, dy, gdk_event_get_time (event));
+ }
+
+ if (scroll->active && gdk_event_is_scroll_stop_event (event))
+ {
+ g_signal_emit (controller, signals[SCROLL_END], 0);
+ scroll->active = FALSE;
+
+ if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC)
+ {
+ gdouble vel_x, vel_y;
+
+ scroll_history_finish (scroll, &vel_x, &vel_y);
+ g_signal_emit (controller, signals[DECELERATE], 0, vel_x, vel_y);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass)
+{
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_event_controller_scroll_finalize;
+ object_class->set_property = gtk_event_controller_scroll_set_property;
+ object_class->get_property = gtk_event_controller_scroll_get_property;
+
+ controller_class->handle_event = gtk_event_controller_scroll_handle_event;
+
+ /**
+ * GtkEventControllerScroll:flags:
+ *
+ * The flags affecting event controller behavior
+ *
+ * Since: 3.93
+ **/
+ pspecs[PROP_FLAGS] =
+ g_param_spec_flags ("flags",
+ P_("Flags"),
+ P_("Flags"),
+ GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS,
+ GTK_EVENT_CONTROLLER_SCROLL_NONE,
+ GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkEventControllerScroll::scroll-begin:
+ * @controller: The object that received the signal
+ *
+ * Signals that a new scrolling operation has begun. It will
+ * only be emitted on devices capable of it.
+ **/
+ signals[SCROLL_BEGIN] =
+ g_signal_new (I_("scroll-begin"),
+ GTK_TYPE_EVENT_CONTROLLER_SCROLL,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ /**
+ * GtkEventControllerScroll::scroll:
+ * @controller: The object that received the signal
+ * @dx: X delta
+ * @dy: Y delta
+ *
+ * Signals that the widget should scroll by the
+ * amount specified by @dx and @dy.
+ **/
+ signals[SCROLL] =
+ g_signal_new (I_("scroll"),
+ GTK_TYPE_EVENT_CONTROLLER_SCROLL,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ _gtk_marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+ /**
+ * GtkEventControllerScroll::scroll-end:
+ * @controller: The object that received the signal
+ *
+ * Signals that a new scrolling operation has finished. It will
+ * only be emitted on devices capable of it.
+ **/
+ signals[SCROLL_END] =
+ g_signal_new (I_("scroll-end"),
+ GTK_TYPE_EVENT_CONTROLLER_SCROLL,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GtkEventControllerScroll::decelerate:
+ * @controller: The object that received the signal
+ * @vel_x: X velocity
+ * @vel_y: Y velocity
+ *
+ * Emitted after scroll is finished if the #GTK_EVENT_CONTROLLER_SCROLL_KINETIC
+ * flag is set. @vel_x and @vel_y express the initial velocity that was
+ * imprinted by the scroll events. @vel_x and @vel_y are expressed in
+ * pixels/ms.
+ **/
+ signals[DECELERATE] =
+ g_signal_new (I_("decelerate"),
+ GTK_TYPE_EVENT_CONTROLLER_SCROLL,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ _gtk_marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ g_object_class_install_properties (object_class, N_PROPS, pspecs);
+}
+
+static void
+gtk_event_controller_scroll_init (GtkEventControllerScroll *scroll)
+{
+ scroll->scroll_history = g_array_new (FALSE, FALSE,
+ sizeof (ScrollHistoryElem));
+}
+
+/**
+ * gtk_event_controller_scroll_new:
+ * @widget: a #GtkWidget
+ * @flags: behavior flags
+ *
+ * Creates a new event controller that will handle scroll events
+ * for the given @widget.
+ *
+ * Returns: a new #GtkEventControllerScroll
+ *
+ * Since: 3.93
+ **/
+GtkEventController *
+gtk_event_controller_scroll_new (GtkWidget *widget,
+ GtkEventControllerScrollFlags flags)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return g_object_new (GTK_TYPE_EVENT_CONTROLLER_SCROLL,
+ "widget", widget,
+ "flags", flags,
+ NULL);
+}
+
+/**
+ * gtk_event_controller_scroll_set_flags:
+ * @scroll: a #GtkEventControllerScroll
+ * @flags: behavior flags
+ *
+ * Sets the flags conditioning scroll controller behavior.
+ *
+ * Since: 3.93
+ **/
+void
+gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll,
+ GtkEventControllerScrollFlags flags)
+{
+ g_return_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll));
+
+ if (scroll->flags == flags)
+ return;
+
+ scroll->flags = flags;
+ g_object_notify_by_pspec (G_OBJECT (scroll), pspecs[PROP_FLAGS]);
+}
+
+/**
+ * gtk_event_controller_scroll_get_flags:
+ * @scroll: a #GtkEventControllerScroll
+ *
+ * Gets the flags conditioning the scroll controller behavior.
+ *
+ * Returns: the controller flags.
+ *
+ * Since: 3.93
+ **/
+GtkEventControllerScrollFlags
+gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *scroll)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll),
+ GTK_EVENT_CONTROLLER_SCROLL_NONE);
+
+ return scroll->flags;
+}
diff --git a/gtk/gtkeventcontrollerscroll.h b/gtk/gtkeventcontrollerscroll.h
new file mode 100644
index 0000000000..d1d33366c7
--- /dev/null
+++ b/gtk/gtkeventcontrollerscroll.h
@@ -0,0 +1,80 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#ifndef __GTK_EVENT_CONTROLLER_SCROLL_H__
+#define __GTK_EVENT_CONTROLLER_SCROLL_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtkeventcontroller.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EVENT_CONTROLLER_SCROLL (gtk_event_controller_scroll_get_type ())
+#define GTK_EVENT_CONTROLLER_SCROLL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScroll))
+#define GTK_EVENT_CONTROLLER_SCROLL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScrollClass))
+#define GTK_IS_EVENT_CONTROLLER_SCROLL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL))
+#define GTK_IS_EVENT_CONTROLLER_SCROLL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_SCROLL))
+#define GTK_EVENT_CONTROLLER_SCROLL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScrollClass))
+
+typedef struct _GtkEventControllerScroll GtkEventControllerScroll;
+typedef struct _GtkEventControllerScrollClass GtkEventControllerScrollClass;
+
+/**
+ * GtkEventControllerScrollFlags:
+ * @GTK_EVENT_CONTROLLER_SCROLL_NONE: Don't emit scroll.
+ * @GTK_EVENT_CONTROLLER_SCROLL_VERTICAL: Emit scroll with vertical deltas.
+ * @GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL: Emit scroll with horizontal deltas.
+ * @GTK_EVENT_CONTROLLER_SCROLL_DISCRETE: Only emit deltas that are multiples of 1.
+ * @GTK_EVENT_CONTROLLER_SCROLL_KINETIC: Emit #GtkEventControllerScroll::decelerate
+ * after continuous scroll finishes.
+ * @GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES: Emit scroll on both axes.
+ *
+ * Describes the behavior of a #GtkEventControllerScroll.
+ *
+ * Since: 3.93
+ **/
+typedef enum {
+ GTK_EVENT_CONTROLLER_SCROLL_NONE = 0,
+ GTK_EVENT_CONTROLLER_SCROLL_VERTICAL = 1 << 0,
+ GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL = 1 << 1,
+ GTK_EVENT_CONTROLLER_SCROLL_DISCRETE = 1 << 2,
+ GTK_EVENT_CONTROLLER_SCROLL_KINETIC = 1 << 3,
+ GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES = (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL),
+} GtkEventControllerScrollFlags;
+
+GDK_AVAILABLE_IN_3_24
+GType gtk_event_controller_scroll_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_24
+GtkEventController *gtk_event_controller_scroll_new (GtkWidget *widget,
+ GtkEventControllerScrollFlags flags);
+GDK_AVAILABLE_IN_3_24
+void gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *controller,
+ GtkEventControllerScrollFlags flags);
+GDK_AVAILABLE_IN_3_24
+GtkEventControllerScrollFlags
+ gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *controller);
+
+G_END_DECLS
+
+#endif /* __GTK_EVENT_CONTROLLER_SCROLL_H__ */
diff --git a/gtk/gtkgesturestylus.c b/gtk/gtkgesturestylus.c
new file mode 100644
index 0000000000..cda366b92f
--- /dev/null
+++ b/gtk/gtkgesturestylus.c
@@ -0,0 +1,269 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017-2018, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+/**
+ * SECTION:gtkgesturestylus
+ * @Short_description: Gesture for stylus input
+ * @Title: GtkGestureStylus
+ * @See_also: #GtkGesture, #GtkGestureSingle
+ *
+ * #GtkGestureStylus is a #GtkGesture implementation specific to stylus
+ * input. The provided signals just provide the basic information
+ */
+
+#include "config.h"
+#include "gtkgesturestylus.h"
+#include "gtkgesturestylusprivate.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+#include "gtkmain.h"
+
+G_DEFINE_TYPE (GtkGestureStylus, gtk_gesture_stylus, GTK_TYPE_GESTURE_SINGLE)
+
+enum {
+ PROXIMITY,
+ DOWN,
+ MOTION,
+ UP,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
+static gboolean
+gtk_gesture_stylus_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GdkModifierType modifiers;
+ guint n_signal;
+ gdouble x, y;
+
+ GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_stylus_parent_class)->handle_event (controller, event);
+
+ if (!gdk_event_get_device_tool (event))
+ return FALSE;
+ if (!gdk_event_get_coords (event, &x, &y))
+ return FALSE;
+
+ switch ((guint) gdk_event_get_event_type (event))
+ {
+ case GDK_BUTTON_PRESS:
+ n_signal = DOWN;
+ break;
+ case GDK_BUTTON_RELEASE:
+ n_signal = UP;
+ break;
+ case GDK_MOTION_NOTIFY:
+ gdk_event_get_state (event, &modifiers);
+
+ if (modifiers & GDK_BUTTON1_MASK)
+ n_signal = MOTION;
+ else
+ n_signal = PROXIMITY;
+ break;
+ default:
+ return FALSE;
+ }
+
+ g_signal_emit (controller, signals[n_signal], 0, x, y);
+
+ return TRUE;
+}
+
+static void
+gtk_gesture_stylus_class_init (GtkGestureStylusClass *klass)
+{
+ GtkEventControllerClass *event_controller_class;
+
+ event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+ event_controller_class->handle_event = gtk_gesture_stylus_handle_event;
+
+ signals[PROXIMITY] =
+ g_signal_new (I_("proximity"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkGestureStylusClass, proximity),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+ signals[DOWN] =
+ g_signal_new (I_("down"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkGestureStylusClass, down),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+ signals[MOTION] =
+ g_signal_new (I_("motion"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkGestureStylusClass, motion),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+ signals[UP] =
+ g_signal_new (I_("up"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkGestureStylusClass, up),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+}
+
+static void
+gtk_gesture_stylus_init (GtkGestureStylus *gesture)
+{
+}
+
+/**
+ * gtk_gesture_stylus_new:
+ * @widget: a #GtkWidget
+ *
+ * Creates a new #GtkGestureStylus.
+ *
+ * Returns: a newly created stylus gesture
+ *
+ * Since: 3.94
+ **/
+GtkGesture *
+gtk_gesture_stylus_new (GtkWidget *widget)
+{
+ return g_object_new (GTK_TYPE_GESTURE_STYLUS,
+ "widget", widget,
+ NULL);
+}
+
+static const GdkEvent *
+gesture_get_current_event (GtkGestureStylus *gesture)
+{
+ GdkEventSequence *sequence;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+
+ return gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+}
+
+/**
+ * gtk_gesture_stylus_get_axis:
+ * @gesture: a #GtkGestureStylus
+ * @axis: requested device axis
+ * @value: (out): return location for the axis value
+ *
+ * Returns the current value for the requested @axis. This function
+ * must be called from either the #GtkGestureStylus:down,
+ * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity
+ * signals.
+ *
+ * Returns: #TRUE if there is a current value for the axis
+ *
+ * Since: 3.94
+ **/
+gboolean
+gtk_gesture_stylus_get_axis (GtkGestureStylus *gesture,
+ GdkAxisUse axis,
+ gdouble *value)
+{
+ const GdkEvent *event;
+
+ g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
+ g_return_val_if_fail (axis < GDK_AXIS_LAST, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ event = gesture_get_current_event (gesture);
+ if (!event)
+ return FALSE;
+
+ return gdk_event_get_axis (event, axis, value);
+}
+
+/**
+ * gtk_gesture_stylus_get_axes:
+ * @gesture: a GtkGestureStylus
+ * @axes: array of requested axes, terminated with #GDK_AXIS_IGNORE
+ * @values: (out): return location for the axis values
+ *
+ * Returns the current values for the requested @axes. This function
+ * must be called from either the #GtkGestureStylus:down,
+ * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity
+ * signals.
+ *
+ * Returns: #TRUE if there is a current value for the axes
+ **/
+gboolean
+gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture,
+ GdkAxisUse axes[],
+ gdouble **values)
+{
+ const GdkEvent *event;
+ GArray *array;
+ gint i = 0;
+
+ g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
+ g_return_val_if_fail (values != NULL, FALSE);
+
+ event = gesture_get_current_event (gesture);
+ if (!event)
+ return FALSE;
+
+ array = g_array_new (TRUE, FALSE, sizeof (gdouble));
+
+ while (axes[i] != GDK_AXIS_IGNORE)
+ {
+ gdouble value;
+
+ if (axes[i] >= GDK_AXIS_LAST)
+ {
+ g_warning ("Requesting unknown axis %d, did you "
+ "forget to add a last GDK_AXIS_IGNORE axis?",
+ axes[i]);
+ g_array_free (array, TRUE);
+ return FALSE;
+ }
+
+ gdk_event_get_axis (event, axes[i], &value);
+ g_array_append_val (array, value);
+ i++;
+ }
+
+ *values = (gdouble *) g_array_free (array, FALSE);
+ return TRUE;
+}
+
+/**
+ * gtk_gesture_stylus_get_device_tool:
+ * @gesture: a #GtkGestureStylus
+ *
+ * Returns the #GdkDeviceTool currently driving input through this gesture.
+ * This function must be called from either the #GtkGestureStylus:down,
+ * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity
+ * signals.
+ *
+ * Returns: (nullable) (transfer none): The current stylus tool
+ **/
+GdkDeviceTool *
+gtk_gesture_stylus_get_device_tool (GtkGestureStylus *gesture)
+{
+ const GdkEvent *event;
+
+ g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
+
+ event = gesture_get_current_event (gesture);
+ if (!event)
+ return NULL;
+
+ return gdk_event_get_device_tool (event);
+}
diff --git a/gtk/gtkgesturestylus.h b/gtk/gtkgesturestylus.h
new file mode 100644
index 0000000000..c45fd8a8b5
--- /dev/null
+++ b/gtk/gtkgesturestylus.h
@@ -0,0 +1,59 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017-2018, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+#ifndef __GTK_GESTURE_STYLUS_H__
+#define __GTK_GESTURE_STYLUS_H__
+
+#include <gtk/gtkgesture.h>
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GESTURE_STYLUS (gtk_gesture_stylus_get_type ())
+#define GTK_GESTURE_STYLUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylus))
+#define GTK_GESTURE_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylusClass))
+#define GTK_IS_GESTURE_STYLUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURE_STYLUS))
+#define GTK_IS_GESTURE_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GESTURE_STYLUS))
+#define GTK_GESTURE_STYLUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylusClass))
+
+typedef struct _GtkGestureStylus GtkGestureStylus;
+typedef struct _GtkGestureStylusClass GtkGestureStylusClass;
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_gesture_stylus_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkGesture * gtk_gesture_stylus_new (GtkWidget *widget);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_gesture_stylus_get_axis (GtkGestureStylus *gesture,
+ GdkAxisUse axis,
+ gdouble *value);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture,
+ GdkAxisUse axes[],
+ gdouble **values);
+GDK_AVAILABLE_IN_ALL
+GdkDeviceTool * gtk_gesture_stylus_get_device_tool (GtkGestureStylus *gesture);
+
+G_END_DECLS
+
+#endif /* __GTK_GESTURE_STYLUS_H__ */
diff --git a/gtk/gtkgesturestylusprivate.h b/gtk/gtkgesturestylusprivate.h
new file mode 100644
index 0000000000..9869b528a5
--- /dev/null
+++ b/gtk/gtkgesturestylusprivate.h
@@ -0,0 +1,51 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017-2018, Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+#ifndef __GTK_GESTURE_STYLUS_PRIVATE_H__
+#define __GTK_GESTURE_STYLUS_PRIVATE_H__
+
+#include "gtkgesturesingleprivate.h"
+#include "gtkgesturestylus.h"
+
+struct _GtkGestureStylus
+{
+ GtkGestureSingle parent_instance;
+};
+
+struct _GtkGestureStylusClass
+{
+ GtkGestureSingleClass parent_class;
+
+ void (*proximity) (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y);
+ void (*down) (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y);
+ void (*motion) (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y);
+ void (*up) (GtkGestureStylus *gesture,
+ gdouble x,
+ gdouble y);
+
+ /*< private >*/
+ gpointer padding[10];
+};
+
+#endif /* __GTK_GESTURE_STYLUS_PRIVATE_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a2cd6f5c86..f3bfe4d7f8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -142,6 +142,9 @@ gtk/gtkentry.c
gtk/gtkentrycompletion.c
gtk/gtkeventbox.c
gtk/gtkeventcontroller.c
+gtk/gtkeventcontrollerkey.c
+gtk/gtkeventcontrollermotion.c
+gtk/gtkeventcontrollerscroll.c
gtk/gtkexpander.c
gtk/gtkfilechooserbutton.c
gtk/gtkfilechooser.c
@@ -170,6 +173,7 @@ gtk/gtkgesturemultipress.c
gtk/gtkgesturepan.c
gtk/gtkgesturerotate.c
gtk/gtkgesturesingle.c
+gtk/gtkgesturestylus.c
gtk/gtkgestureswipe.c
gtk/gtkgesturezoom.c
gtk/gtkglarea.c