[RFC libinput] Add dial input device support

PrasannaKumar Muralidharan prasannatsmkumar at gmail.com
Mon Feb 15 10:51:23 UTC 2016


From: PrasannaKumar Muralidharan <prasannatsmkumar at gmail.com>

I am implementing dial input device support and would like to get feedback on the code that I have implemented so far. Did not implement test cases as of now, any pointers on how the test framework works will be helpful.

Smartwatch can use dial input, Car can use steering wheel rotation as input for maps may be. But I would like to know if dial input device support is useful before proceeding further?

Appreciate positive and negative comments.

---
 src/Makefile.am        |  2 ++
 src/evdev-dial.c       | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/evdev-dial.h       |  6 ++++
 src/evdev.c            | 12 ++++++++
 src/evdev.h            |  6 ++++
 src/libinput-private.h |  5 ++++
 src/libinput.c         | 74 ++++++++++++++++++++++++++++++++++++++++++++++
 src/libinput.h         | 46 +++++++++++++++++++++++++++++
 src/libinput.sym       |  5 ++++
 tools/event-debug.c    | 23 +++++++++++++++
 10 files changed, 259 insertions(+)
 create mode 100644 src/evdev-dial.c
 create mode 100644 src/evdev-dial.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 343e75c..20a32a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,8 @@ libinput_la_SOURCES =			\
 	libinput-private.h		\
 	evdev.c				\
 	evdev.h				\
+	evdev-dial.c			\
+	evdev-dial.h			\
 	evdev-middle-button.c		\
 	evdev-mt-touchpad.c		\
 	evdev-mt-touchpad.h		\
diff --git a/src/evdev-dial.c b/src/evdev-dial.c
new file mode 100644
index 0000000..2427724
--- /dev/null
+++ b/src/evdev-dial.c
@@ -0,0 +1,80 @@
+#include "config.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "evdev-dial.h"
+
+static void
+dial_process(struct evdev_dispatch *dispatch,
+	     struct evdev_device *device,
+	     struct input_event *event,
+	     uint64_t time)
+{
+	struct libinput_device *base = &device->base;
+	dial_notify(base, time, (int16_t)event->value);
+}
+
+/*
+static void
+dial_suspend(struct evdev_dispatch *dispatch,
+	     struct evdev_device *device)
+{
+
+}
+
+static void
+dial_remove(struct evdev_dispatch *dispatch)
+{
+
+}
+*/
+
+static void
+dial_destroy(struct evdev_dispatch *dispatch)
+{
+	free(dispatch);
+}
+
+/*
+static void
+dial_device_added(struct evdev_device *device,
+	          struct evdev_device *added_device)
+{
+
+}
+
+static void
+dial_device_removed(struct evdev_device *device,
+		    struct evdev_device *removed_device)
+{
+
+}
+*/
+
+/* TODO: Check whether other callbacks have to be implemented? */
+static struct evdev_dispatch_interface dial_interface = {
+	dial_process,
+	NULL, /* device suspend */
+	NULL, /* device remove */
+	dial_destroy,
+	NULL, /* device added */
+	NULL, /* device removed */
+	NULL, /* device suspended */
+	NULL, /* device resumed */
+	NULL, /* device post added */
+};
+
+struct evdev_dispatch *
+evdev_dial_create(struct evdev_device *device)
+{
+	struct evdev_dispatch *dispatch = zalloc(sizeof(struct evdev_dispatch));
+	if (!dispatch)
+		return NULL;
+
+	dispatch->interface = &dial_interface;
+
+	return dispatch;
+}
diff --git a/src/evdev-dial.h b/src/evdev-dial.h
new file mode 100644
index 0000000..0e191e7
--- /dev/null
+++ b/src/evdev-dial.h
@@ -0,0 +1,6 @@
+#ifndef EVDEV_DIAL_H
+#define EVDEV_DIAL_H
+
+#include "evdev.h"
+
+#endif /* EVDEV_DIAL_H */
diff --git a/src/evdev.c b/src/evdev.c
index 66673a8..1e044fb 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -63,6 +63,7 @@ enum evdev_device_udev_tags {
         EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7),
         EVDEV_UDEV_TAG_BUTTONSET = (1 << 8),
         EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9),
+        EVDEV_UDEV_TAG_DIAL = (1 << 10),
 };
 
 struct evdev_udev_tag_match {
@@ -82,6 +83,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
 	{"ID_INPUT_JOYSTICK",		EVDEV_UDEV_TAG_JOYSTICK},
 	{"ID_INPUT_ACCELEROMETER",	EVDEV_UDEV_TAG_ACCELEROMETER},
 	{"ID_INPUT_POINTINGSTICK",	EVDEV_UDEV_TAG_POINTINGSTICK},
+	{"ID_INPUT_DIAL",		EVDEV_UDEV_TAG_DIAL},
 
 	/* sentinel value */
 	{ 0 },
@@ -779,6 +781,7 @@ evdev_need_touch_frame(struct evdev_device *device)
 	switch (device->pending_event) {
 	case EVDEV_NONE:
 	case EVDEV_RELATIVE_MOTION:
+	case EVDEV_RELATIVE_DIAL:
 		break;
 	case EVDEV_ABSOLUTE_MT_DOWN:
 	case EVDEV_ABSOLUTE_MT_MOTION:
@@ -2157,6 +2160,15 @@ evdev_configure_device(struct evdev_device *device)
 		return -1;
 	}
 
+	if (udev_tags & EVDEV_UDEV_TAG_DIAL) {
+		device->dispatch = evdev_dial_create(device);
+		device->seat_caps |= EVDEV_DEVICE_DIAL;
+		log_info(libinput, "input device '%s', '%s' is a rotatory dial\n",
+			 device->devname, devnode);
+
+		return device->dispatch == NULL ? -1 : 0;
+	}
+
 	return 0;
 }
 
diff --git a/src/evdev.h b/src/evdev.h
index 8b567a8..b26ae10 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -54,6 +54,7 @@ enum evdev_event_type {
 	EVDEV_ABSOLUTE_MT_MOTION,
 	EVDEV_ABSOLUTE_MT_UP,
 	EVDEV_RELATIVE_MOTION,
+	EVDEV_RELATIVE_DIAL,
 };
 
 enum evdev_device_seat_capability {
@@ -62,6 +63,7 @@ enum evdev_device_seat_capability {
 	EVDEV_DEVICE_TOUCH = (1 << 2),
 	EVDEV_DEVICE_TABLET = (1 << 3),
 	EVDEV_DEVICE_GESTURE = (1 << 5),
+	EVDEV_DEVICE_DIAL = (1 << 9),
 };
 
 enum evdev_device_tags {
@@ -69,6 +71,7 @@ enum evdev_device_tags {
 	EVDEV_TAG_INTERNAL_TOUCHPAD = (1 << 1),
 	EVDEV_TAG_TRACKPOINT = (1 << 2),
 	EVDEV_TAG_KEYBOARD = (1 << 3),
+	EVDEV_TAG_DIAL = (1 << 4),
 };
 
 enum evdev_middlebutton_state {
@@ -309,6 +312,9 @@ struct evdev_dispatch *
 evdev_touchpad_create(struct evdev_device *device);
 
 struct evdev_dispatch *
+evdev_dial_create(struct evdev_device *device);
+
+struct evdev_dispatch *
 evdev_mt_touchpad_create(struct evdev_device *device);
 
 struct evdev_dispatch *
diff --git a/src/libinput-private.h b/src/libinput-private.h
index bc7000d..eabed47 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -503,6 +503,11 @@ gesture_notify_pinch_end(struct libinput_device *device,
 			 int cancelled);
 
 void
+dial_notify(struct libinput_device *device,
+	    uint64_t time,
+	    int16_t value);
+
+void
 touch_notify_frame(struct libinput_device *device,
 		   uint64_t time);
 
diff --git a/src/libinput.c b/src/libinput.c
index 2bcd416..8fc6ed8 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -125,6 +125,12 @@ struct libinput_event_gesture {
 	double angle;
 };
 
+struct libinput_event_dial {
+	struct libinput_event base;
+	uint64_t time;
+	int16_t count;
+};
+
 struct libinput_event_tablet_tool {
 	struct libinput_event base;
 	uint32_t button;
@@ -318,6 +324,15 @@ libinput_event_get_tablet_tool_event(struct libinput_event *event)
 	return (struct libinput_event_tablet_tool *) event;
 }
 
+LIBINPUT_EXPORT struct libinput_event_dial *
+libinput_event_get_dial_event(struct libinput_event *event)
+{
+	require_event_type(libinput_event_get_context(event), event->type, NULL,
+			   LIBINPUT_EVENT_DIAL);
+
+	return (struct libinput_event_dial *) event;
+}
+
 LIBINPUT_EXPORT struct libinput_event_device_notify *
 libinput_event_get_device_notify_event(struct libinput_event *event)
 {
@@ -1473,6 +1488,39 @@ libinput_tablet_tool_unref(struct libinput_tablet_tool *tool)
 	return NULL;
 }
 
+LIBINPUT_EXPORT uint32_t
+libinput_event_dial_get_time(struct libinput_event_dial *event)
+{
+	require_event_type(libinput_event_get_context(&event->base),
+			   event->base.type,
+			   0,
+			   LIBINPUT_EVENT_DIAL);
+
+	return us2ms(event->time);
+}
+
+LIBINPUT_EXPORT uint64_t
+libinput_event_dial_get_time_usec(struct libinput_event_dial *event)
+{
+	require_event_type(libinput_event_get_context(&event->base),
+			   event->base.type,
+			   0,
+			   LIBINPUT_EVENT_DIAL);
+
+	return event->time;
+}
+
+LIBINPUT_EXPORT int16_t
+libinput_event_dial_get_rotation_count(struct libinput_event_dial *event)
+{
+	require_event_type(libinput_event_get_context(&event->base),
+			   event->base.type,
+			   0,
+			   LIBINPUT_EVENT_DIAL);
+
+	return event->count;
+}
+
 struct libinput_source *
 libinput_add_fd(struct libinput *libinput,
 		int fd,
@@ -1953,6 +2001,9 @@ device_has_cap(struct libinput_device *device,
 	case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
 		capability = "CAP_TABLET";
 		break;
+	case LIBINPUT_DEVICE_CAP_DIAL:
+		capability = "CAP_DIAL";
+		break;
 	}
 
 	log_bug_libinput(device->seat->libinput,
@@ -2451,6 +2502,7 @@ event_type_to_str(enum libinput_event_type type)
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_BEGIN);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_UPDATE);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_PINCH_END);
+	CASE_RETURN_STRING(LIBINPUT_EVENT_DIAL);
 	case LIBINPUT_EVENT_NONE:
 		abort();
 	}
@@ -2458,6 +2510,28 @@ event_type_to_str(enum libinput_event_type type)
 	return NULL;
 }
 
+void
+dial_notify(struct libinput_device *device,
+	    uint64_t time,
+	    int16_t value)
+{
+	struct libinput_event_dial *dial_event;
+
+	if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_DIAL))
+		return;
+
+	dial_event = zalloc(sizeof *dial_event);
+	if (!dial_event)
+		return;
+
+	*dial_event = (struct libinput_event_dial) {
+		.time = time,
+		.count = value,
+	};
+
+	post_device_event(device, time, LIBINPUT_EVENT_DIAL, &dial_event->base);
+}
+
 static void
 libinput_post_event(struct libinput *libinput,
 		    struct libinput_event *event)
diff --git a/src/libinput.h b/src/libinput.h
index 8ed5632..fdba4f2 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -60,6 +60,7 @@ enum libinput_device_capability {
 	LIBINPUT_DEVICE_CAP_TOUCH = 2,
 	LIBINPUT_DEVICE_CAP_TABLET_TOOL = 3,
 	LIBINPUT_DEVICE_CAP_GESTURE = 5,
+	LIBINPUT_DEVICE_CAP_DIAL = 8,
 };
 
 /**
@@ -345,6 +346,8 @@ enum libinput_event_type {
 	LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
 	LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
 	LIBINPUT_EVENT_GESTURE_PINCH_END,
+
+	LIBINPUT_EVENT_DIAL = 900,
 };
 
 /**
@@ -552,6 +555,20 @@ libinput_event_get_gesture_event(struct libinput_event *event);
 /**
  * @ingroup event
  *
+ * Return the dial event that is this input event. If the event type does
+ * not match the device event types, this function returns NULL.
+ *
+ * The inverse of this function is
+ * libinput_event_device_notify_get_base_event().
+ *
+ * @return A device event, or NULL for other events
+ */
+struct libinput_event_dial *
+libinput_event_get_dial_event(struct libinput_event *event);
+
+/**
++ * @ingroup event
++ *
  * Return the tablet event that is this input event. If the event type does not
  * match the tablet event types, this function returns NULL.
  *
@@ -2082,6 +2099,35 @@ libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool,
 				   void *user_data);
 
 /**
+ * @ingroup event_dial
+ *
+ * @return The event time for this event
+ */
+uint32_t
+libinput_event_dial_get_time(struct libinput_event_dial *event);
+
+/**
+ * @ingroup event_dial
+ *
+ * @return The event time for this event in microseconds
+ */
+uint64_t
+libinput_event_dial_get_time_usec(struct libinput_event_dial *event);
+
+/**
+ * @ingroup event_dial
+ *
+ * Return the number of rotation made in the dial.
+ *
+ * Positive value indicates clockwise dial rotation, negavtive value indicates
+ * anticlockwise dial rotation.
+ *
+ * @return the number of dial rotation
+ */
+int16_t
+libinput_event_dial_get_rotation_count(struct libinput_event_dial *event);
+
+/**
  * @defgroup base Initialization and manipulation of libinput contexts
  */
 
diff --git a/src/libinput.sym b/src/libinput.sym
index a211388..ce0d859 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -233,4 +233,9 @@ LIBINPUT_1.2 {
 	libinput_tablet_tool_ref;
 	libinput_tablet_tool_set_user_data;
 	libinput_tablet_tool_unref;
+
+	libinput_event_get_dial_event;
+	libinput_event_dial_get_time;
+	libinput_event_dial_get_time_usec;
+	libinput_event_dial_get_rotation_count;
 } LIBINPUT_1.1;
diff --git a/tools/event-debug.c b/tools/event-debug.c
index 648111e..039c64d 100644
--- a/tools/event-debug.c
+++ b/tools/event-debug.c
@@ -121,6 +121,9 @@ print_event_header(struct libinput_event *ev)
 	case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
 		type = "TABLET_BUTTON";
 		break;
+	case LIBINPUT_EVENT_DIAL:
+		type = "DIAL_ROTATE";
+		break;
 	}
 
 	printf("%-7s	%-16s ", libinput_device_get_sysname(dev), type);
@@ -543,6 +546,23 @@ print_gesture_event_without_coords(struct libinput_event *ev)
 }
 
 static void
+print_dial_event(struct libinput_event *ev)
+{
+	struct libinput_event_dial *t = libinput_event_get_dial_event(ev);
+	enum libinput_event_type type;
+	int16_t count = 0;
+
+	type = libinput_event_get_type(ev);
+
+	print_event_time(libinput_event_dial_get_time(t));
+	count = libinput_event_dial_get_rotation_count(t);
+	if (count > 0)
+		printf("Dial rotated clockwise %d times\n", count);
+	else
+		printf("Dial rotated anti-clockwise %d times\n", count);
+}
+
+static void
 print_gesture_event_with_coords(struct libinput_event *ev)
 {
 	struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
@@ -647,6 +667,9 @@ handle_and_print_events(struct libinput *li)
 		case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
 			print_tablet_button_event(ev);
 			break;
+		case LIBINPUT_EVENT_DIAL:
+			print_dial_event(ev);
+			break;
 		}
 
 		libinput_event_destroy(ev);
-- 
2.5.0



More information about the wayland-devel mailing list