Compare commits

...

20 Commits

Author SHA1 Message Date
Carlos Garnacho
35a643a3c2 demos: Use stateful actions for rings/strips in "Touch and Drawing Tablets" demo 2016-08-23 20:32:36 +02:00
Carlos Garnacho
2833214d45 GtkPadController: Make ring/strip actions be activated with the axis value
This way these axes may be used in detail by the implementors of pad GActions.
2016-08-23 20:32:36 +02:00
Carlos Garnacho
6bf3b1de28 GtkEventController: Add some comment describing the filter_event private vfunc
It might not be entirely clear what the boolean return value means.
2016-08-23 20:32:36 +02:00
Carlos Garnacho
5924983ef4 demos: Add pad support to "event axes" demo
And rename it to "Touch and Drawing Tablets", since it's no longer about
"axes" really.

As for pad support in the demo, just keep it "simple", make the
controller handle all pad devices, and make all the actions have the
same callback.
2016-08-23 20:19:51 +02:00
Carlos Garnacho
5d88fe3516 GtkPadController: Notify actions back to the windowing on wayland
This allows applications to provide descriptions of the actions performed
by each pad feature. pass the GtkPadActionEntry labels for this.
2016-08-23 20:19:51 +02:00
Carlos Garnacho
b6fd24300c wayland: Offer wayland-specific method to set pad actions feedback
The wayland tablet protocol allows notifying the compositor with
descriptions of the actions performed by each tablet element. This
API call allows to hook up in to this wayland-specific feature.
2016-08-23 20:19:50 +02:00
Carlos Garnacho
8e173a09f9 wayland: Support pad devices in gdk_wayland_device_get_node_path()
We can return the node path on those too, so do that.
2016-08-23 20:19:50 +02:00
Carlos Garnacho
766bc583f5 wayland: Implement pad event emission
We now send all the set of button/ring/strip/group_mode events.
2016-08-23 20:19:50 +02:00
Carlos Garnacho
23aa1652b8 wayland: Create/expose pad devices
These devices are kind of an strange case. Their "master" device is
the keyboard, because they share toplevel focus with it, regardless
of stylus focus. Nonetheless, they are only expected to send the
GdkEventPad* set of events.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
d971d7fb66 wayland: Add GdkWaylandDevicePad
This is a subclass of GdkWaylandDevice that implements GdkDevicePad,
all pad features are looked up from the info obtained through the
tablet v2 interface.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
537c4c9cb7 wayland: Implement backbone of pad support
All pad interfaces and features are poked, we just now need
exposing those.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
9369f294e3 gtk: Add GtkPadController
This GdkEventController is a helper object to handle pad events,
it allows setting a mapping to action names, to be triggered in
the given action group.

In order to help on places where advanced mapping/configurability
of pad features is not desirable, this controller also allows
passing a NULL pad device, meaning it will listen on all pads,
and/or passing -1 on mode/index, so an action applies to all
modes/features (eg. strips/rings).
2016-08-23 14:11:13 +02:00
Carlos Garnacho
46e06dab9b gtk: Add minimal handling of pad events
No real handling is yet performed, to be done through a GdkEventController
2016-08-23 14:11:13 +02:00
Carlos Garnacho
2467510297 gdk: Add GdkDevicePad
This is an interface meant to be implemented by the "pad" devices.
This device-specific interface exposes the mapping of all pad features,
it allows retrieving:
- The number of buttons/rings/strips
- The number of groups
- The number of modes a group has
- Whether a given button/ring/strip belongs to a given group
2016-08-23 14:11:13 +02:00
Carlos Garnacho
a20481cdb1 gdk: Address pad events similarly to keyboard events
We want the same treatment for those, the event will be emitted on the
toplevel, which will then decide what to do with the event.

It just doesn't make much sense to propagate those up/down the hierarchy,
when we want specifically one action being triggered from those.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
1041c04d07 gdk: Add pad event structs, enum values, and event mask bit
GDK_PAD_BUTTON*,RING and STRIP will be emitted respectively when
pad buttons, rings or strips are interacted with. Each of those
pad components belong to a group (a pad can contain several of
those), which may be in a given mode. All this information is
contained in the event.

GDK_PAD_GROUP_MODE is emitted when a group in the pad switches
mode, which will generally result in a different set of actions
being triggered from the same buttons/rings/strips in the group.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
1f920d59e8 gdkdevice: Add GDK_SOURCE_TABLET_PAD input source type for GdkDevices
This will represent a tablet pad.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
d6f9ed2a99 wayland: Add wayland-specific method to retrieve a device node path
This will be useful at least for g-c-c, in order to match libwacom
data with GdkDevices.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
2ce5df5e12 gdk: Pass hardware ID on gdk_device_tool_new()
And implement this on wayland, where this information is already obtained.
2016-08-23 14:11:13 +02:00
Carlos Garnacho
ca108c7af4 gdk: Add a getter for the hardware id of a GdkDeviceTool
Although scarcely used, this information may be useful to retrieve
from the windowing systems that offer this information.
2016-08-23 14:11:13 +02:00
30 changed files with 2071 additions and 11 deletions

View File

@@ -1,11 +1,12 @@
/* Event Axes
/* Touch and Drawing Tablets
*
* Demonstrates advanced handling of event information from exotic
* input devices.
*
* On one hand, this snippet demonstrates management of input axes,
* On one hand, this snippet demonstrates management of drawing tablets,
* those contain additional information for the pointer other than
* X/Y coordinates.
* X/Y coordinates. Tablet pads events are mapped to actions, which
* are both defined and interpreted by the application.
*
* Input axes are dependent on hardware devices, on linux/unix you
* can see the device axes through xinput list <device>. Each time
@@ -20,6 +21,7 @@
* touchpoints can be tracked.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
typedef struct {
@@ -51,7 +53,30 @@ const gchar *colors[] = {
"burlywood"
};
static GtkPadActionEntry pad_actions[] = {
{ GTK_PAD_ACTION_BUTTON, 1, -1, N_("Nuclear strike"), "pad.nuke" },
{ GTK_PAD_ACTION_BUTTON, 2, -1, N_("Release siberian methane reserves"), "pad.heat" },
{ GTK_PAD_ACTION_BUTTON, 3, -1, N_("Release solar flare"), "pad.fry" },
{ GTK_PAD_ACTION_BUTTON, 4, -1, N_("De-stabilize Oort cloud"), "pad.fall" },
{ GTK_PAD_ACTION_BUTTON, 5, -1, N_("Ignite WR-104"), "pad.burst" },
{ GTK_PAD_ACTION_BUTTON, 6, -1, N_("Lart whoever asks about this button"), "pad.lart" },
{ GTK_PAD_ACTION_RING, -1, -1, N_("Earth axial tilt"), "pad.tilt" },
{ GTK_PAD_ACTION_STRIP, -1, -1, N_("Extent of weak nuclear force"), "pad.dissolve" },
};
static const gchar *pad_action_results[] = {
"",
"",
"",
"",
"",
"💫",
"",
""
};
static guint cur_color = 0;
static guint pad_action_timeout_id = 0;
static AxesInfo *
axes_info_new (void)
@@ -488,12 +513,108 @@ draw_cb (GtkWidget *widget,
return FALSE;
}
static void
update_label_text (GtkWidget *label,
const gchar *text)
{
gchar *markup = NULL;
if (text)
markup = g_strdup_printf ("<span font='48.0'>%s</span>", text);
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (markup);
}
static gboolean
reset_label_text_timeout_cb (gpointer user_data)
{
GtkWidget *label = user_data;
update_label_text (label, NULL);
pad_action_timeout_id = 0;
return G_SOURCE_REMOVE;
}
static void
update_label_and_timeout (GtkWidget *label,
const gchar *text)
{
if (pad_action_timeout_id)
g_source_remove (pad_action_timeout_id);
update_label_text (label, text);
pad_action_timeout_id = g_timeout_add (200, reset_label_text_timeout_cb, label);
}
static void
on_action_activate (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkWidget *label = user_data;
const gchar *result;
gchar *str;
result = g_object_get_data (G_OBJECT (action), "action-result");
if (!parameter)
update_label_and_timeout (label, result);
else
{
str = g_strdup_printf ("%s %.2f", result, g_variant_get_double (parameter));
update_label_and_timeout (label, str);
g_free (str);
}
}
static void
init_pad_controller (GtkWidget *window,
GtkWidget *label)
{
GtkPadController *pad_controller;
GSimpleActionGroup *action_group;
GSimpleAction *action;
gint i;
action_group = g_simple_action_group_new ();
pad_controller = gtk_pad_controller_new (GTK_WINDOW (window),
G_ACTION_GROUP (action_group),
NULL);
for (i = 0; i < G_N_ELEMENTS (pad_actions); i++)
{
if (pad_actions[i].type == GTK_PAD_ACTION_BUTTON)
{
action = g_simple_action_new (pad_actions[i].action_name, NULL);
}
else
{
action = g_simple_action_new_stateful (pad_actions[i].action_name,
G_VARIANT_TYPE_DOUBLE, NULL);
}
g_signal_connect (action, "activate",
G_CALLBACK (on_action_activate), label);
g_object_set_data (G_OBJECT (action), "action-result",
(gpointer) pad_action_results[i]);
g_action_map_add_action (G_ACTION_MAP (action_group), G_ACTION (action));
g_object_unref (action);
}
gtk_pad_controller_set_action_entries (pad_controller, pad_actions,
G_N_ELEMENTS (pad_actions));
g_object_set_data_full (G_OBJECT (window), "pad-controller",
pad_controller, g_object_unref);
g_object_unref (action_group);
}
GtkWidget *
do_event_axes (GtkWidget *toplevel)
{
static GtkWidget *window = NULL;
EventData *event_data;
GtkWidget *box;
GtkWidget *box, *label;
if (!window)
{
@@ -524,6 +645,12 @@ do_event_axes (GtkWidget *toplevel)
G_CALLBACK (event_cb), event_data);
g_signal_connect (box, "draw",
G_CALLBACK (draw_cb), event_data);
label = gtk_label_new ("");
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_container_add (GTK_CONTAINER (box), label);
init_pad_controller (window, label);
}
if (!gtk_widget_get_visible (window))

View File

@@ -25,6 +25,7 @@
<xi:include href="xml/gdkseat.xml" />
<xi:include href="xml/gdkmonitor.xml" />
<xi:include href="xml/gdkdevice.xml" />
<xi:include href="xml/gdkdevicepad.xml" />
<xi:include href="xml/regions.xml" />
<xi:include href="xml/pixbufs.xml" />
<xi:include href="xml/rgba_colors.xml" />

View File

@@ -796,6 +796,25 @@ gdk_device_type_get_type
GDK_MAX_TIMECOORD_AXES
</SECTION>
<SECTION>
<TITLE>GdkDevicePad</TITLE>
<FILE>gdkdevicepad</FILE>
GdkDevicePad
GdkDevicePadFeature
gdk_device_pad_get_n_groups
gdk_device_pad_get_group_n_modes
gdk_device_pad_get_n_features
gdk_device_pad_get_feature_group
<SUBSECTION Standard>
GDK_TYPE_DEVICE_PAD
GDK_DEVICE_PAD
GDK_IS_DEVICE_PAD
<SUBSECTION Private>
gdk_device_pad_get_type
</SECTION>
<SECTION>
<TITLE>GdkSeat</TITLE>
<FILE>gdkseat</FILE>
@@ -941,6 +960,9 @@ GdkEventOwnerChange
GdkEventGrabBroken
GdkEventTouchpadSwipe
GdkEventTouchpadPinch
GdkEventPadButton
GdkEventPadAxis
GdkEventPadGroupMode
<SUBSECTION>
GdkScrollDirection

View File

@@ -4,6 +4,7 @@ gdk_app_launch_context_get_type
gdk_cursor_get_type
gdk_device_get_type
gdk_device_manager_get_type
gdk_device_pad_get_type
gdk_display_get_type
gdk_display_manager_get_type
gdk_drag_context_get_type

View File

@@ -299,7 +299,7 @@
</chapter>
<chapter id="Gestures">
<title>Gestures</title>
<title>Gestures and event handling</title>
<xi:include href="xml/gtkeventcontroller.xml" />
<xi:include href="xml/gtkgesture.xml" />
<xi:include href="xml/gtkgesturesingle.xml" />
@@ -310,6 +310,7 @@
<xi:include href="xml/gtkgestureswipe.xml" />
<xi:include href="xml/gtkgesturerotate.xml" />
<xi:include href="xml/gtkgesturezoom.xml" />
<xi:include href="xml/gtkpadcontroller.xml" />
</chapter>
<chapter id="DeprecatedObjects">

View File

@@ -8450,6 +8450,28 @@ GTK_GESTURE_ZOOM_GET_CLASS
gtk_gesture_zoom_get_type
</SECTION>
<SECTION>
<FILE>gtkpadcontroller</FILE>
<TITLE>GtkPadController</TITLE>
GtkPadController
gtk_pad_controller_new
gtk_pad_controller_set_action_entries
gtk_pad_controller_set_action
GtkPadActionType
GtkPadActionEntry
<SUBSECTION Standard>
GTK_TYPE_PAD_CONTROLLER
GTK_PAD_CONTROLLER
GTK_PAD_CONTROLLER_CLASS
GTK_IS_PAD_CONTROLLER
GTK_IS_PAD_CONTROLLER_CLASS
GTK_PAD_CONTROLLER_GET_CLASS
<SUBSECTION Private>
gtk_pad_controller_get_type
</SECTION>
<SECTION>
<FILE>gtkstacksidebar</FILE>
GtkStackSidebar

View File

@@ -133,6 +133,7 @@ gtk_numerable_icon_get_type
gtk_offscreen_window_get_type
gtk_orientable_get_type
gtk_overlay_get_type
gtk_pad_controller_get_type
gtk_page_setup_get_type
@DISABLE_ON_W32@gtk_page_setup_unix_dialog_get_type
gtk_paned_get_type

View File

@@ -71,6 +71,7 @@ gdk_public_h_sources = \
gdkcairo.h \
gdkcursor.h \
gdkdevice.h \
gdkdevicepad.h \
gdkdevicetool.h \
gdkdevicemanager.h \
gdkdisplay.h \
@@ -114,6 +115,7 @@ gdk_private_headers = \
gdkcursorprivate.h \
gdkdevicemanagerprivate.h \
gdkdeviceprivate.h \
gdkdevicepadprivate.h \
gdkdevicetoolprivate.h \
gdkdisplaymanagerprivate.h \
gdkdisplayprivate.h \
@@ -144,6 +146,7 @@ gdk_c_sources = \
gdkcursor.c \
gdkdeprecated.c \
gdkdevice.c \
gdkdevicepad.c \
gdkdevicetool.c \
gdkdevicemanager.c \
gdkdisplay.c \

View File

@@ -33,6 +33,7 @@
#include <gdk/gdkcairo.h>
#include <gdk/gdkcursor.h>
#include <gdk/gdkdevice.h>
#include <gdk/gdkdevicepad.h>
#include <gdk/gdkdevicetool.h>
#include <gdk/gdkdevicemanager.h>
#include <gdk/gdkdisplay.h>

View File

@@ -49,6 +49,9 @@ typedef struct _GdkTimeCoord GdkTimeCoord;
* as a touchpad. This device type has been added in 3.4.
* @GDK_SOURCE_TRACKPOINT: the device is a trackpoint. This device type has been
* added in 3.22
* @GDK_SOURCE_TABLET_PAD: the device is a "pad", a collection of buttons,
* rings and strips found in drawing tablets. This device type has been
* added in 3.22.
*
* An enumeration describing the type of an input device in general terms.
*/
@@ -61,7 +64,8 @@ typedef enum
GDK_SOURCE_KEYBOARD,
GDK_SOURCE_TOUCHSCREEN,
GDK_SOURCE_TOUCHPAD,
GDK_SOURCE_TRACKPOINT
GDK_SOURCE_TRACKPOINT,
GDK_SOURCE_TABLET_PAD
} GdkInputSource;
/**

150
gdk/gdkdevicepad.c Normal file
View File

@@ -0,0 +1,150 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2016 Red Hat
*
* 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: Carlos Garnacho <carlosg@gnome.org>
*/
/**
* SECTION:gdkdevicepad
* @Short_description: Pad device interface
* @Title: GtkDevicePad
*
* #GdkDevicePad is an interface implemented by devices of type
* %GDK_SOURCE_TABLET_PAD, it allows querying the features provided
* by the pad device.
*
* Tablet pads may contain one or more groups, each containing a subset
* of the buttons/rings/strips available. gdk_device_pad_get_n_groups()
* can be used to obtain the number of groups, gdk_device_pad_get_n_features()
* and gdk_device_pad_get_feature_group() can be combined to find out the
* number of buttons/rings/strips the device has, and how are they grouped.
*
* Each of those groups have different modes, which may be used to map
* each individual pad feature to multiple actions. Only one mode is
* effective (current) for each given group, different groups may have
* different current modes. The number of available modes in a group can
* be found out through gdk_device_pad_get_group_n_modes(), and the current
* mode for a given group will be notified through the #GdkEventPadGroupMode
* event.
*
*/
#include "config.h"
#include "gdkdevicepad.h"
#include "gdkdevicepadprivate.h"
#include "gdkdeviceprivate.h"
G_DEFINE_INTERFACE (GdkDevicePad, gdk_device_pad, GDK_TYPE_DEVICE)
static void
gdk_device_pad_default_init (GdkDevicePadInterface *pad)
{
}
/**
* gdk_device_pad_get_n_groups:
* @pad: a #GdkDevicePad
*
* Returns the number of groups this pad device has. Pads have
* at least one group. A pad group is a subcollection of
* buttons/strip/rings that is affected collectively by a same
* current mode.
*
* Returns: The number of button/ring/strip groups in the pad.
*
* Since: 3.22
**/
gint
gdk_device_pad_get_n_groups (GdkDevicePad *pad)
{
GdkDevicePadInterface *iface = GDK_DEVICE_PAD_GET_IFACE (pad);
g_return_val_if_fail (GDK_IS_DEVICE_PAD (pad), 0);
return iface->get_n_groups (pad);
}
/**
* gdk_device_pad_get_group_n_modes:
* @pad: a #GdkDevicePad
* @group_idx: group to get the number of available modes from
*
* Returns the number of modes that @group may have.
*
* Returns: The number of modes available in @group.
*
* Since: 3.22
**/
gint
gdk_device_pad_get_group_n_modes (GdkDevicePad *pad,
gint group_idx)
{
GdkDevicePadInterface *iface = GDK_DEVICE_PAD_GET_IFACE (pad);
g_return_val_if_fail (GDK_IS_DEVICE_PAD (pad), 0);
g_return_val_if_fail (group_idx >= 0, 0);
return iface->get_group_n_modes (pad, group_idx);
}
/**
* gdk_device_pad_get_n_features:
* @pad: a #GdkDevicePad
* @feature: a pad feature
*
* Returns the number of features a tablet pad has.
*
* Returns: The amount of elements of type @feature that this pad has.
*
* Since: 3.22
**/
gint
gdk_device_pad_get_n_features (GdkDevicePad *pad,
GdkDevicePadFeature feature)
{
GdkDevicePadInterface *iface = GDK_DEVICE_PAD_GET_IFACE (pad);
g_return_val_if_fail (GDK_IS_DEVICE_PAD (pad), 0);
return iface->get_n_features (pad, feature);
}
/**
* gdk_device_pad_get_feature_group:
* @pad: a #GdkDevicePad
* @feature: the feature type to get the group from
* @feature_idx: the index of the feature to get the group from
*
* Returns the group the given @feature and @idx belong to,
* or -1 if feature/index do not exist in @pad.
*
* Returns: The group number of the queried pad feature.
*
* Since: 3.22
**/
gint
gdk_device_pad_get_feature_group (GdkDevicePad *pad,
GdkDevicePadFeature feature,
gint idx)
{
GdkDevicePadInterface *iface = GDK_DEVICE_PAD_GET_IFACE (pad);
g_return_val_if_fail (GDK_IS_DEVICE_PAD (pad), -1);
g_return_val_if_fail (idx >= 0, -1);
return iface->get_feature_group (pad, feature, idx);
}

74
gdk/gdkdevicepad.h Normal file
View File

@@ -0,0 +1,74 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2016 Red Hat
*
* 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: Carlos Garnacho <carlosg@gnome.org>
*/
#ifndef __GDK_DEVICE_PAD_H__
#define __GDK_DEVICE_PAD_H__
#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
G_BEGIN_DECLS
#define GDK_TYPE_DEVICE_PAD (gdk_device_pad_get_type ())
#define GDK_DEVICE_PAD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_PAD, GdkDevicePad))
#define GDK_IS_DEVICE_PAD(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_PAD))
typedef struct _GdkDevicePad GdkDevicePad;
typedef struct _GdkDevicePadInterface GdkDevicePadInterface;
/**
* GdkDevicePadFeature:
* @GDK_DEVICE_PAD_FEATURE_BUTTON: a button
* @GDK_DEVICE_PAD_FEATURE_RING: a ring-shaped interactive area
* @GDK_DEVICE_PAD_FEATURE_STRIP: a straight interactive area
*
* A pad feature.
*/
typedef enum {
GDK_DEVICE_PAD_FEATURE_BUTTON,
GDK_DEVICE_PAD_FEATURE_RING,
GDK_DEVICE_PAD_FEATURE_STRIP
} GdkDevicePadFeature;
GDK_AVAILABLE_IN_3_22
GType gdk_device_pad_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
gint gdk_device_pad_get_n_groups (GdkDevicePad *pad);
GDK_AVAILABLE_IN_3_22
gint gdk_device_pad_get_group_n_modes (GdkDevicePad *pad,
gint group_idx);
GDK_AVAILABLE_IN_3_22
gint gdk_device_pad_get_n_features (GdkDevicePad *pad,
GdkDevicePadFeature feature);
GDK_AVAILABLE_IN_3_22
gint gdk_device_pad_get_feature_group (GdkDevicePad *pad,
GdkDevicePadFeature feature,
gint feature_idx);
G_END_DECLS
#endif /* __GDK_DEVICE_PAD_H__ */

45
gdk/gdkdevicepadprivate.h Normal file
View File

@@ -0,0 +1,45 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2016 Red Hat
*
* 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: Carlos Garnacho <carlosg@gnome.org>
*/
#ifndef __GDK_DEVICE_PAD_PRIVATE_H__
#define __GDK_DEVICE_PAD_PRIVATE_H__
#include "gdkdevicepad.h"
G_BEGIN_DECLS
#define GDK_DEVICE_PAD_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GDK_TYPE_DEVICE_PAD, GdkDevicePadInterface))
struct _GdkDevicePadInterface {
GTypeInterface parent_interface;
gint (* get_n_groups) (GdkDevicePad *pad);
gint (* get_group_n_modes) (GdkDevicePad *pad,
gint group);
gint (* get_n_features) (GdkDevicePad *pad,
GdkDevicePadFeature feature);
gint (* get_feature_group) (GdkDevicePad *pad,
GdkDevicePadFeature feature,
gint idx);
};
G_END_DECLS
#endif /* __GDK_DEVICE_PAD_PRIVATE_H__ */

View File

@@ -31,6 +31,7 @@ enum {
TOOL_PROP_SERIAL,
TOOL_PROP_TOOL_TYPE,
TOOL_PROP_AXES,
TOOL_PROP_HARDWARE_ID,
N_TOOL_PROPS
};
@@ -55,6 +56,9 @@ gdk_device_tool_set_property (GObject *object,
case TOOL_PROP_AXES:
tool->tool_axes = g_value_get_flags (value);
break;
case TOOL_PROP_HARDWARE_ID:
tool->hw_id = g_value_get_uint64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -80,6 +84,9 @@ gdk_device_tool_get_property (GObject *object,
case TOOL_PROP_AXES:
g_value_set_flags (value, tool->tool_axes);
break;
case TOOL_PROP_HARDWARE_ID:
g_value_set_uint64 (value, tool->hw_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -113,6 +120,12 @@ gdk_device_tool_class_init (GdkDeviceToolClass *klass)
GDK_TYPE_AXIS_FLAGS, 0,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
tool_props[TOOL_PROP_HARDWARE_ID] = g_param_spec_uint64 ("hardware-id",
"Hardware ID",
"Hardware ID",
0, G_MAXUINT64, 0,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_TOOL_PROPS, tool_props);
}
@@ -124,11 +137,13 @@ gdk_device_tool_init (GdkDeviceTool *tool)
GdkDeviceTool *
gdk_device_tool_new (guint64 serial,
guint64 hw_id,
GdkDeviceToolType type,
GdkAxisFlags tool_axes)
{
return g_object_new (GDK_TYPE_DEVICE_TOOL,
"serial", serial,
"hardware-id", hw_id,
"tool-type", type,
"axes", tool_axes,
NULL);
@@ -153,6 +168,32 @@ gdk_device_tool_get_serial (GdkDeviceTool *tool)
return tool->serial;
}
/**
* gdk_device_tool_get_hardware_id:
* @tool: a #GdkDeviceTool
*
* Gets the hardware ID of this tool, or 0 if it's not known. When
* non-zero, the identificator is unique for the given tool model,
* meaning that two identical tools will share the same @hardware_id,
* but will have different serial numbers (see gdk_device_tool_get_serial()).
*
* This is a more concrete (and device specific) method to identify
* a #GdkDeviceTool than gdk_device_tool_get_tool_type(), as a tablet
* may support multiple devices with the same #GdkDeviceToolType,
* but having different hardware identificators.
*
* Returns: The hardware identificator of this tool.
*
* Since: 3.22
**/
guint64
gdk_device_tool_get_hardware_id (GdkDeviceTool *tool)
{
g_return_val_if_fail (tool != NULL, 0);
return tool->hw_id;
}
/**
* gdk_device_tool_get_tool_type:
* @tool: a #GdkDeviceTool

View File

@@ -67,6 +67,9 @@ GType gdk_device_tool_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
guint64 gdk_device_tool_get_serial (GdkDeviceTool *tool);
GDK_AVAILABLE_IN_3_22
guint64 gdk_device_tool_get_hardware_id (GdkDeviceTool *tool);
GDK_AVAILABLE_IN_3_22
GdkDeviceToolType gdk_device_tool_get_tool_type (GdkDeviceTool *tool);

View File

@@ -28,6 +28,7 @@ struct _GdkDeviceTool
{
GObject parent_instance;
guint64 serial;
guint64 hw_id;
GdkDeviceToolType type;
GdkAxisFlags tool_axes;
};
@@ -38,6 +39,7 @@ struct _GdkDeviceToolClass
};
GdkDeviceTool *gdk_device_tool_new (guint64 serial,
guint64 hw_id,
GdkDeviceToolType type,
GdkAxisFlags tool_axes);

View File

@@ -920,6 +920,14 @@ gdk_event_get_time (const GdkEvent *event)
case GDK_DROP_START:
case GDK_DROP_FINISHED:
return event->dnd.time;
case GDK_PAD_BUTTON_PRESS:
case GDK_PAD_BUTTON_RELEASE:
return event->pad_button.time;
case GDK_PAD_RING:
case GDK_PAD_STRIP:
return event->pad_axis.time;
case GDK_PAD_GROUP_MODE:
return event->pad_group_mode.time;
case GDK_CLIENT_EVENT:
case GDK_VISIBILITY_NOTIFY:
case GDK_CONFIGURE:
@@ -1023,6 +1031,11 @@ gdk_event_get_state (const GdkEvent *event,
case GDK_SETTING:
case GDK_OWNER_CHANGE:
case GDK_GRAB_BROKEN:
case GDK_PAD_BUTTON_PRESS:
case GDK_PAD_BUTTON_RELEASE:
case GDK_PAD_RING:
case GDK_PAD_STRIP:
case GDK_PAD_GROUP_MODE:
case GDK_EVENT_LAST:
/* no state field */
break;
@@ -1213,6 +1226,10 @@ gdk_event_get_button (const GdkEvent *event,
case GDK_BUTTON_RELEASE:
number = event->button.button;
break;
case GDK_PAD_BUTTON_PRESS:
case GDK_PAD_BUTTON_RELEASE:
number = event->pad_button.button;
break;
default:
fetched = FALSE;
break;

View File

@@ -142,6 +142,9 @@ typedef struct _GdkEventSetting GdkEventSetting;
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
typedef struct _GdkEventTouchpadSwipe GdkEventTouchpadSwipe;
typedef struct _GdkEventTouchpadPinch GdkEventTouchpadPinch;
typedef struct _GdkEventPadButton GdkEventPadButton;
typedef struct _GdkEventPadAxis GdkEventPadAxis;
typedef struct _GdkEventPadGroupMode GdkEventPadGroupMode;
typedef struct _GdkEventSequence GdkEventSequence;
@@ -278,6 +281,16 @@ typedef GdkFilterReturn (*GdkFilterFunc) (GdkXEvent *xevent,
* is determined by its phase field. This event type was added in 3.18.
* @GDK_TOUCHPAD_PINCH: A touchpad pinch gesture event, the current state
* is determined by its phase field. This event type was added in 3.18.
* @GDK_PAD_BUTTON_PRESS: A tablet pad button press event. This event type
* was added in 3.22.
* @GDK_PAD_BUTTON_RELEASE: A tablet pad button release event. This event type
* was added in 3.22.
* @GDK_PAD_RING: A tablet pad axis event from a "ring". This event type was
* added in 3.22.
* @GDK_PAD_STRIP: A tablet pad axis event from a "strip". This event type was
* added in 3.22.
* @GDK_PAD_GROUP_MODE: A tablet pad group mode change. This event type was
* added in 3.22.
* @GDK_EVENT_LAST: marks the end of the GdkEventType enumeration. Added in 2.18
*
* Specifies the type of the event.
@@ -340,6 +353,11 @@ typedef enum
GDK_TOUCH_CANCEL = 40,
GDK_TOUCHPAD_SWIPE = 41,
GDK_TOUCHPAD_PINCH = 42,
GDK_PAD_BUTTON_PRESS = 43,
GDK_PAD_BUTTON_RELEASE = 44,
GDK_PAD_RING = 45,
GDK_PAD_STRIP = 46,
GDK_PAD_GROUP_MODE = 47,
GDK_EVENT_LAST /* helper variable for decls */
} GdkEventType;
@@ -1240,6 +1258,86 @@ struct _GdkEventTouchpadPinch {
guint state;
};
/**
* GdkEventPadButton:
* @type: the type of the event (%GDK_PAD_BUTTON_PRESS or %GDK_PAD_BUTTON_RELEASE).
* @window: the window which received the event.
* @send_event: %TRUE if the event was sent explicitly.
* @time: the time of the event in milliseconds.
* @group: the pad group the button belongs to. A %GDK_SOURCE_TABLET_PAD device
* may have one or more groups containing a set of buttons/rings/strips each.
* @button: The pad button that was pressed.
* @mode: The current mode of @group. Different groups in a %GDK_SOURCE_TABLET_PAD
* device may have different current modes.
*
* Generated during %GDK_SOURCE_TABLET_PAD button presses and releases.
*
* Since: 3.22
*/
struct _GdkEventPadButton {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
guint group;
guint button;
guint mode;
};
/**
* GdkEventPadAxis:
* @type: the type of the event (%GDK_PAD_RING or %GDK_PAD_STRIP).
* @window: the window which received the event.
* @send_event: %TRUE if the event was sent explicitly.
* @time: the time of the event in milliseconds.
* @group: the pad group the ring/strip belongs to. A %GDK_SOURCE_TABLET_PAD
* device may have one or more groups containing a set of buttons/rings/strips
* each.
* @index: number of strip/ring that was interacted. This number is 0-indexed.
* @mode: The current mode of @group. Different groups in a %GDK_SOURCE_TABLET_PAD
* device may have different current modes.
* @value: The current value for the given axis.
*
* Generated during %GDK_SOURCE_TABLET_PAD interaction with tactile sensors.
*
* Since: 3.22
*/
struct _GdkEventPadAxis {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
guint group;
guint index;
guint mode;
gdouble value;
};
/**
* GdkEventPadGroupMode:
* @type: the type of the event (%GDK_PAD_GROUP_MODE).
* @window: the window which received the event.
* @send_event: %TRUE if the event was sent explicitly.
* @time: the time of the event in milliseconds.
* @group: the pad group that is switching mode. A %GDK_SOURCE_TABLET_PAD
* device may have one or more groups containing a set of buttons/rings/strips
* each.
* @mode: The new mode of @group. Different groups in a %GDK_SOURCE_TABLET_PAD
* device may have different current modes.
*
* Generated during %GDK_SOURCE_TABLET_PAD mode switches in a group.
*
* Since: 3.22
*/
struct _GdkEventPadGroupMode {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
guint group;
guint mode;
};
/**
* GdkEvent:
* @type: the #GdkEventType
@@ -1262,6 +1360,11 @@ struct _GdkEventTouchpadPinch {
* @window_state: a #GdkEventWindowState
* @setting: a #GdkEventSetting
* @grab_broken: a #GdkEventGrabBroken
* @touchpad_swipe: a #GdkEventTouchpadSwipe
* @touchpad_pinch: a #GdkEventTouchpadPinch
* @pad_button: a #GdkEventPadButton
* @pad_axis: a #GdkEventPadAxis
* @pad_group_mode: a #GdkEventPadGroupMode
*
* A #GdkEvent contains a union of all of the event types,
* and allows access to the data fields in a number of ways.
@@ -1318,6 +1421,9 @@ union _GdkEvent
GdkEventGrabBroken grab_broken;
GdkEventTouchpadSwipe touchpad_swipe;
GdkEventTouchpadPinch touchpad_pinch;
GdkEventPadButton pad_button;
GdkEventPadAxis pad_axis;
GdkEventPadGroupMode pad_group_mode;
};
GDK_AVAILABLE_IN_ALL

View File

@@ -394,6 +394,7 @@ typedef enum
* @GDK_TOUCH_MASK: receive touch events. Since 3.4
* @GDK_SMOOTH_SCROLL_MASK: receive smooth scrolling events. Since 3.4
@GDK_TOUCHPAD_GESTURE_MASK: receive touchpad gesture events. Since 3.18
* @GDK_TABLET_PAD_MASK: receive tablet pad events. Since 3.22
* @GDK_ALL_EVENTS_MASK: the combination of all the above event masks.
*
* A set of bit-flags to indicate which events a window is to receive.
@@ -449,6 +450,7 @@ typedef enum
GDK_TOUCH_MASK = 1 << 22,
GDK_SMOOTH_SCROLL_MASK = 1 << 23,
GDK_TOUCHPAD_GESTURE_MASK = 1 << 24,
GDK_TABLET_PAD_MASK = 1 << 25,
GDK_ALL_EVENTS_MASK = 0xFFFFFE
} GdkEventMask;

View File

@@ -7960,6 +7960,11 @@ static const guint type_masks[] = {
GDK_TOUCH_MASK, /* GDK_TOUCH_CANCEL = 40 */
GDK_TOUCHPAD_GESTURE_MASK, /* GDK_TOUCHPAD_SWIPE = 41 */
GDK_TOUCHPAD_GESTURE_MASK, /* GDK_TOUCHPAD_PINCH = 42 */
GDK_TABLET_PAD_MASK, /* GDK_PAD_BUTTON_PRESS = 43 */
GDK_TABLET_PAD_MASK, /* GDK_PAD_BUTTON_RELEASE = 44 */
GDK_TABLET_PAD_MASK, /* GDK_PAD_RING = 45 */
GDK_TABLET_PAD_MASK, /* GDK_PAD_STRIP = 46 */
GDK_TABLET_PAD_MASK, /* GDK_PAD_GROUP_MODE = 47 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
@@ -9855,7 +9860,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
if (device)
{
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD &&
gdk_device_get_source (device) != GDK_SOURCE_TABLET_PAD)
{
pointer_info = _gdk_display_get_pointer_info (display, device);

View File

@@ -29,6 +29,7 @@
#include "gdkwayland.h"
#include "gdkkeysyms.h"
#include "gdkdeviceprivate.h"
#include "gdkdevicepadprivate.h"
#include "gdkdevicetoolprivate.h"
#include "gdkdevicemanagerprivate.h"
#include "gdkseatprivate.h"
@@ -45,11 +46,16 @@
#define BUTTON_BASE (BTN_LEFT - 1) /* Used to translate to 1-indexed buttons */
typedef struct _GdkWaylandDevicePad GdkWaylandDevicePad;
typedef struct _GdkWaylandDevicePadClass GdkWaylandDevicePadClass;
typedef struct _GdkWaylandTouchData GdkWaylandTouchData;
typedef struct _GdkWaylandPointerFrameData GdkWaylandPointerFrameData;
typedef struct _GdkWaylandPointerData GdkWaylandPointerData;
typedef struct _GdkWaylandTabletData GdkWaylandTabletData;
typedef struct _GdkWaylandTabletToolData GdkWaylandTabletToolData;
typedef struct _GdkWaylandTabletPadGroupData GdkWaylandTabletPadGroupData;
typedef struct _GdkWaylandTabletPadData GdkWaylandTabletPadData;
struct _GdkWaylandTouchData
{
@@ -112,6 +118,42 @@ struct _GdkWaylandTabletToolData
GdkWaylandTabletData *current_tablet;
};
struct _GdkWaylandTabletPadGroupData
{
GdkWaylandTabletPadData *pad;
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group;
GList *rings;
GList *strips;
GList *buttons;
guint mode_switch_serial;
guint n_modes;
guint current_mode;
struct {
guint source;
gboolean is_stop;
gdouble value;
} axis_tmp_info;
};
struct _GdkWaylandTabletPadData
{
GdkSeat *seat;
struct zwp_tablet_pad_v2 *wp_tablet_pad;
GdkDevice *device;
GdkWaylandTabletData *current_tablet;
guint enter_serial;
uint32_t n_buttons;
gchar *path;
GList *rings;
GList *strips;
GList *mode_groups;
};
struct _GdkWaylandTabletData
{
struct zwp_tablet_v2 *wp_tablet;
@@ -127,6 +169,8 @@ struct _GdkWaylandTabletData
GdkSeat *seat;
GdkWaylandPointerData pointer_info;
GList *pads;
GdkWaylandTabletToolData *current_tool;
gint axis_indices[GDK_AXIS_LAST];
@@ -164,6 +208,7 @@ struct _GdkWaylandSeat
GHashTable *touches;
GList *tablets;
GList *tablet_tools;
GList *tablet_pads;
GdkWaylandPointerData pointer_info;
GdkWaylandPointerData touch_info;
@@ -216,6 +261,25 @@ struct _GdkWaylandDeviceClass
G_DEFINE_TYPE (GdkWaylandDevice, gdk_wayland_device, GDK_TYPE_DEVICE)
struct _GdkWaylandDevicePad
{
GdkWaylandDevice parent_instance;
};
struct _GdkWaylandDevicePadClass
{
GdkWaylandDeviceClass parent_class;
};
static void gdk_wayland_device_pad_iface_init (GdkDevicePadInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GdkWaylandDevicePad, gdk_wayland_device_pad,
GDK_TYPE_WAYLAND_DEVICE,
G_IMPLEMENT_INTERFACE (GDK_TYPE_DEVICE_PAD,
gdk_wayland_device_pad_iface_init))
#define GDK_TYPE_WAYLAND_DEVICE_PAD (gdk_wayland_device_pad_get_type ())
#define GDK_TYPE_WAYLAND_DEVICE_MANAGER (gdk_wayland_device_manager_get_type ())
#define GDK_WAYLAND_DEVICE_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_WAYLAND_DEVICE_MANAGER, GdkWaylandDeviceManager))
#define GDK_WAYLAND_DEVICE_MANAGER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_WAYLAND_DEVICE_MANAGER, GdkWaylandDeviceManagerClass))
@@ -308,6 +372,24 @@ gdk_wayland_device_manager_find_tablet (GdkWaylandSeat *seat,
return NULL;
}
static GdkWaylandTabletPadData *
gdk_wayland_device_manager_find_pad (GdkWaylandSeat *seat,
GdkDevice *device)
{
GList *l;
for (l = seat->tablet_pads; l; l = l->next)
{
GdkWaylandTabletPadData *pad = l->data;
if (pad->device == device)
return pad;
}
return NULL;
}
static gboolean
gdk_wayland_device_update_window_cursor (GdkDevice *device)
{
@@ -790,6 +872,131 @@ gdk_wayland_device_init (GdkWaylandDevice *device_core)
_gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
}
static gint
gdk_wayland_device_pad_get_n_groups (GdkDevicePad *pad)
{
GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (pad));
GdkWaylandTabletPadData *data;
data = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat),
GDK_DEVICE (pad));
g_assert (data != NULL);
return g_list_length (data->mode_groups);
}
static gint
gdk_wayland_device_pad_get_group_n_modes (GdkDevicePad *pad,
gint n_group)
{
GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (pad));
GdkWaylandTabletPadGroupData *group;
GdkWaylandTabletPadData *data;
data = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat),
GDK_DEVICE (pad));
g_assert (data != NULL);
group = g_list_nth_data (data->mode_groups, n_group);
if (!group)
return -1;
return group->n_modes;
}
static gint
gdk_wayland_device_pad_get_n_features (GdkDevicePad *pad,
GdkDevicePadFeature feature)
{
GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (pad));
GdkWaylandTabletPadData *data;
data = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat),
GDK_DEVICE (pad));
g_assert (data != NULL);
switch (feature)
{
case GDK_DEVICE_PAD_FEATURE_BUTTON:
return data->n_buttons;
case GDK_DEVICE_PAD_FEATURE_RING:
return g_list_length (data->rings);
case GDK_DEVICE_PAD_FEATURE_STRIP:
return g_list_length (data->strips);
default:
return -1;
}
}
static gint
gdk_wayland_device_pad_get_feature_group (GdkDevicePad *pad,
GdkDevicePadFeature feature,
gint idx)
{
GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (pad));
GdkWaylandTabletPadGroupData *group;
GdkWaylandTabletPadData *data;
GList *l;
gint i;
data = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat),
GDK_DEVICE (pad));
g_assert (data != NULL);
for (l = data->mode_groups, i = 0; l; l = l->next, i++)
{
group = l->data;
switch (feature)
{
case GDK_DEVICE_PAD_FEATURE_BUTTON:
if (g_list_find (group->buttons, GINT_TO_POINTER (idx)))
return i;
break;
case GDK_DEVICE_PAD_FEATURE_RING:
{
gpointer ring;
ring = g_list_nth_data (data->rings, idx);
if (ring && g_list_find (group->rings, ring))
return i;
break;
}
case GDK_DEVICE_PAD_FEATURE_STRIP:
{
gpointer strip;
strip = g_list_nth_data (data->strips, idx);
if (strip && g_list_find (group->strips, strip))
return i;
break;
}
default:
break;
}
}
return -1;
}
static void
gdk_wayland_device_pad_iface_init (GdkDevicePadInterface *iface)
{
iface->get_n_groups = gdk_wayland_device_pad_get_n_groups;
iface->get_group_n_modes = gdk_wayland_device_pad_get_group_n_modes;
iface->get_n_features = gdk_wayland_device_pad_get_n_features;
iface->get_feature_group = gdk_wayland_device_pad_get_feature_group;
}
static void
gdk_wayland_device_pad_class_init (GdkWaylandDevicePadClass *klass)
{
}
static void
gdk_wayland_device_pad_init (GdkWaylandDevicePad *pad)
{
}
/**
* gdk_wayland_device_get_wl_seat:
* @device: (type GdkWaylandDevice): a #GdkDevice
@@ -2551,6 +2758,43 @@ _gdk_wayland_seat_remove_tablet (GdkWaylandSeat *seat,
g_free (tablet);
}
static void
_gdk_wayland_seat_remove_tablet_pad (GdkWaylandSeat *seat,
GdkWaylandTabletPadData *pad)
{
GdkWaylandDeviceManager *device_manager =
GDK_WAYLAND_DEVICE_MANAGER (seat->device_manager);
seat->tablet_pads = g_list_remove (seat->tablet_pads, pad);
device_manager->devices =
g_list_remove (device_manager->devices, pad->device);
g_signal_emit_by_name (device_manager, "device-removed", pad->device);
_gdk_device_set_associated_device (pad->device, NULL);
g_object_unref (pad->device);
g_free (pad);
}
static GdkWaylandTabletPadGroupData *
tablet_pad_lookup_button_group (GdkWaylandTabletPadData *pad,
uint32_t button)
{
GdkWaylandTabletPadGroupData *group;
GList *l;
for (l = pad->mode_groups; l; l = l->next)
{
group = l->data;
if (g_list_find (group->buttons, GUINT_TO_POINTER (button)))
return group;
}
return NULL;
}
static void
tablet_handle_name (void *data,
struct zwp_tablet_v2 *wp_tablet,
@@ -3105,7 +3349,9 @@ tablet_tool_handle_done (void *data,
{
GdkWaylandTabletToolData *tool = data;
tool->tool = gdk_device_tool_new (tool->hardware_serial, tool->type, tool->axes);
tool->tool = gdk_device_tool_new (tool->hardware_serial,
tool->hardware_id_wacom,
tool->type, tool->axes);
gdk_seat_tool_added (tool->seat, tool->tool);
}
@@ -3617,6 +3863,437 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
tablet_tool_handle_frame,
};
static void
tablet_pad_ring_handle_source (void *data,
struct zwp_tablet_pad_ring_v2 *wp_tablet_pad_ring,
uint32_t source)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s ring = %p source = %d",
G_STRFUNC, wp_tablet_pad_ring, source);
group->axis_tmp_info.source = source;
}
static void
tablet_pad_ring_handle_angle (void *data,
struct zwp_tablet_pad_ring_v2 *wp_tablet_pad_ring,
wl_fixed_t angle)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s ring = %p angle = %f",
G_STRFUNC, wp_tablet_pad_ring, wl_fixed_to_double (angle));
group->axis_tmp_info.value = wl_fixed_to_double (angle);
}
static void
tablet_pad_ring_handle_stop (void *data,
struct zwp_tablet_pad_ring_v2 *wp_tablet_pad_ring)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s ring = %p", G_STRFUNC, wp_tablet_pad_ring);
group->axis_tmp_info.is_stop = TRUE;
}
static void
tablet_pad_ring_handle_frame (void *data,
struct zwp_tablet_pad_ring_v2 *wp_tablet_pad_ring,
uint32_t time)
{
GdkWaylandTabletPadGroupData *group = data;
GdkWaylandTabletPadData *pad = group->pad;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (pad->seat);
GdkEvent *event;
g_debug (G_STRLOC ": %s ring = %p", G_STRFUNC, wp_tablet_pad_ring);
event = gdk_event_new (GDK_PAD_RING);
g_set_object (&event->pad_axis.window, seat->keyboard_focus);
event->pad_axis.time = time;
event->pad_axis.group = g_list_index (pad->mode_groups, group);
event->pad_axis.index = g_list_index (pad->rings, wp_tablet_pad_ring);
event->pad_axis.mode = group->current_mode;
event->pad_axis.value = group->axis_tmp_info.value;
gdk_event_set_device (event, pad->device);
gdk_event_set_source_device (event, pad->device);
_gdk_wayland_display_deliver_event (gdk_seat_get_display (pad->seat),
event);
}
static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = {
tablet_pad_ring_handle_source,
tablet_pad_ring_handle_angle,
tablet_pad_ring_handle_stop,
tablet_pad_ring_handle_frame,
};
static void
tablet_pad_strip_handle_source (void *data,
struct zwp_tablet_pad_strip_v2 *wp_tablet_pad_strip,
uint32_t source)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s strip = %p source = %d",
G_STRFUNC, wp_tablet_pad_strip, source);
group->axis_tmp_info.source = source;
}
static void
tablet_pad_strip_handle_position (void *data,
struct zwp_tablet_pad_strip_v2 *wp_tablet_pad_strip,
uint32_t position)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s strip = %p position = %d",
G_STRFUNC, wp_tablet_pad_strip, position);
group->axis_tmp_info.value = (gdouble) position / 65535;
}
static void
tablet_pad_strip_handle_stop (void *data,
struct zwp_tablet_pad_strip_v2 *wp_tablet_pad_strip)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s strip = %p",
G_STRFUNC, wp_tablet_pad_strip);
group->axis_tmp_info.is_stop = TRUE;
}
static void
tablet_pad_strip_handle_frame (void *data,
struct zwp_tablet_pad_strip_v2 *wp_tablet_pad_strip,
uint32_t time)
{
GdkWaylandTabletPadGroupData *group = data;
GdkWaylandTabletPadData *pad = group->pad;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (pad->seat);
GdkEvent *event;
g_debug (G_STRLOC ": %s strip = %p",
G_STRFUNC, wp_tablet_pad_strip);
event = gdk_event_new (GDK_PAD_STRIP);
g_set_object (&event->pad_axis.window, seat->keyboard_focus);
event->pad_axis.time = time;
event->pad_axis.group = g_list_index (pad->mode_groups, group);
event->pad_axis.index = g_list_index (pad->strips, wp_tablet_pad_strip);
event->pad_axis.mode = group->current_mode;
event->pad_axis.value = group->axis_tmp_info.value;
gdk_event_set_device (event, pad->device);
gdk_event_set_source_device (event, pad->device);
_gdk_wayland_display_deliver_event (gdk_seat_get_display (pad->seat),
event);
}
static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = {
tablet_pad_strip_handle_source,
tablet_pad_strip_handle_position,
tablet_pad_strip_handle_stop,
tablet_pad_strip_handle_frame,
};
static void
tablet_pad_group_handle_buttons (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group,
struct wl_array *buttons)
{
GdkWaylandTabletPadGroupData *group = data;
uint32_t *p;
g_debug (G_STRLOC ": %s pad group = %p, n_buttons = %ld",
G_STRFUNC, wp_tablet_pad_group, buttons->size);
wl_array_for_each (p, buttons)
{
group->buttons = g_list_prepend (group->buttons, GUINT_TO_POINTER (*p));
}
group->buttons = g_list_reverse (group->buttons);
}
static void
tablet_pad_group_handle_ring (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group,
struct zwp_tablet_pad_ring_v2 *wp_tablet_pad_ring)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s pad group = %p, ring = %p",
G_STRFUNC, wp_tablet_pad_group, wp_tablet_pad_ring);
zwp_tablet_pad_ring_v2_add_listener (wp_tablet_pad_ring,
&tablet_pad_ring_listener, group);
zwp_tablet_pad_ring_v2_set_user_data (wp_tablet_pad_ring, group);
group->rings = g_list_append (group->rings, wp_tablet_pad_ring);
group->pad->rings = g_list_append (group->pad->rings, wp_tablet_pad_ring);
}
static void
tablet_pad_group_handle_strip (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group,
struct zwp_tablet_pad_strip_v2 *wp_tablet_pad_strip)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s pad group = %p, strip = %p",
G_STRFUNC, wp_tablet_pad_group, wp_tablet_pad_strip);
zwp_tablet_pad_strip_v2_add_listener (wp_tablet_pad_strip,
&tablet_pad_strip_listener, group);
zwp_tablet_pad_strip_v2_set_user_data (wp_tablet_pad_strip, group);
group->strips = g_list_append (group->strips, wp_tablet_pad_strip);
group->pad->strips = g_list_append (group->pad->strips, wp_tablet_pad_strip);
}
static void
tablet_pad_group_handle_modes (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group,
uint32_t modes)
{
GdkWaylandTabletPadGroupData *group = data;
g_debug (G_STRLOC ": %s pad group = %p, n_modes = %d",
G_STRFUNC, wp_tablet_pad_group, modes);
group->n_modes = modes;
}
static void
tablet_pad_group_handle_done (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group)
{
g_debug (G_STRLOC ": %s pad group = %p", G_STRFUNC, wp_tablet_pad_group);
}
static void
tablet_pad_group_handle_mode (void *data,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group,
uint32_t time,
uint32_t serial,
uint32_t mode)
{
GdkWaylandTabletPadGroupData *group = data;
GdkWaylandTabletPadData *pad = group->pad;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (pad->seat);
GdkEvent *event;
guint n_group;
g_debug (G_STRLOC ": %s pad group = %p, mode = %d",
G_STRFUNC, wp_tablet_pad_group, mode);
group->mode_switch_serial = serial;
group->current_mode = mode;
n_group = g_list_index (pad->mode_groups, group);
event = gdk_event_new (GDK_PAD_GROUP_MODE);
g_set_object (&event->pad_button.window, seat->keyboard_focus);
event->pad_group_mode.group = n_group;
event->pad_group_mode.mode = mode;
event->pad_group_mode.time = time;
gdk_event_set_device (event, pad->device);
gdk_event_set_source_device (event, pad->device);
_gdk_wayland_display_deliver_event (gdk_seat_get_display (pad->seat),
event);
}
static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
tablet_pad_group_handle_buttons,
tablet_pad_group_handle_ring,
tablet_pad_group_handle_strip,
tablet_pad_group_handle_modes,
tablet_pad_group_handle_done,
tablet_pad_group_handle_mode,
};
static void
tablet_pad_handle_group (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
struct zwp_tablet_pad_group_v2 *wp_tablet_pad_group)
{
GdkWaylandTabletPadData *pad = data;
GdkWaylandTabletPadGroupData *group;
g_debug (G_STRLOC ": %s pad group = %p, group = %p",
G_STRFUNC, wp_tablet_pad_group, wp_tablet_pad_group);
group = g_new0 (GdkWaylandTabletPadGroupData, 1);
group->wp_tablet_pad_group = wp_tablet_pad_group;
group->pad = pad;
zwp_tablet_pad_group_v2_add_listener (wp_tablet_pad_group,
&tablet_pad_group_listener, group);
zwp_tablet_pad_group_v2_set_user_data (wp_tablet_pad_group, group);
pad->mode_groups = g_list_append (pad->mode_groups, group);
}
static void
tablet_pad_handle_path (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
const char *path)
{
GdkWaylandTabletPadData *pad = data;
g_debug (G_STRLOC ": %s pad = %p, path = %s",
G_STRFUNC, wp_tablet_pad, path);
pad->path = g_strdup (path);
}
static void
tablet_pad_handle_buttons (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
uint32_t buttons)
{
GdkWaylandTabletPadData *pad = data;
g_debug (G_STRLOC ": %s pad = %p, n_buttons = %d",
G_STRFUNC, wp_tablet_pad, buttons);
pad->n_buttons = buttons;
}
static void
tablet_pad_handle_done (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad)
{
GdkWaylandTabletPadData *pad = data;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (pad->seat);
GdkWaylandDeviceManager *device_manager =
GDK_WAYLAND_DEVICE_MANAGER (seat->device_manager);
g_debug (G_STRLOC ": %s pad = %p", G_STRFUNC, wp_tablet_pad);
pad->device =
g_object_new (GDK_TYPE_WAYLAND_DEVICE_PAD,
"name", "Pad device",
"type", GDK_DEVICE_TYPE_SLAVE,
"input-source", GDK_SOURCE_TABLET_PAD,
"input-mode", GDK_MODE_SCREEN,
"display", gdk_seat_get_display (pad->seat),
"device-manager", device_manager,
"seat", seat,
NULL);
_gdk_device_set_associated_device (pad->device, seat->master_keyboard);
g_signal_emit_by_name (device_manager, "device-added", pad->device);
}
static void
tablet_pad_handle_button (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
uint32_t time,
uint32_t button,
uint32_t state)
{
GdkWaylandTabletPadData *pad = data;
GdkWaylandTabletPadGroupData *group;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (pad->seat);
GdkEvent *event;
gint n_group;
g_debug (G_STRLOC ": %s pad = %p, button = %d, state = %d",
G_STRFUNC, wp_tablet_pad, button, state);
group = tablet_pad_lookup_button_group (pad, button);
n_group = g_list_index (pad->mode_groups, group);
event = gdk_event_new (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED ?
GDK_PAD_BUTTON_PRESS :
GDK_PAD_BUTTON_RELEASE);
g_set_object (&event->pad_button.window, seat->keyboard_focus);
event->pad_button.button = button;
event->pad_button.group = n_group;
event->pad_button.mode = group->current_mode;
event->pad_button.time = time;
gdk_event_set_device (event, pad->device);
gdk_event_set_source_device (event, pad->device);
_gdk_wayland_display_deliver_event (gdk_seat_get_display (pad->seat),
event);
}
static void
tablet_pad_handle_enter (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
uint32_t serial,
struct zwp_tablet_v2 *wp_tablet,
struct wl_surface *surface)
{
GdkWaylandTabletPadData *pad = data;
GdkWaylandTabletData *tablet = zwp_tablet_v2_get_user_data (wp_tablet);
g_debug (G_STRLOC ": %s pad = %p, tablet = %p surface = %p",
G_STRFUNC, wp_tablet_pad, wp_tablet, surface);
/* Relate pad and tablet */
tablet->pads = g_list_prepend (tablet->pads, pad);
pad->current_tablet = tablet;
}
static void
tablet_pad_handle_leave (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad,
uint32_t serial,
struct wl_surface *surface)
{
GdkWaylandTabletPadData *pad = data;
g_debug (G_STRLOC ": %s pad = %p, surface = %p",
G_STRFUNC, wp_tablet_pad, surface);
if (pad->current_tablet)
{
pad->current_tablet->pads = g_list_remove (pad->current_tablet->pads, pad);
pad->current_tablet = NULL;
}
}
static void
tablet_pad_handle_removed (void *data,
struct zwp_tablet_pad_v2 *wp_tablet_pad)
{
GdkWaylandTabletPadData *pad = data;
g_debug (G_STRLOC ": %s pad = %p", G_STRFUNC, wp_tablet_pad);
/* Remove from the current tablet */
if (pad->current_tablet)
{
pad->current_tablet->pads = g_list_remove (pad->current_tablet->pads, pad);
pad->current_tablet = NULL;
}
_gdk_wayland_seat_remove_tablet_pad (GDK_WAYLAND_SEAT (pad->seat), pad);
}
static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
tablet_pad_handle_group,
tablet_pad_handle_path,
tablet_pad_handle_buttons,
tablet_pad_handle_done,
tablet_pad_handle_button,
tablet_pad_handle_enter,
tablet_pad_handle_leave,
tablet_pad_handle_removed,
};
static void
tablet_seat_handle_tablet_added (void *data,
struct zwp_tablet_seat_v2 *wp_tablet_seat,
@@ -3662,8 +4339,17 @@ tablet_seat_handle_pad_added (void *data,
struct zwp_tablet_seat_v2 *wp_tablet_seat,
struct zwp_tablet_pad_v2 *wp_tablet_pad)
{
/* Unhandled at the moment */
zwp_tablet_pad_v2_destroy (wp_tablet_pad);
GdkWaylandSeat *seat = data;
GdkWaylandTabletPadData *pad;
pad = g_new0 (GdkWaylandTabletPadData, 1);
pad->wp_tablet_pad = wp_tablet_pad;
pad->seat = GDK_SEAT (seat);
zwp_tablet_pad_v2_add_listener (wp_tablet_pad, &tablet_pad_listener, pad);
zwp_tablet_pad_v2_set_user_data (wp_tablet_pad, pad);
seat->tablet_pads = g_list_prepend (seat->tablet_pads, pad);
}
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
@@ -3827,6 +4513,9 @@ gdk_wayland_seat_finalize (GObject *object)
for (l = seat->tablet_tools; l != NULL; l = l->next)
_gdk_wayland_seat_remove_tool (seat, l->data);
for (l = seat->tablet_pads; l != NULL; l = l->next)
_gdk_wayland_seat_remove_tablet_pad (seat, l->data);
for (l = seat->tablets; l != NULL; l = l->next)
_gdk_wayland_seat_remove_tablet (seat, l->data);
@@ -4476,3 +5165,102 @@ gdk_wayland_device_get_drop_context (GdkDevice *device)
return GDK_WAYLAND_SEAT (seat)->drop_context;
}
/**
* gdk_wayland_device_get_node_path:
* @device: a #GdkDevice
*
* Returns the /dev/input/event* path of this device.
* For #GdkDevices that possibly coalesce multiple hardware
* devices (eg. mouse, keyboard, touch,...), this function
* will return %NULL.
*
* This is most notably implemented for devices of type
* %GDK_SOURCE_PEN, %GDK_SOURCE_ERASER and %GDK_SOURCE_TABLET_PAD.
*
* Returns: the /dev/input/event* path of this device
**/
const gchar *
gdk_wayland_device_get_node_path (GdkDevice *device)
{
GdkWaylandTabletData *tablet;
GdkWaylandTabletPadData *pad;
GdkSeat *seat;
g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
seat = gdk_device_get_seat (device);
tablet = gdk_wayland_device_manager_find_tablet (GDK_WAYLAND_SEAT (seat),
device);
if (tablet)
return tablet->path;
pad = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat), device);
if (pad)
return pad->path;
return NULL;
}
/**
* gdk_wayland_device_pad_set_feedback:
* @device: a %GDK_SOURCE_TABLET_PAD device
* @feature: Feature to set the feedback label for
* @feature_idx: 0-indexed index of the feature to set the feedback label for
* @label: Feedback label
*
* Sets the feedback label for the given feature/index. This may be used by the
* compositor to provide user feedback of the actions available/performed.
**/
void
gdk_wayland_device_pad_set_feedback (GdkDevice *device,
GdkDevicePadFeature feature,
guint feature_idx,
const gchar *label)
{
GdkWaylandTabletPadData *pad;
GdkWaylandTabletPadGroupData *group;
GdkSeat *seat;
seat = gdk_device_get_seat (device);
pad = gdk_wayland_device_manager_find_pad (GDK_WAYLAND_SEAT (seat),
device);
if (!pad)
return;
if (feature == GDK_DEVICE_PAD_FEATURE_BUTTON)
{
group = tablet_pad_lookup_button_group (pad, feature_idx);
if (!group)
return;
zwp_tablet_pad_v2_set_feedback (pad->wp_tablet_pad, feature_idx, label,
group->mode_switch_serial);
}
else if (feature == GDK_DEVICE_PAD_FEATURE_RING)
{
struct zwp_tablet_pad_ring_v2 *wp_pad_ring;
wp_pad_ring = g_list_nth_data (pad->rings, feature_idx);
if (!wp_pad_ring)
return;
group = zwp_tablet_pad_ring_v2_get_user_data (wp_pad_ring);
zwp_tablet_pad_ring_v2_set_feedback (wp_pad_ring, label,
group->mode_switch_serial);
}
else if (feature == GDK_DEVICE_PAD_FEATURE_STRIP)
{
struct zwp_tablet_pad_strip_v2 *wp_pad_strip;
wp_pad_strip = g_list_nth_data (pad->strips, feature_idx);
if (!wp_pad_strip)
return;
group = zwp_tablet_pad_strip_v2_get_user_data (wp_pad_strip);
zwp_tablet_pad_strip_v2_set_feedback (wp_pad_strip, label,
group->mode_switch_serial);
}
}

View File

@@ -55,6 +55,14 @@ struct wl_keyboard *gdk_wayland_device_get_wl_keyboard (GdkDevice *device);
GDK_AVAILABLE_IN_3_20
struct wl_seat *gdk_wayland_seat_get_wl_seat (GdkSeat *seat);
GDK_AVAILABLE_IN_3_22
const gchar *gdk_wayland_device_get_node_path (GdkDevice *device);
GDK_AVAILABLE_IN_3_22
void gdk_wayland_device_pad_set_feedback (GdkDevice *device,
GdkDevicePadFeature element,
guint idx,
const gchar *label);
G_END_DECLS

View File

@@ -1030,7 +1030,7 @@ handle_property_change (GdkX11DeviceManagerXI2 *device_manager,
if (!tool && serial_id > 0)
{
tool = gdk_device_tool_new (serial_id,
tool = gdk_device_tool_new (serial_id, 0,
GDK_DEVICE_TOOL_TYPE_UNKNOWN, 0);
gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), tool);
}

View File

@@ -250,6 +250,7 @@ gtk_public_h_sources = \
gtkoffscreenwindow.h \
gtkorientable.h \
gtkoverlay.h \
gtkpadcontroller.h \
gtkpagesetup.h \
gtkpaned.h \
gtkpapersize.h \
@@ -829,6 +830,7 @@ gtk_base_c_sources = \
gtkoffscreenwindow.c \
gtkorientable.c \
gtkoverlay.c \
gtkpadcontroller.c \
gtkpagesetup.c \
gtkpaned.c \
gtkpango.c \

View File

@@ -152,6 +152,7 @@
#include <gtk/gtkoffscreenwindow.h>
#include <gtk/gtkorientable.h>
#include <gtk/gtkoverlay.h>
#include <gtk/gtkpadcontroller.h>
#include <gtk/gtkpagesetup.h>
#include <gtk/gtkpapersize.h>
#include <gtk/gtkpaned.h>

View File

@@ -37,6 +37,9 @@ struct _GtkEventControllerClass
/*<private>*/
/* Tells whether the event is filtered out, %TRUE makes
* the event unseen by the handle_event vfunc.
*/
gboolean (* filter_event) (GtkEventController *controller,
const GdkEvent *event);
gpointer padding[10];

View File

@@ -1875,6 +1875,11 @@ gtk_main_do_event (GdkEvent *event)
case GDK_TOUCH_CANCEL:
case GDK_TOUCHPAD_SWIPE:
case GDK_TOUCHPAD_PINCH:
case GDK_PAD_BUTTON_PRESS:
case GDK_PAD_BUTTON_RELEASE:
case GDK_PAD_RING:
case GDK_PAD_STRIP:
case GDK_PAD_GROUP_MODE:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;

520
gtk/gtkpadcontroller.c Normal file
View File

@@ -0,0 +1,520 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2016, 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:gtkpadcontroller
* @Short_description: Controller for drawing tablet pads
* @Title: GtkPadController
* @See_also: #GtkEventController, #GdkDevicePad
*
* #GtkPadController is an event controller for the pads found in drawing
* tablets (The collection of buttons and tactile sensors often found around
* the stylus-sensitive area).
*
* These buttons and sensors have no implicit meaning, and by default they
* perform no action, this event controller is provided to map those to
* #GAction objects, thus letting the application give those a more semantic
* meaning.
*
* Buttons and sensors are not constrained to triggering a single action, some
* %GDK_SOURCE_TABLET_PAD devices feature multiple "modes", all these input
* elements have one current mode, which may determine the final action
* being triggered. Pad devices often divide buttons and sensors into groups,
* all elements in a group share the same current mode, but different groups
* may have different modes. See gdk_device_pad_get_n_groups() and
* gdk_device_pad_get_group_n_modes().
*
* Each of the actions that a given button/strip/ring performs for a given
* mode is defined by #GtkPadActionEntry, it contains an action name that
* will be looked up in the given #GActionGroup and activated whenever the
* specified input element and mode are triggered.
*
* A simple example of #GtkPadController usage, assigning button 1 in all
* modes and pad devices to an "invert-selection" action:
* |[
* GtkPadActionEntry *pad_actions[] = {
* { GTK_PAD_ACTION_BUTTON, 1, -1, "Invert selection", "pad-actions.invert-selection" },
* …
* };
*
* …
* action_group = g_simple_action_group_new ();
* action = g_simple_action_new ("pad-actions.invert-selection", NULL);
* g_signal_connect (action, "activate", on_invert_selection_activated, NULL);
* g_action_map_add_action (G_ACTION_MAP (action_group), action);
* …
* pad_controller = gtk_pad_controller_new (window, action_group, NULL);
* ]|
*
* The actions belonging to rings/strips will be activated with a parameter
* of type %G_VARIANT_TYPE_DOUBLE bearing the value of the given axis, it
* is recommended that those are made stateful and accepting this #GVariantType.
*/
#include "config.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkpadcontroller.h"
#include "gtkwindow.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif
struct _GtkPadController {
GtkEventController parent_instance;
GActionGroup *action_group;
GdkDevice *pad;
GList *entries;
};
struct _GtkPadControllerClass {
GtkEventControllerClass parent_class;
};
enum {
PROP_0,
PROP_ACTION_GROUP,
PROP_PAD,
N_PROPS
};
static GParamSpec *pspecs[N_PROPS] = { NULL };
G_DEFINE_TYPE (GtkPadController, gtk_pad_controller, GTK_TYPE_EVENT_CONTROLLER)
static GtkPadActionEntry *
gtk_pad_action_entry_copy (const GtkPadActionEntry *entry)
{
GtkPadActionEntry *copy;
copy = g_slice_new0 (GtkPadActionEntry);
*copy = *entry;
copy->label = g_strdup (entry->label);
copy->action_name = g_strdup (entry->action_name);
return copy;
}
static void
gtk_pad_action_entry_free (GtkPadActionEntry *entry)
{
g_free (entry->label);
g_free (entry->action_name);
g_slice_free (GtkPadActionEntry, entry);
}
static const GtkPadActionEntry *
gtk_pad_action_find_match (GtkPadController *controller,
GtkPadActionType type,
gint index,
gint mode)
{
GList *l;
for (l = controller->entries; l; l = l->next)
{
GtkPadActionEntry *entry = l->data;
gboolean match_index = FALSE, match_mode = FALSE;
if (entry->type != type)
continue;
match_index = entry->index < 0 || entry->index == index;
match_mode = entry->mode < 0 || entry->mode == mode;
if (match_index && match_mode)
return entry;
}
return NULL;
}
static void
gtk_pad_controller_activate_action (GtkPadController *controller,
const GtkPadActionEntry *entry)
{
g_action_group_activate_action (controller->action_group,
entry->action_name,
NULL);
}
static void
gtk_pad_controller_activate_action_with_axis (GtkPadController *controller,
const GtkPadActionEntry *entry,
gdouble value)
{
g_action_group_activate_action (controller->action_group,
entry->action_name,
g_variant_new_double (value));
}
static void
gtk_pad_controller_handle_mode_switch (GtkPadController *controller,
GdkDevice *pad,
guint group,
guint mode)
{
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gdk_device_get_display (pad)))
{
const GtkPadActionEntry *entry;
gint elem, idx, n_features;
for (elem = GTK_PAD_ACTION_BUTTON; elem <= GTK_PAD_ACTION_STRIP; elem++)
{
n_features = gdk_device_pad_get_n_features (GDK_DEVICE_PAD (pad),
elem);
for (idx = 0; idx < n_features; idx++)
{
if (gdk_device_pad_get_feature_group (GDK_DEVICE_PAD (pad),
elem, idx) != group)
continue;
entry = gtk_pad_action_find_match (controller, elem, idx, mode);
if (!entry)
continue;
if (!g_action_group_has_action (controller->action_group,
entry->action_name))
continue;
gdk_wayland_device_pad_set_feedback (pad, elem, idx,
g_dgettext (NULL, entry->label));
}
}
}
#endif
}
static gboolean
gtk_pad_controller_filter_event (GtkEventController *controller,
const GdkEvent *event)
{
GtkPadController *pad_controller = GTK_PAD_CONTROLLER (controller);
if (event->type != GDK_PAD_BUTTON_PRESS &&
event->type != GDK_PAD_BUTTON_RELEASE &&
event->type != GDK_PAD_RING &&
event->type != GDK_PAD_STRIP &&
event->type != GDK_PAD_GROUP_MODE)
return TRUE;
if (pad_controller->pad &&
gdk_event_get_source_device (event) != pad_controller->pad)
return TRUE;
return FALSE;
}
static gboolean
gtk_pad_controller_handle_event (GtkEventController *controller,
const GdkEvent *event)
{
GtkPadController *pad_controller = GTK_PAD_CONTROLLER (controller);
const GtkPadActionEntry *entry;
GtkPadActionType type;
gint index, mode;
if (event->type == GDK_PAD_GROUP_MODE)
{
gtk_pad_controller_handle_mode_switch (pad_controller,
gdk_event_get_source_device (event),
event->pad_group_mode.group,
event->pad_group_mode.mode);
return GDK_EVENT_PROPAGATE;
}
switch (event->type)
{
case GDK_PAD_BUTTON_PRESS:
type = GTK_PAD_ACTION_BUTTON;
index = event->pad_button.button;
mode = event->pad_button.mode;
break;
case GDK_PAD_RING:
case GDK_PAD_STRIP:
type = event->type == GDK_PAD_RING ?
GTK_PAD_ACTION_RING : GTK_PAD_ACTION_STRIP;
index = event->pad_axis.index;
mode = event->pad_axis.mode;
break;
default:
return GDK_EVENT_PROPAGATE;
}
entry = gtk_pad_action_find_match (pad_controller,
type, index, mode);
if (!entry)
return GDK_EVENT_PROPAGATE;
if (event->type == GDK_PAD_RING ||
event->type == GDK_PAD_STRIP)
{
gtk_pad_controller_activate_action_with_axis (pad_controller, entry,
event->pad_axis.value);
}
else
{
gtk_pad_controller_activate_action (pad_controller, entry);
}
return GDK_EVENT_STOP;
}
static void
gtk_pad_controller_set_pad (GtkPadController *controller,
GdkDevice *pad)
{
g_return_if_fail (!pad || GDK_IS_DEVICE (pad));
g_return_if_fail (!pad || gdk_device_get_source (pad) == GDK_SOURCE_TABLET_PAD);
g_set_object (&controller->pad, pad);
}
static void
gtk_pad_controller_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkPadController *controller = GTK_PAD_CONTROLLER (object);
switch (prop_id)
{
case PROP_ACTION_GROUP:
controller->action_group = g_value_dup_object (value);
break;
case PROP_PAD:
gtk_pad_controller_set_pad (controller, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_pad_controller_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkPadController *controller = GTK_PAD_CONTROLLER (object);
switch (prop_id)
{
case PROP_ACTION_GROUP:
g_value_set_object (value, controller->action_group);
break;
case PROP_PAD:
g_value_set_object (value, controller->pad);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_pad_controller_dispose (GObject *object)
{
GtkPadController *controller = GTK_PAD_CONTROLLER (object);
g_clear_object (&controller->action_group);
g_clear_object (&controller->pad);
G_OBJECT_CLASS (gtk_pad_controller_parent_class)->dispose (object);
}
static void
gtk_pad_controller_finalize (GObject *object)
{
GtkPadController *controller = GTK_PAD_CONTROLLER (object);
g_list_free_full (controller->entries, (GDestroyNotify) gtk_pad_action_entry_free);
G_OBJECT_CLASS (gtk_pad_controller_parent_class)->finalize (object);
}
static void
gtk_pad_controller_class_init (GtkPadControllerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
controller_class->filter_event = gtk_pad_controller_filter_event;
controller_class->handle_event = gtk_pad_controller_handle_event;
object_class->set_property = gtk_pad_controller_set_property;
object_class->get_property = gtk_pad_controller_get_property;
object_class->dispose = gtk_pad_controller_dispose;
object_class->finalize = gtk_pad_controller_finalize;
pspecs[PROP_ACTION_GROUP] =
g_param_spec_object ("action-group",
P_("Action group"),
P_("Action group to launch actions from"),
G_TYPE_ACTION_GROUP,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
pspecs[PROP_PAD] =
g_param_spec_object ("pad",
P_("Pad device"),
P_("Pad device to control"),
GDK_TYPE_DEVICE,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_PROPS, pspecs);
}
static void
gtk_pad_controller_init (GtkPadController *controller)
{
}
/**
* gtk_pad_controller_new:
* @window: a #GtkWindow
* @group: #GActionGroup to trigger actions from
* @pad: (nullable): A %GDK_SOURCE_TABLET_PAD device, or %NULL to handle all pads
*
* Creates a new #GtkPadController that will associate events from @pad to
* actions. A %NULL pad may be provided so the controller manages all pad devices
* generically, it is discouraged to mix #GtkPadController objects with %NULL
* and non-%NULL @pad argument on the same @window, as execution order is not
* guaranteed.
*
* The #GtkPadController is created with no mapped actions. In order to map pad
* events to actions, use gtk_pad_controller_set_action_entries() or
* gtk_pad_controller_set_action().
*
* Returns: A newly created #GtkPadController
*
* Since: 3.22
**/
GtkPadController *
gtk_pad_controller_new (GtkWindow *window,
GActionGroup *group,
GdkDevice *pad)
{
g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
g_return_val_if_fail (G_IS_ACTION_GROUP (group), NULL);
g_return_val_if_fail (!pad || GDK_IS_DEVICE (pad), NULL);
g_return_val_if_fail (!pad || gdk_device_get_source (pad) == GDK_SOURCE_TABLET_PAD, NULL);
return g_object_new (GTK_TYPE_PAD_CONTROLLER,
"propagation-phase", GTK_PHASE_CAPTURE,
"widget", window,
"action-group", group,
"pad", pad,
NULL);
}
static gint
entry_compare_func (gconstpointer a,
gconstpointer b)
{
const GtkPadActionEntry *entry1 = a, *entry2 = b;
if (entry1->mode > entry2->mode)
return -1;
else if (entry1->mode < entry2->mode)
return 1;
else if (entry1->index > entry2->index)
return -1;
else if (entry1->index < entry2->index)
return 1;
return 0;
}
static void
gtk_pad_controller_add_entry (GtkPadController *controller,
const GtkPadActionEntry *entry)
{
GtkPadActionEntry *copy;
copy = gtk_pad_action_entry_copy (entry);
controller->entries = g_list_insert_sorted (controller->entries, copy,
(GCompareFunc) entry_compare_func);
}
/**
* gtk_pad_controller_set_action_entries:
* @controller: a #GtkPadController
* @entries: (array length=n_entries): the action entries to set on @controller
* @n_entries: the number of elements in @entries
*
* This is a convenience function to add a group of action entries on
* @controller. See #GtkPadActionEntry and gtk_pad_controller_set_action().
*
* Since: 3.22
**/
void
gtk_pad_controller_set_action_entries (GtkPadController *controller,
const GtkPadActionEntry *entries,
gint n_entries)
{
gint i;
g_return_if_fail (GTK_IS_PAD_CONTROLLER (controller));
g_return_if_fail (entries != NULL);
for (i = 0; i < n_entries; i++)
gtk_pad_controller_add_entry (controller, &entries[i]);
}
/**
* gtk_pad_controller_set_action:
* @controller: a #GtkPadController
* @type: the type of pad feature that will trigger this action
* @index: the 0-indexed button/ring/strip number that will trigger this action
* @mode: the mode that will trigger this action, or -1 for all modes.
* @label: Human readable description of this action, this string should
* be deemed user-visible.
* @action_name: action name that will be activated in the #GActionGroup
*
* Adds an individual action to @controller. This action will only be activated
* if the given button/ring/strip number in @index is interacted while
* the current mode is @mode. -1 may be used for simple cases, so the action
* is triggered on all modes.
*
* The given @label should be considered user-visible, so internationalization
* rules apply. Some windowing systems may be able to use those for user
* feedback.
*
* Since: 3.22
**/
void
gtk_pad_controller_set_action (GtkPadController *controller,
GtkPadActionType type,
gint index,
gint mode,
const gchar *label,
const gchar *action_name)
{
GtkPadActionEntry entry = { type, index, mode,
(gchar *) label, (gchar *) action_name };
g_return_if_fail (GTK_IS_PAD_CONTROLLER (controller));
g_return_if_fail (type <= GTK_PAD_ACTION_RING);
gtk_pad_controller_add_entry (controller, &entry);
}

99
gtk/gtkpadcontroller.h Normal file
View File

@@ -0,0 +1,99 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2016, 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_PAD_CONTROLLER_H__
#define __GTK_PAD_CONTROLLER_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_PAD_CONTROLLER (gtk_pad_controller_get_type ())
#define GTK_PAD_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_PAD_CONTROLLER, GtkPadController))
#define GTK_PAD_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_PAD_CONTROLLER, GtkPadControllerClass))
#define GTK_IS_PAD_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_PAD_CONTROLLER))
#define GTK_IS_PAD_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_PAD_CONTROLLER))
#define GTK_PAD_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_PAD_CONTROLLER, GtkPadControllerClass))
typedef struct _GtkPadController GtkPadController;
typedef struct _GtkPadControllerClass GtkPadControllerClass;
typedef struct _GtkPadActionEntry GtkPadActionEntry;
/**
* GtkPadActionType:
* @GTK_PAD_ACTION_BUTTON: Action is triggered by a pad button
* @GTK_PAD_ACTION_RING: Action is triggered by a pad ring
* @GTK_PAD_ACTION_STRIP: Action is triggered by a pad strip
*
* The type of a pad action.
*/
typedef enum {
GTK_PAD_ACTION_BUTTON,
GTK_PAD_ACTION_RING,
GTK_PAD_ACTION_STRIP
} GtkPadActionType;
/**
* GtkPadActionEntry:
* @type: the type of pad feature that will trigger this action entry.
* @index: the 0-indexed button/ring/strip number that will trigger this action
* entry.
* @mode: the mode that will trigger this action entry, or -1 for all modes.
* @label: Human readable description of this action entry, this string should
* be deemed user-visible.
* @action_name: action name that will be activated in the #GActionGroup.
*
* Struct defining a pad action entry.
*/
struct _GtkPadActionEntry {
GtkPadActionType type;
gint index;
gint mode;
gchar *label;
gchar *action_name;
};
GDK_AVAILABLE_IN_3_22
GType gtk_pad_controller_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
GtkPadController *gtk_pad_controller_new (GtkWindow *window,
GActionGroup *group,
GdkDevice *pad);
GDK_AVAILABLE_IN_3_22
void gtk_pad_controller_set_action_entries (GtkPadController *controller,
const GtkPadActionEntry *entries,
gint n_entries);
GDK_AVAILABLE_IN_3_22
void gtk_pad_controller_set_action (GtkPadController *controller,
GtkPadActionType type,
gint index,
gint mode,
const gchar *label,
const gchar *action_name);
G_END_DECLS
#endif /* __GTK_PAD_CONTROLLER_H__ */

View File

@@ -7617,6 +7617,11 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_TOUCHPAD_PINCH:
return_val |= _gtk_widget_run_controllers (widget, event, GTK_PHASE_BUBBLE);
/* Fall through */
case GDK_PAD_BUTTON_PRESS:
case GDK_PAD_BUTTON_RELEASE:
case GDK_PAD_RING:
case GDK_PAD_STRIP:
case GDK_PAD_GROUP_MODE:
case GDK_EXPOSE:
case GDK_NOTHING:
signal_num = -1;