[PATCH libinput 5/7] pad: Add a new API for modes and mode groups

Peter Hutterer peter.hutterer at who-t.net
Mon Jun 6 06:50:42 UTC 2016


Move mode control to libinput. This reduces some flexibility on what we can do
with modes but makes it a lot easier for anyone to implement modes correctly
and have the LEDs apply appropriately, etc. Let's go with the option to make
the 95% use-case easy. Note: whether the mode is actually used is up to the
caller, e.g.  under Windows and OS X the mode only applies to the
rings/strips, not the buttons.

A tablet pad has 1 or more mode groups, all buttons/ring/strips are assigned
to a mode group. That group has a numeric mode index and is hooked to the
LEDs. libinput will switch the LEDs accordingly.

The mode group is a separate object. This allows for better APIs when it comes
to:
* checking whether a button/ring/strip is part of a mode group
* checking whether a button will trigger a mode transition
* checking which mode transition will happen

and in the future potentially:
* setting which button should change the mode transition
* changing what type of mode transition should happen.
* moving a button from one mode group to the other

This patch adds the basic scaffolding, without any real implementation.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 doc/tablet-support.dox |  59 ++++++++-
 src/libinput-private.h |   6 +
 src/libinput.c         | 128 +++++++++++++++++++
 src/libinput.h         | 324 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/libinput.sym       |  16 +++
 test/litest.c          |   3 +
 tools/event-debug.c    |  61 ++++++++--
 tools/event-gui.c      |   1 +
 8 files changed, 586 insertions(+), 12 deletions(-)

diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox
index c555cea..b7267a8 100644
--- a/doc/tablet-support.dox
+++ b/doc/tablet-support.dox
@@ -240,8 +240,7 @@ tablet.
 
 Some buttons may have expected default behaviors. For example, on Wacom
 Intuos Pro series tablets, the button inside the touch ring is expected to
-switch between a mode switch. Mode switching is a feature implemented in the
-caller and libinput does not provide specific handling. Callers should use
+switch between a mode switch, see @ref tablet-pad-modes. Callers should use
 external sources like libwacom to identify which buttons have semantic
 behaviors.
 
@@ -276,4 +275,60 @@ symmetric and thus do not support left-handed mode. libinput requires
 libwacom to determine if a tablet is capable of being switched to
 left-handed mode.
 
+ at section tablet-pad-modes Tablet pad modes
+
+Tablet pad modes are virtual groupings of button, ring and strip
+functionality. A caller may assign different functionalities depending on
+the mode the tablet is in. For example, in mode 0 the touch ring may emulate
+scrolling, in mode 1 the touch ring may emulate zooming, etc. libinput
+handles the modes and mode switching but does not assign specific
+functionality to buttons, rings or strips based on the mode. It is up to the
+caller to decide whether the mode only applies to buttons, rings and strips
+or only to rings and strips (this is the case with the Wacom OS X and
+Windows driver).
+
+The availability of modes on a touchpad usually depends on visual feedback
+such as LEDs around the touch ring. If no visual feedback is available, only
+one mode may be available.
+
+Mode switching is controlled by libinput and usually toggled by one or
+more buttons on the device. For example, on the Wacom Intuos 4, 5, and
+Pro series tablets the mode button is the button centered in the touch
+ring and toggles the modes sequentially. On the Wacom Cintiq 24HD the
+three buttons next to each touch ring allow for directly changing the
+mode to the desired setting.
+
+Multiple modes may exist on the tablet, libinput uses the term "mode group"
+for such groupings of buttons that share a mode and mode toggle. For
+example, the Wacom Cintiq 24HD has two separate mode groups, one for the
+left set of buttons, strips, and touch rings and one for the right set.
+libinput handles the mode groups independently and returns the mode for each
+button as appropriate. The mode group is static for the lifetime of the
+device.
+
+ at image html tablet-intuos-modes.svg "Modes on an Intuos Pro-like tablet"
+
+In the image above, the Intuos Pro-like tablet provides 4 LEDs to indicate
+the currently active modes. The button inside the touch ring cycles through
+the modes in a clockwise fashion. The upper-right LED indicates that the
+currently active mode is 1, based on 0-indexed mode numbering.
+libinput_event_tablet_pad_get_mode() would thus return 1 for all button and
+ring events on this tablet. When the center button is pressed, the mode
+switches to mode 2, the LED changes to the bottom-right and
+libinput_event_tablet_pad_get_mode() returns 2 for the center button event
+and all subsequent events.
+
+ at image html tablet-cintiq24hd-modes.svg "Modes on an Cintiq 24HD-like tablet"
+
+In the image above, the Cintiq 24HD-like tablet provides 3 LEDs on each side
+of the tablet to indicate the currently active mode for that group of
+buttons and the respective ring. The buttons next to the touch ring select
+the mode directly. The two LEDs indicate that the mode for the left set of
+buttons is currently 0, the mode for the right set of buttons is currently
+1, based on 0-indexed mode numbering. libinput_event_tablet_pad_get_mode()
+would thus return 0 for all button and ring events on the left and 1 for all
+button and ring events on the right. When one of the three mode toggle
+buttons on the right is pressed, the right mode switches to that button's
+mode but the left mode remains unchanged.
+
 */
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 1052212..98cb419 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -329,6 +329,12 @@ struct libinput_tablet_tool {
 	bool has_pressure_offset;
 };
 
+struct libinput_tablet_pad_mode_group {
+	struct list link;
+	int refcount;
+	void *user_data;
+};
+
 struct libinput_event {
 	enum libinput_event_type type;
 	struct libinput_device *device;
diff --git a/src/libinput.c b/src/libinput.c
index 0f676a3..e174aae 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -140,6 +140,8 @@ struct libinput_event_tablet_tool {
 
 struct libinput_event_tablet_pad {
 	struct libinput_event base;
+	unsigned int mode;
+	struct libinput_tablet_pad_mode_group *mode_group;
 	uint64_t time;
 	struct {
 		uint32_t button;
@@ -2580,6 +2582,7 @@ event_type_to_str(enum libinput_event_type type)
 	CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_BUTTON);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP);
+	CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_MODE);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END);
@@ -2829,6 +2832,105 @@ libinput_device_tablet_pad_get_num_strips(struct libinput_device *device)
 	return evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device);
 }
 
+LIBINPUT_EXPORT int
+libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device)
+{
+	return 0;
+}
+
+LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group*
+libinput_device_tablet_pad_get_mode_group(struct libinput_device *device,
+					  unsigned int index)
+{
+	return NULL;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_tablet_pad_mode_group_get_num_modes(
+				     struct libinput_tablet_pad_mode_group *group)
+{
+	return 1;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_tablet_pad_mode_group_get_mode(struct libinput_tablet_pad_mode_group *group)
+{
+	return 1;
+}
+
+LIBINPUT_EXPORT int
+libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group,
+					  unsigned int button)
+{
+	return 1;
+}
+
+LIBINPUT_EXPORT int
+libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
+					unsigned int ring)
+{
+	return 1;
+}
+
+LIBINPUT_EXPORT int
+libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group,
+					 unsigned int strip)
+{
+	return 1;
+}
+
+LIBINPUT_EXPORT int
+libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group,
+						unsigned int button)
+{
+	return 0;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_tablet_pad_mode_group_button_get_target_mode(struct libinput_tablet_pad_mode_group *group,
+						      unsigned int button)
+{
+	return 0;
+}
+
+LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
+libinput_tablet_pad_mode_group_ref(
+			struct libinput_tablet_pad_mode_group *group)
+{
+	group->refcount++;
+	return group;
+}
+
+LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
+libinput_tablet_pad_mode_group_unref(
+			struct libinput_tablet_pad_mode_group *group)
+{
+	assert(group->refcount > 0);
+
+	group->refcount--;
+	if (group->refcount > 0)
+		return group;
+
+	list_remove(&group->link);
+	free(group);
+	return NULL;
+}
+
+LIBINPUT_EXPORT void
+libinput_tablet_pad_mode_group_set_user_data(
+			struct libinput_tablet_pad_mode_group *group,
+			void *user_data)
+{
+	group->user_data = user_data;
+}
+
+LIBINPUT_EXPORT void *
+libinput_tablet_pad_mode_group_get_user_data(
+			struct libinput_tablet_pad_mode_group *group)
+{
+	return group->user_data;
+}
+
 LIBINPUT_EXPORT struct libinput_event *
 libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event)
 {
@@ -2989,6 +3091,32 @@ libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *eve
 	return event->button.state;
 }
 
+LIBINPUT_EXPORT unsigned int
+libinput_event_tablet_pad_get_mode(struct libinput_event_tablet_pad *event)
+{
+	require_event_type(libinput_event_get_context(&event->base),
+			   event->base.type,
+			   0,
+			   LIBINPUT_EVENT_TABLET_PAD_RING,
+			   LIBINPUT_EVENT_TABLET_PAD_STRIP,
+			   LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+	return event->mode;
+}
+
+LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
+libinput_event_tablet_pad_get_mode_group(struct libinput_event_tablet_pad *event)
+{
+	require_event_type(libinput_event_get_context(&event->base),
+			   event->base.type,
+			   NULL,
+			   LIBINPUT_EVENT_TABLET_PAD_RING,
+			   LIBINPUT_EVENT_TABLET_PAD_STRIP,
+			   LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+	return event->mode_group;
+}
+
 LIBINPUT_EXPORT uint32_t
 libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event)
 {
diff --git a/src/libinput.h b/src/libinput.h
index 212bf4a..88345ba 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -356,6 +356,277 @@ enum libinput_tablet_tool_tip_state {
 };
 
 /**
+ * @defgroup tablet_pad_modes Tablet pad modes
+ *
+ * Handling the virtual mode groups of buttons, strips and rings on tablet
+ * pad devices. See @ref tablet-pad-modes for details.
+ */
+
+/**
+ * @ingroup tablet_pad_modes
+ * @struct libinput_tablet_pad_mode_group
+ *
+ * A mode on a tablet pad is a virtual grouping of functionality, usually
+ * based on some visual feedback like LEDs on the pad. The set of buttons,
+ * rings and strips that share the same mode are a "mode group". Whenever
+ * the mode changes, all buttons, rings and strips within this mode group
+ * are affected. See @ref tablet-pad-modes for detail.
+ *
+ * Most tablets only have a single mode group, some tablets provide multiple
+ * mode groups through independent banks of LEDs (e.g. the Wacom Cintiq
+ * 24HD). libinput guarantees that at least one mode group is always
+ * available.
+ *
+ * This struct is refcounted, use libinput_tablet_pad_mode_group_ref() and
+ * libinput_tablet_pad_mode_group_unref().
+ */
+struct libinput_tablet_pad_mode_group;
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Most devices only provide a single mode group, however devices such as
+ * the Wacom Cintiq 22HD provide two mode groups. If multiple mode groups
+ * are available, a caller should use
+ * libinput_tablet_pad_mode_group_has_button(),
+ * libinput_tablet_pad_mode_group_has_ring() and
+ * libinput_tablet_pad_mode_group_has_strip() to associate each button,
+ * ring and strip with the correct mode group.
+ *
+ * @return the number of mode groups available on this device
+ */
+int
+libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * The returned mode group is not refcounted and may become invalid after
+ * the next call to libinput. Use libinput_tablet_pad_mode_group_ref() and
+ * libinput_tablet_pad_mode_group_unref() to continue using the handle
+ * outside of the immediate scope.
+ *
+ * While at least one reference is kept by the caller, the returned mode
+ * group will be identical for each subsequent call of this function with
+ * the same index and that same struct is returned from
+ * libinput_event_tablet_pad_get_mode_group(), provided the event was
+ * generated by this mode group.
+ *
+ * @param device A device with the @ref LIBINPUT_DEVICE_CAP_TABLET_PAD
+ * capability
+ * @param index A mode group index less
+ * @return the mode group with the given index or NULL if an invalid index
+ * is given.
+ */
+struct libinput_tablet_pad_mode_group*
+libinput_device_tablet_pad_get_mode_group(struct libinput_device *device,
+					  unsigned int index);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * The returned number is the same index as passed to
+ * libinput_device_tablet_pad_get_mode_group(). For tablets with only one
+ * mode this number is always 0
+ *
+ * @param group A previously obtained mode group
+ * @return the numeric index this mode group represents, starting at 0
+ */
+unsigned int
+libinput_tablet_pad_mode_group_get_index(struct libinput_tablet_pad_mode_group *group);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Query the mode group for the number of available modes. This number is
+ * usually decided by the number of physical LEDs available on the device.
+ * Different mode groups may have a different number of modes.
+ * Use libinput_tablet_pad_mode_group_get_mode() to get the currently active
+ * mode.
+ *
+ * libinput guarantees that at least one mode is available. A device without
+ * mode switching capability has a single mode group and a single mode.
+ *
+ * @param group A previously obtained mode group
+ * @return the number of modes available in this mode group
+ */
+unsigned int
+libinput_tablet_pad_mode_group_get_num_modes(struct libinput_tablet_pad_mode_group *group);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Return the current mode this mode group is in. Note that the returned
+ * mode is the mode valid as of completing the last libinput_dispatch().
+ * The returned mode may thus be different to the mode returned by
+ * libinput_event_tablet_pad_get_mode().
+ *
+ * For example, if the mode was toggled three times between the call to
+ * libinput_dispatch(), this function returns the third mode but the events
+ * in the event queue will return the modes 1, 2 and 3, respectively.
+ *
+ * @param group A previously obtained mode group
+ * @return the numeric index of the current mode in this group, starting at 0
+ */
+unsigned int
+libinput_tablet_pad_mode_group_get_mode(struct libinput_tablet_pad_mode_group *group);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Devices without mode switching capabilies return true for every button.
+ *
+ * @param group A previously obtained mode group
+ * @param button A button index, starting at 0
+ * @return true if the given button index is part of this mode group or
+ * false otherwise
+ */
+int
+libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group,
+					  unsigned int button);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Devices without mode switching capabilies return true for every ring.
+ *
+ * @param group A previously obtained mode group
+ * @param ring A ring index, starting at 0
+ * @return true if the given ring index is part of this mode group or
+ * false otherwise
+ */
+int
+libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
+					  unsigned int ring);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Devices without mode switching capabilies return true for every strip.
+ *
+ * @param group A previously obtained mode group
+ * @param strip A strip index, starting at 0
+ * @return true if the given strip index is part of this mode group or
+ * false otherwise
+ */
+int
+libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group,
+					  unsigned int strip);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Devices without mode switching capabilies return false for every button.
+ *
+ * @param group A previously obtained mode group
+ * @param button A button index, starting at 0
+ * @retval non-zero if the button is a mode toggle button for this group, or
+ * zero otherwise
+ */
+int
+libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group,
+						unsigned int button);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Semantic mode values used by
+ * libinput_tablet_pad_mode_group_button_get_target_mode().
+ *
+ * A mode toggle button may either select a specific mode directly or
+ * toggle to the previous or next mode.
+ */
+enum libinput_tablet_pad_mode_group_targets {
+	/**
+	 * Marks the maximum target mode allowed for direct mode selection.
+	 * Any mode number equal or below should be considered a direct mode
+	 * selection. e.g. a value of 2 means "select mode index 2"
+	 */
+	LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_MAX_DIRECT = 0xffff,
+	LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_PREVIOUS = 0x10000,
+	LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_NEXT,
+};
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Return the target mode for this mode toggle button. If the returned value
+ * is less or equal to @ref
+ * LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_MAX_DIRECT, the target mode index
+ * is the returned value.
+ *
+ * It is an application bug to call this function for a button index that is
+ * not a mode toggle button. In that case, the return value is always 0.
+ *
+ * @param group A previously obtained mode group
+ * @param button A button index, starting at 0
+ * @return the mode selected when this mode toggle button is
+ * pressed, starting at index 0
+ * @retval LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_PREVIOUS  Upon pressing
+ * this button, the previous mode is selected
+ * @retval LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_NEXT  Upon pressing this button, the next mode is selected
+ */
+unsigned int
+libinput_tablet_pad_mode_group_button_get_target_mode(struct libinput_tablet_pad_mode_group *group,
+						      unsigned int button);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Increase the refcount of the mode group. A mode device group will be
+ * freed whenever the refcount reaches 0.
+ *
+ * @param group A previously obtained mode group
+ * @return The passed mode group
+ */
+struct libinput_tablet_pad_mode_group *
+libinput_tablet_pad_mode_group_ref(
+			struct libinput_tablet_pad_mode_group *group);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Decrease the refcount of the mode group. A mode device group will be
+ * freed whenever the refcount reaches 0.
+ *
+ * @param group A previously obtained mode group
+ * @return NULL if the group was destroyed, otherwise the passed mode group
+ */
+struct libinput_tablet_pad_mode_group *
+libinput_tablet_pad_mode_group_unref(
+			struct libinput_tablet_pad_mode_group *group);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Set caller-specific data associated with this mode group. libinput does
+ * not manage, look at, or modify this data. The caller must ensure the
+ * data is valid.
+ *
+ * @param group A previously obtained mode group
+ * @param user_data Caller-specific data pointer
+ * @see libinput_tablet_pad_mode_group_get_user_data
+ *
+ */
+void
+libinput_tablet_pad_mode_group_set_user_data(
+			struct libinput_tablet_pad_mode_group *group,
+			void *user_data);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
+ * Get the caller-specific data associated with this input device, if any.
+ *
+ * @param group A previously obtained mode group
+ * @return Caller-specific data pointer or NULL if none was set
+ * @see libinput_tablet_pad_mode_group_set_user_data
+ */
+void *
+libinput_tablet_pad_mode_group_get_user_data(
+			struct libinput_tablet_pad_mode_group *group);
+
+/**
  * @ingroup base
  *
  * Event type for events returned by libinput_get_event().
@@ -504,6 +775,19 @@ enum libinput_event_type {
 	 */
 	LIBINPUT_EVENT_TABLET_PAD_STRIP,
 
+	/**
+	 * A mode change on a device with the @ref
+	 * LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+	 *
+	 * This event is triggered when the mode is changed through
+	 * external means. The event reflects a mode change (see @ref
+	 * tablet-pad-modes) occuring as a result other than that of
+	 * pressing a mode toggle button.
+	 *
+	 * @note Support for this event is not yet implemented.
+	 */
+	LIBINPUT_EVENT_TABLET_PAD_MODE,
+
 	LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
 	LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
 	LIBINPUT_EVENT_GESTURE_SWIPE_END,
@@ -2380,6 +2664,46 @@ libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *eve
 /**
  * @ingroup event_tablet_pad
  *
+ * Returns the current mode this button, ring, or strip is considered in.
+ * The mode is a virtual grouping of functionality, usually based on some
+ * visual feedback like LEDs on the pad. See @ref tablet-pad-modes for
+ * details. Mode indices start at 0, a device that does not support modes
+ * always returns 0.
+ *
+ * Mode switching is controlled by libinput and more than one mode may exist
+ * on the tablet. This function returns the mode the button, ring or strip
+ * of this event is logically grouped in. If the button is the mode toggle
+ * button and the button event caused a new mode to be toggled, the mode
+ * returned is the new mode the button is in.
+ *
+ * @param event The libinput tablet pad event
+ * @return the current 0-indexed mode of this button, ring or strip
+ */
+unsigned int
+libinput_event_tablet_pad_get_mode(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet_pad
+ *
+ * Returns the current mode group this button, ring, or strip is considered in.
+ * The mode is a virtual grouping of functionality, usually based on some
+ * visual feedback like LEDs on the pad. See @ref tablet-pad-modes for
+ * details.
+ *
+ * The returned mode group is not refcounted and may become invalid after
+ * the next call to libinput. Use libinput_tablet_pad_mode_group_ref() and
+ * libinput_tablet_pad_mode_group_unref() to continue using the handle
+ * outside of the immediate scope.
+ *
+ * @param event The libinput tablet pad event
+ * @return the current 0-indexed mode of this button, ring or strip
+ */
+struct libinput_tablet_pad_mode_group *
+libinput_event_tablet_pad_get_mode_group(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
  * @param event The libinput tablet pad event
  * @return The event time for this event
  */
diff --git a/src/libinput.sym b/src/libinput.sym
index c6a0e4c..631c36d 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -258,4 +258,20 @@ LIBINPUT_1.4 {
 	libinput_device_config_rotation_get_default_angle;
 	libinput_device_config_rotation_is_available;
 	libinput_device_config_rotation_set_angle;
+	libinput_device_tablet_pad_get_mode_group;
+	libinput_device_tablet_pad_get_num_mode_groups;
+	libinput_event_tablet_pad_get_mode;
+	libinput_event_tablet_pad_get_mode_group;
+	libinput_tablet_pad_mode_group_button_get_target_mode;
+	libinput_tablet_pad_mode_group_button_is_toggle;
+	libinput_tablet_pad_mode_group_get_index;
+	libinput_tablet_pad_mode_group_get_mode;
+	libinput_tablet_pad_mode_group_get_num_modes;
+	libinput_tablet_pad_mode_group_get_user_data;
+	libinput_tablet_pad_mode_group_has_button;
+	libinput_tablet_pad_mode_group_has_strip;
+	libinput_tablet_pad_mode_group_has_ring;
+	libinput_tablet_pad_mode_group_ref;
+	libinput_tablet_pad_mode_group_set_user_data;
+	libinput_tablet_pad_mode_group_unref;
 } LIBINPUT_1.3;
diff --git a/test/litest.c b/test/litest.c
index 66f08f2..41a9308 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -2097,6 +2097,9 @@ litest_event_type_str(struct libinput_event *event)
 	case LIBINPUT_EVENT_TABLET_PAD_STRIP:
 		str = "TABLET PAD STRIP";
 		break;
+	case LIBINPUT_EVENT_TABLET_PAD_MODE:
+		str = "TABLET PAD MODE";
+		break;
 	}
 	return str;
 }
diff --git a/tools/event-debug.c b/tools/event-debug.c
index a5608d2..c875035 100644
--- a/tools/event-debug.c
+++ b/tools/event-debug.c
@@ -130,6 +130,9 @@ print_event_header(struct libinput_event *ev)
 	case LIBINPUT_EVENT_TABLET_PAD_STRIP:
 		type = "TABLET_PAD_STRIP";
 		break;
+	case LIBINPUT_EVENT_TABLET_PAD_MODE:
+		type = "TABLET_PAD_STRIP";
+		break;
 	}
 
 	printf("%-7s	%-16s ", libinput_device_get_sysname(dev), type);
@@ -232,16 +235,18 @@ print_device_notify(struct libinput_event *ev)
 
 	if (libinput_device_has_capability(dev,
 					   LIBINPUT_DEVICE_CAP_TABLET_PAD)) {
-		int nbuttons, nstrips, nrings;
+		int nbuttons, nstrips, nrings, ngroups;
 
 		nbuttons = libinput_device_tablet_pad_get_num_buttons(dev);
 		nstrips = libinput_device_tablet_pad_get_num_strips(dev);
 		nrings = libinput_device_tablet_pad_get_num_rings(dev);
+		ngroups = libinput_device_tablet_pad_get_num_mode_groups(dev);
 
-		printf(" buttons:%d strips:%d rings:%d",
+		printf(" buttons:%d strips:%d rings:%d mode groups:%d",
 		       nbuttons,
 		       nstrips,
-		       nrings);
+		       nrings,
+		       ngroups);
 	}
 
 	printf("\n");
@@ -604,14 +609,25 @@ static void
 print_tablet_pad_button_event(struct libinput_event *ev)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+	struct libinput_tablet_pad_mode_group *group;
 	enum libinput_button_state state;
+	unsigned int button, mode;
 
 	print_event_time(libinput_event_tablet_pad_get_time(p));
 
+	button = libinput_event_tablet_pad_get_button_number(p),
 	state = libinput_event_tablet_pad_get_button_state(p);
-	printf("%3d %s\n",
-	       libinput_event_tablet_pad_get_button_number(p),
-	       state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released");
+	mode = libinput_event_tablet_pad_get_mode(p);
+	printf("%3d %s (mode %d)",
+	       button,
+	       state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
+	       mode);
+
+	group = libinput_event_tablet_pad_get_mode_group(p);
+	if (libinput_tablet_pad_mode_group_button_is_toggle(group, button))
+		printf(" <mode toggle>");
+
+	printf("\n");
 }
 
 static void
@@ -619,6 +635,7 @@ print_tablet_pad_ring_event(struct libinput_event *ev)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	const char *source = "<invalid>";
+	unsigned int mode;
 
 	print_event_time(libinput_event_tablet_pad_get_time(p));
 
@@ -631,10 +648,12 @@ print_tablet_pad_ring_event(struct libinput_event *ev)
 		break;
 	}
 
-	printf("ring %d position %.2f (source %s)\n",
+	mode = libinput_event_tablet_pad_get_mode(p);
+	printf("ring %d position %.2f (source %s) (mode %d)\n",
 	       libinput_event_tablet_pad_get_ring_number(p),
 	       libinput_event_tablet_pad_get_ring_position(p),
-	       source);
+	       source,
+	       mode);
 }
 
 static void
@@ -642,6 +661,7 @@ print_tablet_pad_strip_event(struct libinput_event *ev)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	const char *source = "<invalid>";
+	unsigned int mode;
 
 	print_event_time(libinput_event_tablet_pad_get_time(p));
 
@@ -654,10 +674,28 @@ print_tablet_pad_strip_event(struct libinput_event *ev)
 		break;
 	}
 
-	printf("strip %d position %.2f (source %s)\n",
+	mode = libinput_event_tablet_pad_get_mode(p);
+	printf("strip %d position %.2f (source %s) (mode %d)\n",
 	       libinput_event_tablet_pad_get_strip_number(p),
 	       libinput_event_tablet_pad_get_strip_position(p),
-	       source);
+	       source,
+	       mode);
+}
+
+static void
+print_tablet_pad_mode_event(struct libinput_event *ev)
+{
+	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+	struct libinput_tablet_pad_mode_group *group;
+	unsigned int mode;
+
+	print_event_time(libinput_event_tablet_pad_get_time(p));
+
+	group = libinput_event_tablet_pad_get_mode_group(p);
+	mode = libinput_event_tablet_pad_get_mode(p);
+	printf("group %d mode %d\n",
+	       libinput_tablet_pad_mode_group_get_index(group),
+	       mode);
 }
 
 static int
@@ -748,6 +786,9 @@ handle_and_print_events(struct libinput *li)
 		case LIBINPUT_EVENT_TABLET_PAD_STRIP:
 			print_tablet_pad_strip_event(ev);
 			break;
+		case LIBINPUT_EVENT_TABLET_PAD_MODE:
+			print_tablet_pad_mode_event(ev);
+			break;
 		}
 
 		libinput_event_destroy(ev);
diff --git a/tools/event-gui.c b/tools/event-gui.c
index 605f00a..5eeea1e 100644
--- a/tools/event-gui.c
+++ b/tools/event-gui.c
@@ -843,6 +843,7 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
 		case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
 		case LIBINPUT_EVENT_TABLET_PAD_RING:
 		case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+		case LIBINPUT_EVENT_TABLET_PAD_MODE:
 			break;
 		}
 
-- 
2.7.4



More information about the wayland-devel mailing list