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 <[email protected]> > > > > 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 <[email protected]> > > Signed-off-by: Hans de Goede <[email protected]> > > 1, 2, 3, 7, 8 and this one Reviewed-by: Jonas Ådahl <[email protected]> > 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 > > [email protected] > > http://lists.freedesktop.org/mailman/listinfo/wayland-devel > _______________________________________________ > wayland-devel mailing list > [email protected] > http://lists.freedesktop.org/mailman/listinfo/wayland-devel > _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
