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

Jonas Ådahl jadahl at gmail.com
Wed Apr 23 14:35:35 PDT 2014


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
> 
> 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 08783a3..f953cd1 100644
> --- 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.

> + *
> + * 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


More information about the wayland-devel mailing list