From: Peter Hutterer <peter.hutte...@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.hutte...@who-t.net> Signed-off-by: Hans de Goede <hdego...@redhat.com> Reviewed-by: Jonas Ã…dahl <jad...@gmail.com> Reviewed-by: Hans de Goede <hdego...@redhat.com> Reviewed-by: Peter Hutterer <peter.hutte...@who-t.net> --- doc/Makefile.am | 2 +- doc/touchpad-softbutton-state-machine.svg | 173 ++++++++++++ src/evdev-mt-touchpad-buttons.c | 450 +++++++++++++++++++++++++++++- src/evdev-mt-touchpad.c | 15 + src/evdev-mt-touchpad.h | 48 ++++ src/libinput.h | 40 +++ 6 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 doc/touchpad-softbutton-state-machine.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index 75fa98a..a33638d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,4 +1,4 @@ -EXTRA_DIST = touchpad-tap-state-machine.svg +EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg if HAVE_DOXYGEN diff --git a/doc/touchpad-softbutton-state-machine.svg b/doc/touchpad-softbutton-state-machine.svg new file mode 100644 index 0000000..1838e35 --- /dev/null +++ b/doc/touchpad-softbutton-state-machine.svg @@ -0,0 +1,173 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="516px" height="759px" version="1.1"> +<defs/> +<g transform="translate(0.5,0.5)"> +<path d="M 190 352 L 216 352" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 221 352 L 214 355 L 216 352 L 214 348 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<ellipse cx="113" cy="61" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="113" y="51"> +NONE</text> +<text x="113" y="65"> +on-entry:</text> +<text x="113" y="79"> +curr = none</text> +</g> +<rect x="40" y="301" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="115" y="335"> +BOTTOM_NEW</text> +<text x="115" y="349"> +on-entry:</text> +<text x="115" y="363"> +curr = button</text> +<text x="115" y="377"> +start inner timeout</text> +</g> +<rect x="351" y="303" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="416" y="343"> +AREA</text> +<text x="416" y="357"> +on-entry:</text> +<text x="416" y="371"> +curr =area</text> +</g> +<path d="M 243 327 C 245 324 249 322 254 322 L 287 322 C 292 322 296 324 298 327 L 318 350 C 319 351 319 353 318 354 L 298 377 C 296 380 292 382 287 382 L 254 382 C 249 382 245 380 243 377 L 223 354 C 222 353 222 351 223 350 L 243 327 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="271" y="349"> +finger in</text> +<text x="271" y="363"> +area</text> +</g> +<rect x="50" y="623" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="115" y="677"> +BOTTOM</text> +</g> +<path d="M 243 6 C 245 3 249 1 254 1 L 287 1 C 292 1 296 3 298 6 L 318 29 C 319 31 319 32 318 33 L 298 56 C 296 59 292 61 287 61 L 254 61 C 249 61 245 59 243 56 L 223 33 C 222 32 222 31 223 29 L 243 6 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="271" y="28"> +finger</text> +<text x="271" y="42"> +up</text> +</g> +<path d="M 22 482 C 25 479 29 477 33 477 L 67 477 C 71 477 75 479 78 482 L 98 505 C 99 506 99 508 98 509 L 78 532 C 75 535 71 537 67 537 L 33 537 C 29 537 25 535 22 532 L 2 509 C 2 508 2 506 2 505 L 22 482 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="50" y="497"> +phys</text> +<text x="50" y="511"> +button</text> +<text x="50" y="525"> +press</text> +</g> +<path d="M 319 352 L 344 352" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 350 352 L 343 356 L 344 352 L 343 349 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 136 482 C 138 479 142 477 147 477 L 180 477 C 185 477 189 479 191 482 L 211 505 C 212 506 212 508 211 509 L 191 532 C 189 535 185 537 180 537 L 147 537 C 142 537 138 535 136 532 L 116 509 C 115 508 115 506 116 505 L 136 482 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="163" y="504"> +inner</text> +<text x="163" y="518"> +timeout</text> +</g> +<path d="M 115 403 L 147 471" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 149 476 L 143 471 L 147 471 L 149 468 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 155 537 L 131 616" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 130 621 L 129 614 L 131 616 L 135 616 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 223 604 C 226 601 230 599 234 599 L 268 599 C 272 599 276 601 279 604 L 299 627 C 299 628 299 629 299 631 L 279 654 C 276 657 272 659 268 659 L 234 659 C 230 659 226 657 223 654 L 203 631 C 202 629 202 628 203 627 L 223 604 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="251" y="626"> +finger in</text> +<text x="251" y="640"> +AREA</text> +</g> +<path d="M 180 673 L 199 634" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 202 630 L 202 638 L 199 634 L 195 634 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<rect x="319" y="629" width="194" height="94" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="416" y="666"> +BOTTOM_TO_AREA</text> +<text x="416" y="680"> +on-entry:</text> +<text x="416" y="694"> +start outer timeout</text> +</g> +<path d="M 299 629 L 317 670" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 319 675 L 313 670 L 317 670 L 319 667 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 388 452 C 391 449 395 447 399 447 L 433 447 C 437 447 441 449 444 452 L 464 475 C 464 476 464 478 464 479 L 444 502 C 441 505 437 507 433 507 L 399 507 C 395 507 391 505 388 502 L 368 479 C 367 478 367 476 368 475 L 388 452 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="416" y="474"> +outer</text> +<text x="416" y="488"> +timeout</text> +</g> +<path d="M 416 629 L 416 513" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 416 508 L 419 515 L 416 513 L 412 515 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 416 447 L 416 409" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 416 404 L 419 411 L 416 409 L 412 411 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 94 146 C 96 143 99 141 102 141 L 128 141 C 131 141 134 143 136 146 L 152 170 C 152 171 152 172 152 174 L 136 197 C 134 201 131 202 128 203 L 102 203 C 99 202 96 201 94 197 L 78 174 C 78 172 78 171 78 170 L 94 146 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="115" y="169"> +finger in</text> +<text x="115" y="183"> +bottom</text> +</g> +<path d="M 113 91 L 114 134" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 114 140 L 111 133 L 114 134 L 118 132 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 115 203 L 115 295" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 115 300 L 111 293 L 115 295 L 118 293 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 396 146 C 398 143 401 141 404 141 L 428 141 C 431 141 433 143 435 146 L 449 170 C 450 171 450 173 449 174 L 435 197 C 433 201 431 202 428 203 L 404 203 C 401 202 398 201 396 197 L 382 174 C 382 173 382 171 382 170 L 396 146 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="416" y="169"> +finger in</text> +<text x="416" y="183"> +area</text> +</g> +<path d="M 416 203 L 416 296" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 416 301 L 412 294 L 416 296 L 419 294 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 162 61 L 376 157" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 381 159 L 373 159 L 376 157 L 376 153 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 248 474 C 251 469 256 467 260 467 L 297 467 C 301 467 306 469 309 474 L 330 504 C 331 506 331 508 330 510 L 309 540 C 306 544 301 547 297 547 L 260 547 C 256 547 251 544 248 540 L 227 510 C 226 508 226 506 227 504 L 248 474 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="278" y="497"> +finger in</text> +<text x="278" y="511"> +bottom</text> +<text x="278" y="525"> +button != curr</text> +</g> +<path d="M 253 468 L 200 398" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 197 393 L 204 397 L 200 398 L 198 401 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 378 629 L 315 552" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 312 548 L 319 551 L 315 552 L 313 555 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<ellipse cx="413" cy="31" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="413" y="35"> +ANY</text> +</g> +<path d="M 364 31 L 325 31" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 320 31 L 327 28 L 325 31 L 327 35 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 222 31 L 168 50" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 163 51 L 169 46 L 168 50 L 171 53 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 50 537 L 111 617" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 114 622 L 107 618 L 111 617 L 113 614 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 115 403 L 54 472" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 51 476 L 53 468 L 54 472 L 58 473 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 224 682 C 227 678 231 676 236 676 L 272 676 C 277 676 281 678 284 682 L 305 713 C 306 715 306 717 305 718 L 284 749 C 281 753 277 756 272 756 L 236 756 C 231 756 227 753 224 749 L 203 718 C 202 717 202 715 203 713 L 224 682 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/> +<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px"> +<text x="254" y="699"> +finger in</text> +<text x="254" y="713"> +bottom</text> +<text x="254" y="727"> +button == curr</text> +</g> +<path d="M 319 676 L 308 710" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 306 715 L 305 707 L 308 710 L 312 709 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 202 716 L 183 678" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 181 674 L 187 678 L 183 678 L 181 681 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 164 623 L 234 551" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 238 548 L 236 555 L 234 551 L 231 550 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 136 403 L 221 465" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +<path d="M 225 468 L 217 467 L 221 465 L 222 461 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> +</g> +</svg> diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index ef514f7..61055ac 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -20,11 +20,345 @@ * 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 */ + +/***************************************** + * BEFORE YOU EDIT THIS FILE, 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); + + t->button.state = new_state; + switch (t->button.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; + } +} + +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)); +} + +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->state == TOUCH_END) { + tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time); + } 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 +377,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 +418,46 @@ 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; + } 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 +526,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 +592,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 c86b057..7f73f6e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -399,6 +399,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. @@ -596,6 +598,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); @@ -608,10 +611,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); @@ -649,6 +660,9 @@ tp_init_slots(struct tp_dispatch *tp, if (!tp->touches) return -1; + for (i = 0; i < tp->ntouches; i++) + tp_init_touch(tp, &tp->touches[i]); + return 0; } @@ -690,6 +704,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 85c7d71..d771e21 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@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel