[RFC libinput] Add dial input device support

Peter Hutterer peter.hutterer at who-t.net
Thu Feb 18 00:29:05 UTC 2016


Hi PrasannaKumar

On Mon, Feb 15, 2016 at 04:21:23PM +0530, PrasannaKumar Muralidharan wrote:
> 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.

First: yes, implementing support for dial interfaces is within the scope of
libinput, I think it makes sense to add this. But not quite in this form,
the various CAP flags are libinput interfaces that denote a specific
functionality. We should not add one per event group.

A while ago we started work on the so-called buttonset interface which is 
a generic interface to support a bunch of devices like yours:
https://lists.freedesktop.org/archives/wayland-devel/2016-January/026670.html

Since that patchset we've split out the wacom-specific parts so you can
ignore any strip/ring handling. There is no dial axis atm, but there
could be.

Have a look at that patchset, the documentation should explain what the
interface is for, etc. I think adding dial to buttonset and then getting
that interface ready for merging is the best approach here.

One comment on the API (to save you from scrolling down):
libinput_event_dial_get_rotation_count() should simply be
libinput_event_dial_get_rotation() and return the value in degrees. This is
more flexible than the current one which reads like it's multiples of 360
degrees?

Cheers,
   Peter
 
> ---
>  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