[PATCH libinput 09/20] touchpad: add clickpad-style software buttons

Peter Hutterer peter.hutterer at who-t.net
Wed Apr 23 20:27:05 PDT 2014


On Wed, Apr 23, 2014 at 11:35:35PM +0200, Jonas Ådahl wrote:
> On Tue, Apr 15, 2014 at 02:28:06PM +0200, Hans de Goede wrote:
> > From: Peter Hutterer <peter.hutterer at who-t.net>
> > 
> > This is a slightly fancier implementation than the simplest model and ported
> > over from libtouchpad. It implements a state machine for the software buttons
> > with left and right buttons currently implemented. Buttons are oriented
> > left-to-right, in a horizontal bar. No random button placement allowed.
> > 
> > In general, the procedure is:
> > - if a finger sets down in the left button area, a click is a left click
> > - if a finger sets down in the right button area, a click is a right click
> > - if a finger leaves the button area, a click is a left click
> > - if a finger starts outside the button area, a click is a left click
> > 
> > Two timeouts are used to handle buttons more smoothly:
> > - if a finger sets down in a button area but "immediately" moves over
> >   to a different area, that area takes effect on a click.
> > - if a finger leaves a button area and "immediately" clicks or moves back into
> >   the area, the button still takes effect on a click.
> > - if a finger changes between areas and stays there for a timeout, that area
> >   takes effect on a click.
> > 
> > HdG:
> > -Simplified the state machine a bit
> > -Renamed the button area states to BOTTOM_foo to make it easier to later add
> >  support for a top button area such as can be found one the Thinkpad [2-5]40
> >  series.
> > -Init area.top_edge to INT_MAX in the non soft button case to make the entire
> >  state machine a nop in that case
> 
> 
> 
> > 
> > Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> > Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> 
> 1, 2, 3, 7, 8 and this one Reviewed-by: Jonas Ådahl <jadahl at gmail.com>
> so far.
> 
> 12 I sneakily pushed already. Some style comments in this one inline as
> well.
> 
> > ---
> >  doc/Makefile.am                           |   2 +-
> >  doc/touchpad-softbutton-state-machine.svg | 173 ++++++++++++
> >  src/evdev-mt-touchpad-buttons.c           | 453 +++++++++++++++++++++++++++++-
> >  src/evdev-mt-touchpad.c                   |  15 +
> >  src/evdev-mt-touchpad.h                   |  48 ++++
> >  src/libinput.h                            |  40 +++
> >  6 files changed, 728 insertions(+), 3 deletions(-)
> >  create mode 100644 doc/touchpad-softbutton-state-machine.svg

..

> > --- a/src/evdev-mt-touchpad-buttons.c
> > +++ b/src/evdev-mt-touchpad-buttons.c
> > @@ -20,11 +20,349 @@
> >   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> >   */
> >  
> > +#include <errno.h>
> > +#include <limits.h>
> > +#include <time.h>
> >  #include <math.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <linux/input.h>
> > +#include <sys/timerfd.h>
> >  
> >  #include "evdev-mt-touchpad.h"
> >  
> >  #define DEFAULT_BUTTON_MOTION_THRESHOLD 0.02 /* in percent of size */
> > +#define DEFAULT_BUTTON_ENTER_TIMEOUT 100 /* ms */
> > +#define DEFAULT_BUTTON_LEAVE_TIMEOUT 300 /* ms */
> > +
> > +/*****************************************
> > + * DO NOT EDIT THIS FILE!
> 
> Is this really true? What it should say it what it says just below.
> There are also potential changes that needs editing of this file that
> has no impact on the diagram.

I've changed this to "BEFORE YOU EDIT THIS FILE, ..." instead. All style
issues below fixed, too. thanks for the review.

Cheers,
   Peter

> 
> > + *
> > + * Look at the state diagram in doc/touchpad-softbutton-state-machine.svg, or
> > + * online at
> > + * https://drive.google.com/file/d/0B1NwWmji69nocUs1cVJTbkdwMFk/edit?usp=sharing
> > + * (it's a http://draw.io diagram)
> > + *
> > + * Any changes in this file must be represented in the diagram.
> > + *
> > + * The state machine only affects the soft button area code.
> > + */
> > +
> > +#define CASE_RETURN_STRING(a) case a: return #a;
> > +
> > +static inline const char*
> > +button_state_to_str(enum button_state state) {
> > +	switch(state) {
> > +	CASE_RETURN_STRING(BUTTON_STATE_NONE);
> > +	CASE_RETURN_STRING(BUTTON_STATE_AREA);
> > +	CASE_RETURN_STRING(BUTTON_STATE_BOTTOM);
> > +	CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_NEW);
> > +	CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_TO_AREA);
> > +	}
> > +	return NULL;
> > +}
> > +
> > +static inline const char*
> > +button_event_to_str(enum button_event event) {
> > +	switch(event) {
> > +	CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_IN_AREA);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_UP);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_PRESS);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_RELEASE);
> > +	CASE_RETURN_STRING(BUTTON_EVENT_TIMEOUT);
> > +	}
> > +	return NULL;
> > +}
> > +
> > +static inline bool
> > +is_inside_button_area(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	return t->y >= tp->buttons.area.top_edge;
> > +}
> > +
> > +static inline bool
> > +is_inside_right_area(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	return is_inside_button_area(tp, t) &&
> > +	       t->x > tp->buttons.area.rightbutton_left_edge;
> > +}
> > +
> > +static inline bool
> > +is_inside_left_area(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	return is_inside_button_area(tp, t) &&
> > +	       !is_inside_right_area(tp, t);
> > +}
> > +
> > +static void
> > +tp_button_set_timer(struct tp_dispatch *tp, uint32_t timeout)
> > +{
> > +	struct itimerspec its;
> > +	its.it_interval.tv_sec = 0;
> > +	its.it_interval.tv_nsec = 0;
> > +	its.it_value.tv_sec = timeout / 1000;
> > +	its.it_value.tv_nsec = (timeout % 1000) * 1000 * 1000;
> > +	timerfd_settime(tp->buttons.timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
> > +}
> > +
> > +static void
> > +tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	t->button.timeout = t->millis + DEFAULT_BUTTON_ENTER_TIMEOUT;
> > +	tp_button_set_timer(tp, t->button.timeout);
> > +}
> > +
> > +static void
> > +tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	t->button.timeout = t->millis + DEFAULT_BUTTON_LEAVE_TIMEOUT;
> > +	tp_button_set_timer(tp, t->button.timeout);
> > +}
> > +
> > +static void
> > +tp_button_clear_timer(struct tp_dispatch *tp, struct tp_touch *t)
> > +{
> > +	t->button.timeout = 0;
> > +}
> > +
> > +/*
> > + * tp_button_set_state, change state and implement on-entry behavior
> > + * as described in the state machine diagram.
> > + */
> > +static void
> > +tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
> > +		    enum button_state new_state, enum button_event event)
> > +{
> > +	tp_button_clear_timer(tp, t);
> > +
> > +	switch (new_state) {
> > +	case BUTTON_STATE_NONE:
> > +		t->button.curr = 0;
> > +		break;
> > +	case BUTTON_STATE_AREA:
> > +		t->button.curr = BUTTON_EVENT_IN_AREA;
> > +		break;
> > +	case BUTTON_STATE_BOTTOM:
> > +		break;
> > +	case BUTTON_STATE_BOTTOM_NEW:
> > +		t->button.curr = event;
> > +		tp_button_set_enter_timer(tp, t);
> > +		break;
> > +	case BUTTON_STATE_BOTTOM_TO_AREA:
> > +		tp_button_set_leave_timer(tp, t);
> > +		break;
> > +	}
> > +	t->button.state = new_state;
> > +}
> > +
> > +static void
> > +tp_button_none_handle_event(struct tp_dispatch *tp,
> > +			    struct tp_touch *t,
> > +			    enum button_event event)
> > +{
> > +	switch (event) {
> > +	case BUTTON_EVENT_IN_BOTTOM_R:
> > +	case BUTTON_EVENT_IN_BOTTOM_L:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, event);
> > +		break;
> > +	case BUTTON_EVENT_IN_AREA:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
> > +		break;
> > +	case BUTTON_EVENT_UP:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
> > +		break;
> > +	case BUTTON_EVENT_PRESS:
> > +	case BUTTON_EVENT_RELEASE:
> > +	case BUTTON_EVENT_TIMEOUT:
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +tp_button_area_handle_event(struct tp_dispatch *tp,
> > +			    struct tp_touch *t,
> > +			    enum button_event event)
> > +{
> > +	switch (event) {
> > +	case BUTTON_EVENT_IN_BOTTOM_R:
> > +	case BUTTON_EVENT_IN_BOTTOM_L:
> > +	case BUTTON_EVENT_IN_AREA:
> > +		break;
> > +	case BUTTON_EVENT_UP:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
> > +		break;
> > +	case BUTTON_EVENT_PRESS:
> > +	case BUTTON_EVENT_RELEASE:
> > +	case BUTTON_EVENT_TIMEOUT:
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +tp_button_bottom_handle_event(struct tp_dispatch *tp,
> > +			    struct tp_touch *t,
> > +			    enum button_event event)
> > +{
> > +	switch (event) {
> > +	case BUTTON_EVENT_IN_BOTTOM_R:
> > +	case BUTTON_EVENT_IN_BOTTOM_L:
> > +		if (event != t->button.curr)
> > +			tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
> > +					    event);
> > +		break;
> > +	case BUTTON_EVENT_IN_AREA:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_TO_AREA, event);
> > +		break;
> > +	case BUTTON_EVENT_UP:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
> > +		break;
> > +	case BUTTON_EVENT_PRESS:
> > +	case BUTTON_EVENT_RELEASE:
> > +	case BUTTON_EVENT_TIMEOUT:
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +tp_button_bottom_new_handle_event(struct tp_dispatch *tp,
> > +				struct tp_touch *t,
> > +				enum button_event event)
> > +{
> > +	switch(event) {
> > +	case BUTTON_EVENT_IN_BOTTOM_R:
> > +	case BUTTON_EVENT_IN_BOTTOM_L:
> > +		if (event != t->button.curr)
> > +			tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
> > +					    event);
> > +		break;
> > +	case BUTTON_EVENT_IN_AREA:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
> > +		break;
> > +	case BUTTON_EVENT_UP:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
> > +		break;
> > +	case BUTTON_EVENT_PRESS:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
> > +		break;
> > +	case BUTTON_EVENT_RELEASE:
> > +		break;
> > +	case BUTTON_EVENT_TIMEOUT:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +tp_button_bottom_to_area_handle_event(struct tp_dispatch *tp,
> > +				      struct tp_touch *t,
> > +				      enum button_event event)
> > +{
> > +	switch(event) {
> > +	case BUTTON_EVENT_IN_BOTTOM_R:
> > +	case BUTTON_EVENT_IN_BOTTOM_L:
> > +		if (event == t->button.curr)
> > +			tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
> > +					    event);
> > +		else
> > +			tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
> > +					    event);
> > +		break;
> > +	case BUTTON_EVENT_IN_AREA:
> > +		break;
> > +	case BUTTON_EVENT_UP:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
> > +		break;
> > +	case BUTTON_EVENT_PRESS:
> > +	case BUTTON_EVENT_RELEASE:
> > +		break;
> > +	case BUTTON_EVENT_TIMEOUT:
> > +		tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +tp_button_handle_event(struct tp_dispatch *tp,
> > +		       struct tp_touch *t,
> > +		       enum button_event event,
> > +		       uint32_t time)
> > +{
> > +	enum button_state current = t->button.state;
> > +
> > +	switch(t->button.state) {
> > +	case BUTTON_STATE_NONE:
> > +		tp_button_none_handle_event(tp, t, event);
> > +		break;
> > +	case BUTTON_STATE_AREA:
> > +		tp_button_area_handle_event(tp, t, event);
> > +		break;
> > +	case BUTTON_STATE_BOTTOM:
> > +		tp_button_bottom_handle_event(tp, t, event);
> > +		break;
> > +	case BUTTON_STATE_BOTTOM_NEW:
> > +		tp_button_bottom_new_handle_event(tp, t, event);
> > +		break;
> > +	case BUTTON_STATE_BOTTOM_TO_AREA:
> > +		tp_button_bottom_to_area_handle_event(tp, t, event);
> > +		break;
> > +	}
> > +
> > +	if (current != t->button.state)
> > +		log_debug("button state: from %s, event %s to %s\n",
> > +				button_state_to_str(current),
> > +				button_event_to_str(event),
> > +				button_state_to_str(t->button.state));
> 
> Incorrect indentation.
> 
> > +}
> > +
> > +int
> > +tp_button_handle_state(struct tp_dispatch *tp, uint32_t time)
> > +{
> > +	struct tp_touch *t;
> > +
> > +	tp_for_each_touch(tp, t) {
> > +		if (t->state == TOUCH_NONE)
> > +			continue;
> > +		if (t->fake)
> > +			continue;
> > +
> 
> Both of the following two branches should have surrounding {}
> 
> > +		if (t->state == TOUCH_END)
> > +				tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time);
> 
> Wrong indentation.
> 
> > +		else if (t->dirty) {
> > +			if (is_inside_right_area(tp, t))
> > +				tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
> > +			else if (is_inside_left_area(tp, t))
> > +				tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
> > +			else
> > +				tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
> > +		}
> > +		if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
> > +			tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time);
> > +		if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
> > +			tp_button_handle_event(tp, t, BUTTON_EVENT_PRESS, time);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int
> > +tp_button_handle_timeout(struct tp_dispatch *tp, uint32_t now)
> > +{
> > +	struct tp_touch *t;
> > +	uint32_t min_timeout = INT_MAX;
> > +
> > +	tp_for_each_touch(tp, t) {
> > +		if (t->button.timeout != 0 && t->button.timeout <= now) {
> > +			tp_button_clear_timer(tp, t);
> > +			tp_button_handle_event(tp, t, BUTTON_EVENT_TIMEOUT, now);
> > +		}
> > +		if (t->button.timeout != 0)
> > +			min_timeout = min(t->button.timeout, min_timeout);
> > +	}
> > +
> > +	return min_timeout == INT_MAX ? 0 : min_timeout;
> > +}
> >  
> >  int
> >  tp_process_button(struct tp_dispatch *tp,
> > @@ -43,6 +381,28 @@ tp_process_button(struct tp_dispatch *tp,
> >  	return 0;
> >  }
> >  
> > +static void
> > +tp_button_timeout_handler(void *data)
> > +{
> > +	struct tp_dispatch *tp = data;
> > +	uint64_t expires;
> > +	int len;
> > +	struct timespec ts;
> > +	uint32_t now;
> > +
> > +	len = read(tp->buttons.timer_fd, &expires, sizeof expires);
> > +	if (len != sizeof expires)
> > +		/* This will only happen if the application made the fd
> > +		 * non-blocking, but this function should only be called
> > +		 * upon the timeout, so lets continue anyway. */
> > +		log_error("timerfd read error: %s\n", strerror(errno));
> > +
> > +	clock_gettime(CLOCK_MONOTONIC, &ts);
> > +	now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
> > +
> > +	tp_button_handle_timeout(tp, now);
> > +}
> > +
> >  int
> >  tp_init_buttons(struct tp_dispatch *tp,
> >  		struct evdev_device *device)
> > @@ -62,12 +422,45 @@ tp_init_buttons(struct tp_dispatch *tp,
> >  
> >  	if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* Apple */
> >  		tp->buttons.use_clickfinger = true;
> > -	else
> > -		tp->buttons.use_clickfinger = false;
> > +
> > +	tp->buttons.use_softbuttons = !tp->buttons.use_clickfinger &&
> > +				      !tp->buttons.has_buttons;
> > +
> > +	if (tp->buttons.use_softbuttons) {
> > +		tp->buttons.area.top_edge = height * .8 + device->abs.min_y;
> > +		tp->buttons.area.rightbutton_left_edge = width/2 + device->abs.min_x;
> > +		tp->buttons.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
> > +
> > +		if (tp->buttons.timer_fd == -1)
> > +			return -1;
> > +
> > +		tp->buttons.source =
> > +			libinput_add_fd(tp->device->base.seat->libinput,
> > +					tp->buttons.timer_fd,
> > +					tp_button_timeout_handler,
> > +					tp);
> > +		if (tp->buttons.source == NULL)
> > +			return -1;
> > +	} else
> > +		tp->buttons.area.top_edge = INT_MAX;
> 
> Should have surruonding {}
> 
> >  
> >  	return 0;
> >  }
> >  
> > +void
> > +tp_destroy_buttons(struct tp_dispatch *tp)
> > +{
> > +	if (tp->buttons.source) {
> > +		libinput_remove_source(tp->device->base.seat->libinput,
> > +				       tp->buttons.source);
> > +		tp->buttons.source = NULL;
> > +	}
> > +	if (tp->buttons.timer_fd > -1) {
> > +		close(tp->buttons.timer_fd);
> > +		tp->buttons.timer_fd = -1;
> > +	}
> > +}
> > +
> >  static int
> >  tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint32_t time)
> >  {
> > @@ -136,6 +529,59 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint32_t time)
> >  	return 0;
> >  }
> >  
> > +static int
> > +tp_post_softbutton_buttons(struct tp_dispatch *tp, uint32_t time)
> > +{
> > +	uint32_t current, old, button;
> > +	enum libinput_pointer_button_state state;
> > +
> > +	current = tp->buttons.state;
> > +	old = tp->buttons.old_state;
> > +
> > +	if (current == old)
> > +		return 0;
> > +
> > +	if (tp->nfingers_down == 0 || tp->nfingers_down > 2)
> > +		return 0;
> > +
> > +	if (current) {
> > +		struct tp_touch *t;
> > +		button = 0;
> > +
> > +		tp_for_each_touch(tp, t) {
> > +			if (t->button.curr == BUTTON_EVENT_IN_BOTTOM_R)
> > +				button |= 0x2;
> > +			else if (t->button.curr == BUTTON_EVENT_IN_BOTTOM_L)
> > +				button |= 0x1;
> > +		}
> > +
> > +		switch (button) {
> > +		case 0:	/* only in area */
> > +		case 1: /* only left area */
> > +			button = BTN_LEFT;
> > +			break;
> > +		case 2: /* only right area */
> > +			button = BTN_RIGHT;
> > +			break;
> > +		case 3: /* left + right area */
> > +			button = BTN_MIDDLE;
> > +			break;
> > +		}
> > +
> > +		tp->buttons.active = button;
> > +		state = LIBINPUT_POINTER_BUTTON_STATE_PRESSED;
> > +	} else {
> > +		state = LIBINPUT_POINTER_BUTTON_STATE_RELEASED;
> > +		button = tp->buttons.active;
> > +	}
> > +
> > +	pointer_notify_button(&tp->device->base,
> > +			      time,
> > +			      button,
> > +			      state);
> > +	return 1;
> > +}
> > +
> >  int
> >  tp_post_button_events(struct tp_dispatch *tp, uint32_t time)
> >  {
> > @@ -149,6 +595,9 @@ tp_post_button_events(struct tp_dispatch *tp, uint32_t time)
> >  		rc = tp_post_physical_buttons(tp, time);
> >  	else if (tp->buttons.use_clickfinger)
> >  		rc = tp_post_clickfinger_buttons(tp, time);
> > +	else if (tp->buttons.use_softbuttons)
> > +		rc = tp_post_softbutton_buttons(tp, time);
> > +
> >  
> >  	return rc;
> >  }
> > diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
> > index 1873569..9df4a78 100644
> > --- a/src/evdev-mt-touchpad.c
> > +++ b/src/evdev-mt-touchpad.c
> > @@ -398,6 +398,8 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
> >  		tp_unpin_finger(tp, t);
> >  	}
> >  
> > +	tp_button_handle_state(tp, time);
> > +
> >  	/*
> >  	 * We have a physical button down event on a clickpad. To avoid
> >  	 * spurious pointer moves by the clicking finger we pin all fingers.
> > @@ -595,6 +597,7 @@ tp_destroy(struct evdev_dispatch *dispatch)
> >  		(struct tp_dispatch*)dispatch;
> >  
> >  	tp_destroy_tap(tp);
> > +	tp_destroy_buttons(tp);
> >  
> >  	if (tp->filter)
> >  		tp->filter->interface->destroy(tp->filter);
> > @@ -607,10 +610,18 @@ static struct evdev_dispatch_interface tp_interface = {
> >  	tp_destroy
> >  };
> >  
> > +static void
> > +tp_init_touch(struct tp_dispatch *tp,
> > +	      struct tp_touch *t)
> > +{
> > +	t->button.state = BUTTON_STATE_NONE;
> > +}
> > +
> >  static int
> >  tp_init_slots(struct tp_dispatch *tp,
> >  	      struct evdev_device *device)
> >  {
> > +	size_t i;
> >  	const struct input_absinfo *absinfo;
> >  
> >  	absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
> > @@ -646,6 +657,9 @@ tp_init_slots(struct tp_dispatch *tp,
> >  	tp->touches = calloc(tp->ntouches,
> >  			     sizeof(struct tp_touch));
> >  
> > +	for (i = 0; i < tp->ntouches; i++)
> > +		tp_init_touch(tp, &tp->touches[i]);
> > +
> >  	return 0;
> >  }
> >  
> > @@ -687,6 +701,7 @@ tp_init(struct tp_dispatch *tp,
> >  	tp->base.interface = &tp_interface;
> >  	tp->device = device;
> >  	tp->tap.timer_fd = -1;
> > +	tp->buttons.timer_fd = -1;
> >  
> >  	if (tp_init_slots(tp, device) != 0)
> >  		return -1;
> > diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
> > index 87d291a..8d8dd84 100644
> > --- a/src/evdev-mt-touchpad.h
> > +++ b/src/evdev-mt-touchpad.h
> > @@ -46,6 +46,24 @@ enum touch_state {
> >  	TOUCH_END
> >  };
> >  
> > +enum button_event {
> > +       BUTTON_EVENT_IN_BOTTOM_R = 30,
> > +       BUTTON_EVENT_IN_BOTTOM_L,
> > +       BUTTON_EVENT_IN_AREA,
> > +       BUTTON_EVENT_UP,
> > +       BUTTON_EVENT_PRESS,
> > +       BUTTON_EVENT_RELEASE,
> > +       BUTTON_EVENT_TIMEOUT,
> > +};
> > +
> > +enum button_state {
> > +       BUTTON_STATE_NONE,
> > +       BUTTON_STATE_AREA,
> > +       BUTTON_STATE_BOTTOM,
> > +       BUTTON_STATE_BOTTOM_NEW,
> > +       BUTTON_STATE_BOTTOM_TO_AREA,
> > +};
> > +
> >  enum scroll_state {
> >  	SCROLL_STATE_NONE,
> >  	SCROLL_STATE_SCROLLING
> > @@ -101,6 +119,14 @@ struct tp_touch {
> >  		int32_t center_x;
> >  		int32_t center_y;
> >  	} pinned;
> > +
> > +	/* Software-button state and timeout if applicable */
> > +	struct {
> > +		enum button_state state;
> > +		/* We use button_event here so we can use == on events */
> > +		enum button_event curr;
> > +		uint32_t timeout;
> > +	} button;
> >  };
> >  
> >  struct tp_dispatch {
> > @@ -130,10 +156,26 @@ struct tp_dispatch {
> >  	struct {
> >  		bool has_buttons;		/* true for physical LMR buttons */
> >  		bool use_clickfinger;		/* number of fingers decides button number */
> > +		bool use_softbuttons;		/* use software-button area */
> >  		uint32_t state;
> >  		uint32_t old_state;
> >  		uint32_t motion_dist;		/* for pinned touches */
> >  		unsigned int active;		/* currently active button, for release event */
> > +
> > +		/* Only used if has_buttons is false. The software button area is always
> > +		 * a horizontal strip across the touchpad. Depending on the
> > +		 * rightbutton_left_edge value, the buttons are split according to the
> > +		 * edge settings.
> > +		  */
> > +		struct {
> > +			int32_t top_edge;
> > +			int32_t rightbutton_left_edge;
> > +		} area;
> > +
> > +		unsigned int timeout;		/* current timeout in ms */
> > +
> > +		int timer_fd;
> > +		struct libinput_source *source;
> >  	} buttons;				/* physical buttons */
> >  
> >  	struct {
> > @@ -173,6 +215,9 @@ tp_destroy_tap(struct tp_dispatch *tp);
> >  int
> >  tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device);
> >  
> > +void
> > +tp_destroy_buttons(struct tp_dispatch *tp);
> > +
> >  int
> >  tp_process_button(struct tp_dispatch *tp,
> >  		  const struct input_event *e,
> > @@ -181,4 +226,7 @@ tp_process_button(struct tp_dispatch *tp,
> >  int
> >  tp_post_button_events(struct tp_dispatch *tp, uint32_t time);
> >  
> > +int
> > +tp_button_handle_state(struct tp_dispatch *tp, uint32_t time);
> > +
> >  #endif
> > diff --git a/src/libinput.h b/src/libinput.h
> > index 810a66c..91797ed 100644
> > --- a/src/libinput.h
> > +++ b/src/libinput.h
> > @@ -39,6 +39,46 @@ extern "C" {
> >   */
> >  
> >  /**
> > + * @page tpbuttons Touchpad button behavior
> > + *
> > + * For touchpad devices without physical buttons, libinput enables an
> > + * emulated right button area through either of two methods.
> > + *
> > + * Software button areas
> > + * =====================
> > + * On most touchpads, the bottom area of the touchpad is split into a a left
> > + * and a right-button area. Pressing the touchpad down with a finger in
> > + * those areas will generate clicks as shown in the diagram below:
> > + *
> > + * @code
> > +    +------------------------+
> > +    |                        |
> > +    |                        |
> > +    |          LEFT          |
> > +    |                        |
> > +    |                        |
> > +    +------------------------+
> > +    |    LEFT    |   RIGHT   |
> > +    +------------------------+
> > + * @endcode
> > + *
> > + * Generally, the touchpad will emulate a right-button click if the finger
> > + * was set down in the right button area and did not leave the
> > + * right button area before clicking, even if another finger was already
> > + * down on the touchpad in another area.
> > + * A middle click is generated by clicking the touchpad when one finger is
> > + * in the bottom left button area, and one finger is in the botton right
> > + * button area.
> > + * The exact behavior of the touchpad is implementation-dependent.
> > + *
> > + * Clickfinger
> > + * ===========
> > + * On Apple touchpads, no button areas are provided. Instead, use a
> > + * two-finger click for a right button click, and a three-finger click for a
> > + * middle button click.
> > + */
> > +
> > +/**
> >   * @ingroup fixed_point
> >   *
> >   * libinput 24.8 fixed point real number.
> > -- 
> > 1.9.0
> > 
> > _______________________________________________
> > wayland-devel mailing list
> > wayland-devel at lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/wayland-devel
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
> 


More information about the wayland-devel mailing list