[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