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

Peter Hutterer peter.hutterer at who-t.net
Thu Mar 27 23:23:58 PDT 2014


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 left 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 the left button area but "immediately" moves over
  to a different button area, that second button 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.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 doc/Makefile.am                           |   2 +-
 doc/touchpad-softbutton-state-machine.svg | 561 ++++++++++++++++++++++++
 src/evdev-mt-touchpad-buttons.c           | 703 +++++++++++++++++++++++++++++-
 src/evdev-mt-touchpad.c                   |  15 +
 src/evdev-mt-touchpad.h                   |  44 ++
 src/libinput.h                            |  40 ++
 6 files changed, 1362 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..c28e8d3
--- /dev/null
+++ b/doc/touchpad-softbutton-state-machine.svg
@@ -0,0 +1,561 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+xmlns:xlink="http://www.w3.org/1999/xlink" width="1958px" height="1282px"
+version="1.1"><defs/><g transform="translate(0.5,0.5)"><path d="M 772 1189 L
+278 387" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 275 383 L 282 387 L 278 387 L 276 390 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 894 282 L 1069 502" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1072 506 L 1065 503 L 1069 502 L 1071 498 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><ellipse cx="878" cy="42"
+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="878"
+y="46">NONE</text></g><path d="M 737 127 C 739 124 743 122 748 122 L 781 122
+C 786 122 790 124 792 127 L 812 150 C 813 151 813 152 812 154 L 792 177 C
+790 180 786 182 781 182 L 748 182 C 743 182 739 180 737 177 L 717 154 C 716
+152 716 151 717 150 L 737 127 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="765" y="149">finger</text><text x="765" y="163">in
+R</text></g><path d="M 847 72 L 800 117" fill="none" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 796 121 L 799 114 L
+800 117 L 804 119 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><rect x="669" y="242" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="734"
+y="266">RIGHT_NEW</text></g><rect x="813" y="242" width="130" height="40"
+rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="878"
+y="266">LEFT_NEW</text></g><rect x="959" y="242" width="130" height="40"
+rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="1024"
+y="266">AREA</text></g><path d="M 756 182 L 741 236" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+739 241 L 738 233 L 741 236 L 745 235 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 867 422 C 869 419
+873 417 878 417 L 911 417 C 916 417 920 419 922 422 L 942 445 C 943 446 943
+447 942 449 L 922 472 C 920 475 916 477 911 477 L 878 477 C 873 477 869 475
+867 472 L 847 449 C 846 447 846 446 847 445 L 867 422 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="895" y="451">finger
+up</text></g><path d="M 943 447 L 1289 447 Q 1299 447 1299 437 L 1299 52 Q
+1299 42 1289 42 L 934 42" fill="none" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 929 42 L 936 38 L
+934 42 L 936 45 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 751 282 L 864 412" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+868 416 L 860 413 L 864 412 L 866 408 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 880 282 L 891 410"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 892 416 L 888 409 L 891 410 L 895 408 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1010 282 L 919 412" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+916 416 L 917 408 L 919 412 L 923 412 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 679 622 C 682 619
+686 617 690 617 L 724 617 C 728 617 732 619 735 622 L 755 645 C 756 646 756
+647 755 649 L 735 672 C 732 675 728 677 724 677 L 690 677 C 686 677 682 675
+679 672 L 659 649 C 659 647 659 646 659 645 L 679 622 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="707" y="644">finger
+in</text><text x="707" y="658">L</text></g><path d="M 732 282 L 710 610"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 709 616 L 706 608 L 710 610 L 713 609 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1069 512 C 1072 509 1076 507 1080 507 L
+1114 507 C 1118 507 1122 509 1125 512 L 1145 535 C 1146 536 1146 537 1145
+539 L 1125 562 C 1122 565 1118 567 1114 567 L 1080 567 C 1076 567 1072 565
+1069 562 L 1049 539 C 1049 537 1049 536 1049 535 L 1069 512 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="1097" y="534">finger
+in</text><text x="1097" y="548">AREA</text></g><rect x="1149" y="674"
+width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000"
+stroke-width="2" pointer-events="none"/><g fill="#000000"
+font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="1214"
+y="698">LEFT</text></g><path d="M 1863 619 C 1865 616 1869 614 1874 614 L
+1907 614 C 1912 614 1916 616 1918 619 L 1938 642 C 1939 644 1939 645 1938
+646 L 1918 669 C 1916 672 1912 674 1907 674 L 1874 674 C 1869 674 1865 672
+1863 669 L 1843 646 C 1842 645 1842 644 1843 642 L 1863 619 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="1891"
+y="634">phys</text><text x="1891" y="648">button</text><text x="1891"
+y="662">press</text></g><path d="M 1279 689 L 1836 648" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1841 648 L 1834 652 L 1836 648 L 1834 645 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1861 438 C 1862 437
+1862 435 1863 434 C 1864 434 1865 433 1866 433 L 1935 433 C 1936 433 1938
+434 1939 435 C 1940 436 1940 437 1940 438 L 1921 488 C 1920 489 1919 491
+1919 492 C 1918 492 1917 493 1916 493 L 1846 493 C 1845 493 1844 492 1843
+491 C 1842 490 1842 489 1842 488 Z" fill="#ff6666" 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="1891" y="460">button 1</text><text x="1891"
+y="474">press</text></g><path d="M 1891 614 L 1891 499" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1891 494 L 1894 501 L 1891 499 L 1887 501 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><rect x="677" y="787"
+width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000"
+stroke-width="2" pointer-events="none"/><g fill="#000000"
+font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="742"
+y="811">RIGHT_TO_LEFT</text></g><path d="M 714 677 L 736 781" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+737 786 L 732 780 L 736 781 L 739 778 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 511 622 C 513 619
+517 617 522 617 L 555 617 C 560 617 564 619 566 622 L 586 645 C 587 646 587
+647 586 649 L 566 672 C 564 675 560 677 555 677 L 522 677 C 517 677 513 675
+511 672 L 491 649 C 490 647 490 646 491 645 L 511 622 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="539" y="644">finger
+in</text><text x="539" y="658">AREA</text></g><path d="M 723 282 L 557 611"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 554 616 L 554 608 L 557 611 L 560 611 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 131 97 C 132 96 132 94 133 93 C 134 92 135
+92 136 92 L 205 92 C 206 92 208 92 209 93 C 210 94 210 96 210 97 L 191 147 C
+190 148 189 149 189 150 C 188 151 187 152 186 152 L 116 152 C 115 151 114
+151 113 150 C 112 149 112 148 112 147 Z" fill="#ff6666" 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="161" y="119">button 2</text><text x="161"
+y="133">press</text></g><rect x="239" y="72" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="304"
+y="96">PRESSED_RIGHT</text></g><path d="M 210 111 L 232 107" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+237 106 L 231 111 L 232 107 L 230 104 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 559 7 C 562 4 566 2
+570 2 L 604 2 C 608 2 612 4 615 7 L 635 30 C 636 31 636 32 635 34 L 615 57 C
+612 60 608 62 604 62 L 570 62 C 566 62 562 60 559 57 L 539 34 C 539 32 539
+31 539 30 L 559 7 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="587"
+y="22">phys</text><text x="587" y="36">button</text><text x="587"
+y="50">release</text></g><path d="M 369 78 L 532 43" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+537 42 L 531 47 L 532 43 L 530 40 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 558 62 L 287 337"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 283 341 L 285 334 L 287 337 L 290 338 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 582 137 C 585 134 589 132 593 132 L 627
+132 C 631 132 635 134 638 137 L 658 160 C 659 161 659 162 658 164 L 638 187
+C 635 190 631 192 627 192 L 593 192 C 589 192 585 190 582 187 L 562 164 C
+562 162 562 161 562 160 L 582 137 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="610" y="159">finger</text><text x="610"
+y="173">up</text></g><path d="M 829 64 L 664 137" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+660 140 L 664 134 L 664 137 L 667 140 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 639 132 L 639 73 Q
+639 63 649 63 L 836 63" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 842 63 L 835 67 L 836 63 L 835 60 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><rect x="198" y="342" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="263"
+y="366">RIGHT</text></g><path d="M 669 276 L 334 347" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+329 348 L 335 343 L 334 347 L 336 350 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 294 382 L 655 613"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 659 616 L 651 615 L 655 613 L 655 609 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 456 337 C 459 334 463 332 467 332 L 501
+332 C 505 332 509 334 512 337 L 532 360 C 533 361 533 362 532 364 L 512 387
+C 509 390 505 392 501 392 L 467 392 C 463 392 459 390 456 387 L 436 364 C
+436 362 436 361 436 360 L 456 337 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="484" y="359">inner</text><text x="484"
+y="373">timeout</text></g><path d="M 684 282 L 538 340" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+534 342 L 539 336 L 538 340 L 541 343 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 436 362 L 334 362"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 329 362 L 336 358 L 334 362 L 336 365 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 369 482 C 372 479 376 477 380 477 L 414
+477 C 418 477 422 479 425 482 L 445 505 C 446 506 446 507 445 509 L 425 532
+C 422 535 418 537 414 537 L 380 537 C 376 537 372 535 369 532 L 349 509 C
+349 507 349 506 349 505 L 369 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="397" y="504">finger in</text><text x="397"
+y="518">L</text></g><path d="M 121 482 C 123 479 127 477 132 477 L 165 477 C
+170 477 174 479 176 482 L 196 505 C 197 506 197 507 196 509 L 176 532 C 174
+535 170 537 165 537 L 132 537 C 127 537 123 535 121 532 L 101 509 C 100 507
+100 506 101 505 L 121 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="149" y="504">finger in</text><text x="149"
+y="518">AREA</text></g><path d="M 247 382 L 176 472" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+173 476 L 174 468 L 176 472 L 180 473 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><rect x="2" y="582"
+width="130" height="40" rx="2" ry="2" fill="#ccffcc" stroke="#000000"
+stroke-width="2" pointer-events="none"/><g fill="#000000"
+font-family="Helvetica" text-anchor="middle" font-size="12px"><text x="67"
+y="606">RIGHT_TO_AREA</text></g><path d="M 123 537 L 88 577" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 85
+581 L 87 573 L 88 577 L 92 578 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 324 802 C 327 799
+331 797 335 797 L 369 797 C 373 797 377 799 380 802 L 400 825 C 401 826 401
+827 400 829 L 380 852 C 377 855 373 857 369 857 L 335 857 C 331 857 327 855
+324 852 L 304 829 C 304 827 304 826 304 825 L 324 802 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="352"
+y="824">outer</text><text x="352" y="838">timeout</text></g><path d="M 92
+622 L 309 793" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 313 796 L 305 794 L 309 793 L 310 789 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 251 524 C 253 521 257 519 262 519 L 295
+519 C 300 519 304 521 306 524 L 326 547 C 327 549 327 550 326 551 L 306 574
+C 304 577 300 579 295 579 L 262 579 C 257 579 253 577 251 574 L 231 551 C
+230 550 230 549 231 547 L 251 524 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="279" y="546">finger in</text><text x="279"
+y="560">R</text></g><path d="M 132 586 L 224 563" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+229 562 L 223 567 L 224 563 L 221 560 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 276 519 L 265 388"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 264 383 L 268 390 L 265 388 L 261 390 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 281 382 L 365 472" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+368 476 L 361 473 L 365 472 L 366 468 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 446 482 L 833 285"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 838 282 L 833 289 L 833 285 L 830 282 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 369 612 C 372 609 376 607 380 607 L 414
+607 C 418 607 422 609 425 612 L 445 635 C 446 636 446 637 445 639 L 425 662
+C 422 665 418 667 414 667 L 380 667 C 376 667 372 665 369 662 L 349 639 C
+349 637 349 636 349 635 L 369 612 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="397" y="634">finger in</text><text x="397"
+y="648">L</text></g><path d="M 132 609 L 342 631" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+347 631 L 340 634 L 342 631 L 341 627 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 438 607 L 873 286"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 877 282 L 874 289 L 873 286 L 869 284 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 251 187 C 253 184 257 182 262 182 L 295
+182 C 300 182 304 184 306 187 L 326 210 C 327 211 327 212 326 214 L 306 237
+C 304 240 300 242 295 242 L 262 242 C 257 242 253 240 251 237 L 231 214 C
+230 212 230 211 231 210 L 251 187 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="279" y="202">phys</text><text x="279"
+y="216">button</text><text x="279" y="230">press</text></g><path d="M 239
+182 L 205 156" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 201 152 L 209 154 L 205 156 L 204 159 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 265 342 L 275 248" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+275 243 L 278 250 L 275 248 L 271 249 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 754 532 C 757 529
+761 527 765 527 L 799 527 C 803 527 807 529 810 532 L 830 555 C 831 556 831
+557 830 559 L 810 582 C 807 585 803 587 799 587 L 765 587 C 761 587 757 585
+754 582 L 734 559 C 734 557 734 556 734 555 L 754 532 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="782" y="554">finger
+in</text><text x="782" y="568">R</text></g><path d="M 871 282 L 794 521"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 792 526 L 791 518 L 794 521 L 798 520 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 777 527 L 738 288" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+737 283 L 742 289 L 738 288 L 735 290 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1089 507 L 1030
+288" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1029 283 L 1034 289 L 1030 288 L 1028 290
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 999 612 C 1002 609 1006 607 1010 607 L
+1044 607 C 1048 607 1052 609 1055 612 L 1075 635 C 1076 636 1076 637 1075
+639 L 1055 662 C 1052 665 1048 667 1044 667 L 1010 667 C 1006 667 1002 665
+999 662 L 979 639 C 979 637 979 636 979 635 L 999 612 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="1027"
+y="634">inner</text><text x="1027" y="648">timeout</text></g><path d="M 886
+282 L 1013 601" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1015 606 L 1009 600 L 1013 601 L 1015 598
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1076 652 L 1143 672" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1148 674 L 1140 675 L 1143 672 L 1142 669 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1109 792 C 1112 789
+1116 787 1120 787 L 1154 787 C 1158 787 1162 789 1165 792 L 1185 815 C 1186
+816 1186 817 1185 819 L 1165 842 C 1162 845 1158 847 1154 847 L 1120 847 C
+1116 847 1112 845 1109 842 L 1089 819 C 1089 817 1089 816 1089 815 L 1109
+792 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="1137"
+y="814">finger in</text><text x="1137" y="828">AREA</text></g><path d="M
+1201 714 L 1159 781" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1156 786 L 1157 778 L 1159 781 L 1163 782
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><rect x="1049" y="904" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="1114"
+y="928">LEFT_TO_AREA</text></g><path d="M 1130 847 L 1119 898" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1118 903 L 1116 896 L 1119 898 L 1123 897 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1229 997 C 1232 994
+1236 992 1240 992 L 1274 992 C 1278 992 1282 994 1285 997 L 1305 1020 C 1306
+1021 1306 1022 1305 1024 L 1285 1047 C 1282 1050 1278 1052 1274 1052 L 1240
+1052 C 1236 1052 1232 1050 1229 1047 L 1209 1024 C 1209 1022 1209 1021 1209
+1020 L 1229 997 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="1257"
+y="1019">finger in</text><text x="1257" y="1033">L</text></g><path d="M 1143
+944 L 1208 988" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1212 991 L 1204 990 L 1208 988 L 1208 984
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1253 992 L 1217 721" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1216 715 L 1221 722 L 1217 721 L 1214 723 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 931 929 C 933 926
+937 924 942 924 L 975 924 C 980 924 984 926 986 929 L 1006 952 C 1007 954
+1007 955 1006 956 L 986 979 C 984 982 980 984 975 984 L 942 984 C 937 984
+933 982 931 979 L 911 956 C 910 955 910 954 911 952 L 931 929 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="959"
+y="951">outer</text><text x="959" y="965">timeout</text></g><path d="M 1049
+937 L 1013 944" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1008 945 L 1014 940 L 1013 944 L 1016 947
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 961 924 L 1021 288" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1022 283 L 1024 290 L 1021 288 L 1017 290 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1072 1012 C 1075
+1009 1079 1007 1083 1007 L 1117 1007 C 1121 1007 1125 1009 1128 1012 L 1148
+1035 C 1149 1036 1149 1037 1148 1039 L 1128 1062 C 1125 1065 1121 1067 1117
+1067 L 1083 1067 C 1079 1067 1075 1065 1072 1062 L 1052 1039 C 1052 1037
+1052 1036 1052 1035 L 1072 1012 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="1100" y="1034">finger in</text><text x="1100"
+y="1048">R</text></g><path d="M 1111 944 L 1104 1000" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1104 1006 L 1101 998 L 1104 1000 L 1108 999 Z" fill="#000000"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1169 904 L 1836 664" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1841 662 L 1836 668 L 1836 664 L 1833 661
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 931 282 L 1836 624" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1841 626 L 1833 626 L 1836 624 L 1836 620 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 78 582 L 259 247"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 262 243 L 261 251 L 259 247 L 255 247 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 388 797 L 995 286" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+999 282 L 996 290 L 995 286 L 991 284 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 576 617 L 993 286"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 997 282 L 994 290 L 993 286 L 990 284 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><rect x="1826" y="289" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="1891"
+y="313">PRESSED_LEFT</text></g><path d="M 1891 433 L 1891 336" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1891 330 L 1894 337 L 1891 336 L 1887 337 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1363 157 C 1365 154
+1369 152 1374 152 L 1407 152 C 1412 152 1416 154 1418 157 L 1438 180 C 1439
+181 1439 182 1438 184 L 1418 207 C 1416 210 1412 212 1407 212 L 1374 212 C
+1369 212 1365 210 1363 207 L 1343 184 C 1342 182 1342 181 1343 180 L 1363
+157 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="1391"
+y="172">phys</text><text x="1391" y="186">button</text><text x="1391"
+y="200">release</text></g><path d="M 1849 289 L 1535 138" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1530 136 L 1538 135 L 1535 138 L 1535 142 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1342 192 L 1095
+246" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1090 247 L 1096 242 L 1095 246 L 1097 249
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 466 1009 C 469 1006 473 1004 477 1004 L
+511 1004 C 515 1004 519 1006 522 1009 L 542 1032 C 543 1034 543 1035 542
+1036 L 522 1059 C 519 1062 515 1064 511 1064 L 477 1064 C 473 1064 469 1062
+466 1059 L 446 1036 C 446 1035 446 1034 446 1032 L 466 1009 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="494" y="1031">finger
+in</text><text x="494" y="1045">R</text></g><path d="M 720 827 L 531 1000"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 528 1003 L 530 996 L 531 1000 L 535 1001
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 484 1004 L 271 388" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+270 383 L 275 388 L 271 388 L 269 391 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 785 899 C 788 896
+792 894 796 894 L 830 894 C 834 894 838 896 841 899 L 861 922 C 862 924 862
+925 861 926 L 841 949 C 838 952 834 954 830 954 L 796 954 C 792 954 788 952
+785 949 L 765 926 C 765 925 765 924 765 922 L 785 899 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="813"
+y="921">outer</text><text x="813" y="935">timeout</text></g><path d="M 754
+827 L 792 889" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 794 893 L 788 889 L 792 889 L 794 885 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 862 896 L 1173 717" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1178 715 L 1173 721 L 1173 717 L 1170 715 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 726 787 L 283 247"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 279 243 L 286 246 L 283 247 L 281 250 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><rect x="970" y="1239" width="130" height="40" rx="2"
+ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="1035"
+y="1263">LEFT_TO_RIGHT</text></g><path d="M 1091 1067 L 1043 1233"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1041 1238 L 1040 1230 L 1043 1233 L 1046
+1232 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 763 1194 C 765 1191 769 1189 774 1189 L
+807 1189 C 812 1189 816 1191 818 1194 L 838 1217 C 839 1219 839 1220 838
+1221 L 818 1244 C 816 1247 812 1249 807 1249 L 774 1249 C 769 1249 765 1247
+763 1244 L 743 1221 C 742 1220 742 1219 743 1217 L 763 1194 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="790"
+y="1216">outer</text><text x="790" y="1230">timeout</text></g><path d="M 970
+1246 L 845 1221" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 840 1219 L 848 1217 L 845 1221 L 846 1224
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1056 1239 L 1252 1056" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1256 1053 L 1253 1060 L 1252 1056 L 1249 1055 Z" fill="#000000"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+669 255 L 333 218" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 328 217 L 335 214 L 333 218 L 335 221 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 849 127 C 852 124 856 122 860 122 L 894
+122 C 898 122 902 124 905 127 L 925 150 C 926 151 926 152 925 154 L 905 177
+C 902 180 898 182 894 182 L 860 182 C 856 182 852 180 849 177 L 829 154 C
+829 152 829 151 829 150 L 849 127 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="877" y="149">finger in</text><text x="877"
+y="163">L</text></g><path d="M 878 72 L 877 115" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+877 121 L 874 114 L 877 115 L 881 114 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 877 182 L 878 235"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 878 241 L 874 234 L 878 235 L 881 234 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 991 127 C 993 124 997 122 1002 122 L 1035
+122 C 1040 122 1044 124 1046 127 L 1066 150 C 1067 151 1067 152 1066 154 L
+1046 177 C 1044 180 1040 182 1035 182 L 1002 182 C 997 182 993 180 991 177 L
+971 154 C 970 152 970 151 971 150 L 991 127 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="1019" y="149">finger
+in</text><text x="1019" y="163">AREA</text></g><path d="M 916 72 L 975 118"
+fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 979 121 L 972 120 L 975 118 L 976 114 Z"
+fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1020 182 L 1022 235" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1023 241 L 1019 234 L 1022 235 L 1026 233 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1411 1027 C 1414
+1024 1418 1022 1422 1022 L 1456 1022 C 1460 1022 1464 1024 1467 1027 L 1487
+1050 C 1488 1051 1488 1052 1487 1054 L 1467 1077 C 1464 1080 1460 1082 1456
+1082 L 1422 1082 C 1418 1082 1414 1080 1411 1077 L 1391 1054 C 1391 1052
+1391 1051 1391 1050 L 1411 1027 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="1439" y="1049">finger in</text><text x="1439"
+y="1063">R</text></g><path d="M 1226 714 L 1417 1016" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1419 1021 L 1413 1017 L 1417 1016 L 1419 1013 Z" fill="#000000"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1391 1077 L 1080 1236" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1075 1239 L 1080 1232 L 1080 1236 L 1083
+1239 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 494 1134 C 497 1131 501 1129 505 1129 L
+539 1129 C 543 1129 547 1131 550 1134 L 570 1157 C 571 1159 571 1160 570
+1161 L 550 1184 C 547 1187 543 1189 539 1189 L 505 1189 C 501 1189 497 1187
+494 1184 L 474 1161 C 474 1160 474 1159 474 1157 L 494 1134 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="522" y="1156">finger
+in</text><text x="522" y="1170">AREA</text></g><path d="M 730 827 L 544
+1124" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 541 1128 L 542 1121 L 544 1124 L 548 1124
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 498 1129 L 87 627" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 84
+623 L 91 626 L 87 627 L 86 630 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1543 567 C 1545 564
+1549 562 1554 562 L 1587 562 C 1592 562 1596 564 1598 567 L 1618 590 C 1619
+591 1619 592 1618 594 L 1598 617 C 1596 620 1592 622 1587 622 L 1554 622 C
+1549 622 1545 620 1543 617 L 1523 594 C 1522 592 1522 591 1523 590 L 1543
+567 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="1571"
+y="582">phys</text><text x="1571" y="596">button</text><text x="1571"
+y="610">press</text></g><path d="M 1057 282 L 1517 559" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1521 562 L 1513 561 L 1517 559 L 1517 555 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1581 428 C 1582 427
+1582 425 1583 424 C 1584 424 1585 423 1586 423 L 1655 423 C 1656 423 1658
+424 1659 425 C 1660 426 1660 427 1660 428 L 1641 478 C 1640 479 1639 481
+1639 482 C 1638 482 1637 483 1636 483 L 1566 483 C 1565 483 1564 482 1563
+481 C 1562 480 1562 479 1562 478 Z" fill="#ff6666" 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="1611" y="450">button 1</text><text x="1611"
+y="464">press</text></g><rect x="1522" y="282" width="130" height="40"
+rx="2" ry="2" fill="#ccffcc" stroke="#000000" stroke-width="2"
+pointer-events="none"/><g fill="#000000" font-family="Helvetica"
+text-anchor="middle" font-size="12px"><text x="1587"
+y="306">PRESSED_AREA</text></g><path d="M 1579 562 L 1600 489" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1602 484 L 1603 492 L 1600 489 L 1597 490 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1606 423 L 1591
+328" fill="none" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1590 323 L 1595 329 L 1591 328 L 1588 330
+Z" fill="#000000" stroke="#000000" stroke-miterlimit="10"
+pointer-events="none"/><path d="M 1554 282 L 1444 215" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1440 212 L 1448 213 L 1444 215 L 1444 219 Z" fill="#000000" stroke="#000000"
+stroke-miterlimit="10" pointer-events="none"/><path d="M 1453 87 C 1455 84
+1459 82 1464 82 L 1497 82 C 1502 82 1506 84 1508 87 L 1528 110 C 1529 111
+1529 112 1528 114 L 1508 137 C 1506 140 1502 142 1497 142 L 1464 142 C 1459
+142 1455 140 1453 137 L 1433 114 C 1432 112 1432 111 1433 110 L 1453 87 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="1481"
+y="102">phys</text><text x="1481" y="116">button</text><text x="1481"
+y="130">release</text></g><path d="M 1467 142 L 1225 668" fill="none"
+stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M
+1223 673 L 1223 665 L 1225 668 L 1229 668 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..b538d1b 100644
--- a/src/evdev-mt-touchpad-buttons.c
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -20,11 +20,600 @@
  * 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!
+ *
+ * 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.
+ */
+
+enum button_event {
+       BUTTON_EVENT_IN_R = 30,
+       BUTTON_EVENT_IN_L,
+       BUTTON_EVENT_IN_AREA,
+       BUTTON_EVENT_UP,
+       BUTTON_EVENT_PRESS,
+       BUTTON_EVENT_RELEASE,
+       BUTTON_EVENT_TIMEOUT,
+};
+
+#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_LEFT);
+	CASE_RETURN_STRING(BUTTON_STATE_LEFT_NEW);
+	CASE_RETURN_STRING(BUTTON_STATE_RIGHT);
+	CASE_RETURN_STRING(BUTTON_STATE_RIGHT_NEW);
+	CASE_RETURN_STRING(BUTTON_STATE_LEFT_TO_AREA);
+	CASE_RETURN_STRING(BUTTON_STATE_RIGHT_TO_AREA);
+	CASE_RETURN_STRING(BUTTON_STATE_LEFT_TO_RIGHT);
+	CASE_RETURN_STRING(BUTTON_STATE_RIGHT_TO_LEFT);
+	CASE_RETURN_STRING(BUTTON_STATE_PRESSED_RIGHT);
+	CASE_RETURN_STRING(BUTTON_STATE_PRESSED_LEFT);
+	CASE_RETURN_STRING(BUTTON_STATE_PRESSED_AREA);
+	}
+	return NULL;
+}
+
+static inline const char*
+button_event_to_str(enum button_event event) {
+	switch(event) {
+	CASE_RETURN_STRING(BUTTON_EVENT_IN_R);
+	CASE_RETURN_STRING(BUTTON_EVENT_IN_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;
+}
+
+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_R:
+		t->button.state = BUTTON_STATE_RIGHT_NEW;
+		tp_button_set_enter_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_LEFT_NEW;
+		tp_button_set_enter_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_AREA;
+		break;
+	case BUTTON_EVENT_UP:
+		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)
+{
+	tp_button_clear_timer(tp, t);
+
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+	case BUTTON_EVENT_IN_L:
+	case BUTTON_EVENT_IN_AREA:
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_AREA;
+		break;
+	case BUTTON_EVENT_RELEASE:
+	case BUTTON_EVENT_TIMEOUT:
+		break;
+	}
+}
+
+static void
+tp_button_left_handle_event(struct tp_dispatch *tp,
+			    struct tp_touch *t,
+			    enum button_event event)
+{
+	tp_button_clear_timer(tp, t);
+
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		t->button.state = BUTTON_STATE_LEFT_TO_RIGHT;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_LEFT_TO_AREA;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_LEFT;
+		break;
+	case BUTTON_EVENT_RELEASE:
+	case BUTTON_EVENT_TIMEOUT:
+		break;
+	}
+}
+
+static void
+tp_button_left_new_handle_event(struct tp_dispatch *tp,
+				struct tp_touch *t,
+				enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		t->button.state = BUTTON_STATE_RIGHT_NEW;
+		tp_button_set_enter_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_LEFT_TO_AREA;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_LEFT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_LEFT;
+		break;
+	}
+}
+
+static void
+tp_button_right_handle_event(struct tp_dispatch *tp,
+			     struct tp_touch *t,
+			     enum button_event event)
+{
+	tp_button_clear_timer(tp, t);
+
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_RIGHT_TO_LEFT;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_RIGHT_TO_AREA;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_RIGHT;
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+	case BUTTON_EVENT_RELEASE:
+		break;
+	}
+}
+
+static void
+tp_button_right_new_handle_event(struct tp_dispatch *tp,
+				 struct tp_touch *t,
+				 enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_LEFT_NEW;
+		tp_button_set_enter_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_AREA;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_RIGHT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_RIGHT;
+		break;
+	}
+}
+
+static void
+tp_button_left_to_area_handle_event(struct tp_dispatch *tp,
+				    struct tp_touch *t,
+				    enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		t->button.state = BUTTON_STATE_LEFT_TO_RIGHT;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_LEFT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_LEFT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_AREA;
+		break;
+	}
+}
+
+static void
+tp_button_right_to_area_handle_event(struct tp_dispatch *tp,
+				     struct tp_touch *t,
+				     enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		t->button.state = BUTTON_STATE_RIGHT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_RIGHT_TO_LEFT;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_RIGHT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_AREA;
+		break;
+	}
+}
+
+static void
+tp_button_left_to_right_handle_event(struct tp_dispatch *tp,
+				     struct tp_touch *t,
+				     enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		break;
+	case BUTTON_EVENT_IN_L:
+		t->button.state = BUTTON_STATE_LEFT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_LEFT_TO_AREA;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_LEFT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_RIGHT;
+		break;
+	}
+}
+
+static void
+tp_button_right_to_left_handle_event(struct tp_dispatch *tp,
+				     struct tp_touch *t,
+				     enum button_event event)
+{
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+		t->button.state = BUTTON_STATE_RIGHT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_IN_L:
+		break;
+	case BUTTON_EVENT_IN_AREA:
+		t->button.state = BUTTON_STATE_RIGHT_TO_AREA;
+		tp_button_set_leave_timer(tp, t);
+		break;
+	case BUTTON_EVENT_UP:
+		t->button.state = BUTTON_STATE_NONE;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_PRESS:
+		t->button.state = BUTTON_STATE_PRESSED_RIGHT;
+		tp_button_clear_timer(tp, t);
+		break;
+	case BUTTON_EVENT_RELEASE:
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		t->button.state = BUTTON_STATE_LEFT;
+		break;
+	}
+}
+
+static void
+tp_button_pressed_right_handle_event(struct tp_dispatch *tp,
+				     struct tp_touch *t,
+				     enum button_event event)
+{
+	tp_button_clear_timer(tp, t);
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+	case BUTTON_EVENT_IN_L:
+	case BUTTON_EVENT_IN_AREA:
+	case BUTTON_EVENT_UP:
+	case BUTTON_EVENT_PRESS:
+		break;
+	case BUTTON_EVENT_RELEASE:
+		t->button.state = BUTTON_STATE_RIGHT;
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		break;
+	}
+}
+
+static void
+tp_button_pressed_left_handle_event(struct tp_dispatch *tp,
+				    struct tp_touch *t,
+				    enum button_event event)
+{
+	tp_button_clear_timer(tp, t);
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+	case BUTTON_EVENT_IN_L:
+	case BUTTON_EVENT_IN_AREA:
+	case BUTTON_EVENT_UP:
+	case BUTTON_EVENT_PRESS:
+		break;
+	case BUTTON_EVENT_RELEASE:
+		t->button.state = BUTTON_STATE_LEFT;
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		break;
+	}
+}
+
+static void
+tp_button_pressed_area_handle_event(struct tp_dispatch *tp,
+				    struct tp_touch *t,
+				    enum button_event event)
+{
+	tp_button_clear_timer(tp, t);
+	switch(event) {
+	case BUTTON_EVENT_IN_R:
+	case BUTTON_EVENT_IN_L:
+	case BUTTON_EVENT_IN_AREA:
+	case BUTTON_EVENT_UP:
+	case BUTTON_EVENT_PRESS:
+		break;
+	case BUTTON_EVENT_RELEASE:
+		t->button.state = BUTTON_STATE_AREA;
+		break;
+	case BUTTON_EVENT_TIMEOUT:
+		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_LEFT:
+		tp_button_left_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_LEFT_NEW:
+		tp_button_left_new_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_RIGHT:
+		tp_button_right_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_RIGHT_NEW:
+		tp_button_right_new_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_LEFT_TO_AREA:
+		tp_button_left_to_area_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_RIGHT_TO_AREA:
+		tp_button_right_to_area_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_LEFT_TO_RIGHT:
+		tp_button_left_to_right_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_RIGHT_TO_LEFT:
+		tp_button_right_to_left_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_PRESSED_RIGHT:
+		tp_button_pressed_right_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_PRESSED_LEFT:
+		tp_button_pressed_left_handle_event(tp, t, event);
+		break;
+	case BUTTON_STATE_PRESSED_AREA:
+		tp_button_pressed_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->fake)
+			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_R, time);
+			else if (is_inside_left_area(tp, t))
+				tp_button_handle_event(tp, t, BUTTON_EVENT_IN_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 +632,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 +673,44 @@ 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;
+	}
 
 	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 +779,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.state == BUTTON_STATE_PRESSED_RIGHT)
+				button |= 0x2;
+			else if (t->button.state == BUTTON_STATE_PRESSED_LEFT)
+				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 +845,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 1c03d42..de7a7e1 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -423,6 +423,8 @@ tp_process_state(struct tp_dispatch *tp, uint32_t time)
 		tp_unpin_finger(tp, t);
 	}
 
+	tp_button_handle_state(tp, time);
+
 	if (!tp->buttons.has_buttons) {
 		/* We have a physical button down event on a clickpad. For drag and
 		   drop, this means we try to identify which finger pressed the
@@ -622,6 +624,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);
@@ -634,10 +637,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);
@@ -673,6 +684,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;
 }
 
@@ -714,6 +728,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 f3e5b31..fa4d932 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -46,6 +46,22 @@ enum touch_state {
 	TOUCH_END
 };
 
+enum button_state {
+       BUTTON_STATE_NONE,
+       BUTTON_STATE_AREA,
+       BUTTON_STATE_LEFT,
+       BUTTON_STATE_LEFT_NEW,
+       BUTTON_STATE_RIGHT,
+       BUTTON_STATE_RIGHT_NEW,
+       BUTTON_STATE_LEFT_TO_AREA,
+       BUTTON_STATE_RIGHT_TO_AREA,
+       BUTTON_STATE_LEFT_TO_RIGHT,
+       BUTTON_STATE_RIGHT_TO_LEFT,
+       BUTTON_STATE_PRESSED_RIGHT,
+       BUTTON_STATE_PRESSED_LEFT,
+       BUTTON_STATE_PRESSED_AREA,
+};
+
 enum scroll_state {
 	SCROLL_STATE_NONE,
 	SCROLL_STATE_SCROLLING
@@ -107,6 +123,12 @@ struct tp_touch {
 		int32_t center_x;
 		int32_t center_y;
 	} pinned;
+
+	/* Software-button state and timeout if applicable */
+	struct {
+		enum button_state state;
+		uint32_t timeout;
+	} button;
 };
 
 struct tp_dispatch {
@@ -136,10 +158,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 {
@@ -179,6 +217,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,
@@ -187,4 +228,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 f291ce8..7c68351 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -35,6 +35,46 @@
  */
 
 /**
+ * @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.8.5.3



More information about the wayland-devel mailing list