--- /dev/null
+<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>
* 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 /* 2% of size */
+#define DEFAULT_BUTTON_ENTER_TIMEOUT 100 /* ms */
+#define DEFAULT_BUTTON_LEAVE_TIMEOUT 300 /* ms */
+
+/*****************************************
+ * BEFORE YOU EDIT THIS FILE, look at the state diagram in
+ * doc/touchpad-softbutton-state-machine.svg, or online at
+ * https://drive.google.com/file/d/0B1NwWmji69nocUs1cVJTbkdwMFk/edit?usp=sharing
+ * (it's a http://draw.io diagram)
+ *
+ * Any changes in this file must be represented in the diagram.
+ *
+ * The state machine only affects the soft button area code.
+ */
+
+#define CASE_RETURN_STRING(a) case a: return #a;
+
+static inline const char*
+button_state_to_str(enum button_state state) {
+ switch(state) {
+ CASE_RETURN_STRING(BUTTON_STATE_NONE);
+ CASE_RETURN_STRING(BUTTON_STATE_AREA);
+ CASE_RETURN_STRING(BUTTON_STATE_BOTTOM);
+ CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_NEW);
+ CASE_RETURN_STRING(BUTTON_STATE_BOTTOM_TO_AREA);
+ }
+ return NULL;
+}
+
+static inline const char*
+button_event_to_str(enum button_event event) {
+ switch(event) {
+ CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R);
+ CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L);
+ CASE_RETURN_STRING(BUTTON_EVENT_IN_AREA);
+ CASE_RETURN_STRING(BUTTON_EVENT_UP);
+ CASE_RETURN_STRING(BUTTON_EVENT_PRESS);
+ CASE_RETURN_STRING(BUTTON_EVENT_RELEASE);
+ CASE_RETURN_STRING(BUTTON_EVENT_TIMEOUT);
+ }
+ return NULL;
+}
+
+static inline bool
+is_inside_button_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return t->y >= tp->buttons.area.top_edge;
+}
+
+static inline bool
+is_inside_right_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return is_inside_button_area(tp, t) &&
+ t->x > tp->buttons.area.rightbutton_left_edge;
+}
+
+static inline bool
+is_inside_left_area(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return is_inside_button_area(tp, t) &&
+ !is_inside_right_area(tp, t);
+}
+
+static void
+tp_button_set_timer(struct tp_dispatch *tp, uint32_t timeout)
+{
+ struct itimerspec its;
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = timeout / 1000;
+ its.it_value.tv_nsec = (timeout % 1000) * 1000 * 1000;
+ timerfd_settime(tp->buttons.timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
+}
+
+static void
+tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ t->button.timeout = t->millis + DEFAULT_BUTTON_ENTER_TIMEOUT;
+ tp_button_set_timer(tp, t->button.timeout);
+}
+
+static void
+tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ t->button.timeout = t->millis + DEFAULT_BUTTON_LEAVE_TIMEOUT;
+ tp_button_set_timer(tp, t->button.timeout);
+}
+
+static void
+tp_button_clear_timer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ t->button.timeout = 0;
+}
+
+/*
+ * tp_button_set_state, change state and implement on-entry behavior
+ * as described in the state machine diagram.
+ */
+static void
+tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t,
+ enum button_state new_state, enum button_event event)
+{
+ tp_button_clear_timer(tp, t);
+
+ t->button.state = new_state;
+ switch (t->button.state) {
+ case BUTTON_STATE_NONE:
+ t->button.curr = 0;
+ break;
+ case BUTTON_STATE_AREA:
+ t->button.curr = BUTTON_EVENT_IN_AREA;
+ break;
+ case BUTTON_STATE_BOTTOM:
+ break;
+ case BUTTON_STATE_BOTTOM_NEW:
+ t->button.curr = event;
+ tp_button_set_enter_timer(tp, t);
+ break;
+ case BUTTON_STATE_BOTTOM_TO_AREA:
+ tp_button_set_leave_timer(tp, t);
+ break;
+ }
+}
+
+static void
+tp_button_none_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event)
+{
+ switch (event) {
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW, event);
+ break;
+ case BUTTON_EVENT_IN_AREA:
+ tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+ break;
+ case BUTTON_EVENT_UP:
+ tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+ break;
+ case BUTTON_EVENT_PRESS:
+ case BUTTON_EVENT_RELEASE:
+ case BUTTON_EVENT_TIMEOUT:
+ break;
+ }
+}
+
+static void
+tp_button_area_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event)
+{
+ switch (event) {
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ case BUTTON_EVENT_IN_AREA:
+ break;
+ case BUTTON_EVENT_UP:
+ tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+ break;
+ case BUTTON_EVENT_PRESS:
+ case BUTTON_EVENT_RELEASE:
+ case BUTTON_EVENT_TIMEOUT:
+ break;
+ }
+}
+
+static void
+tp_button_bottom_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event)
+{
+ switch (event) {
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ if (event != t->button.curr)
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+ event);
+ break;
+ case BUTTON_EVENT_IN_AREA:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_TO_AREA, event);
+ break;
+ case BUTTON_EVENT_UP:
+ tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+ break;
+ case BUTTON_EVENT_PRESS:
+ case BUTTON_EVENT_RELEASE:
+ case BUTTON_EVENT_TIMEOUT:
+ break;
+ }
+}
+
+static void
+tp_button_bottom_new_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event)
+{
+ switch(event) {
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ if (event != t->button.curr)
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+ event);
+ break;
+ case BUTTON_EVENT_IN_AREA:
+ tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+ break;
+ case BUTTON_EVENT_UP:
+ tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+ break;
+ case BUTTON_EVENT_PRESS:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+ break;
+ case BUTTON_EVENT_RELEASE:
+ break;
+ case BUTTON_EVENT_TIMEOUT:
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event);
+ break;
+ }
+}
+
+static void
+tp_button_bottom_to_area_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event)
+{
+ switch(event) {
+ case BUTTON_EVENT_IN_BOTTOM_R:
+ case BUTTON_EVENT_IN_BOTTOM_L:
+ if (event == t->button.curr)
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM,
+ event);
+ else
+ tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM_NEW,
+ event);
+ break;
+ case BUTTON_EVENT_IN_AREA:
+ break;
+ case BUTTON_EVENT_UP:
+ tp_button_set_state(tp, t, BUTTON_STATE_NONE, event);
+ break;
+ case BUTTON_EVENT_PRESS:
+ case BUTTON_EVENT_RELEASE:
+ break;
+ case BUTTON_EVENT_TIMEOUT:
+ tp_button_set_state(tp, t, BUTTON_STATE_AREA, event);
+ break;
+ }
+}
+
+static void
+tp_button_handle_event(struct tp_dispatch *tp,
+ struct tp_touch *t,
+ enum button_event event,
+ uint32_t time)
+{
+ enum button_state current = t->button.state;
+
+ switch(t->button.state) {
+ case BUTTON_STATE_NONE:
+ tp_button_none_handle_event(tp, t, event);
+ break;
+ case BUTTON_STATE_AREA:
+ tp_button_area_handle_event(tp, t, event);
+ break;
+ case BUTTON_STATE_BOTTOM:
+ tp_button_bottom_handle_event(tp, t, event);
+ break;
+ case BUTTON_STATE_BOTTOM_NEW:
+ tp_button_bottom_new_handle_event(tp, t, event);
+ break;
+ case BUTTON_STATE_BOTTOM_TO_AREA:
+ tp_button_bottom_to_area_handle_event(tp, t, event);
+ break;
+ }
+
+ if (current != t->button.state)
+ log_debug("button state: from %s, event %s to %s\n",
+ button_state_to_str(current),
+ button_event_to_str(event),
+ button_state_to_str(t->button.state));
+}
+
+int
+tp_button_handle_state(struct tp_dispatch *tp, uint32_t time)
+{
+ struct tp_touch *t;
+
+ tp_for_each_touch(tp, t) {
+ if (t->state == TOUCH_NONE)
+ continue;
+
+ if (t->state == TOUCH_END) {
+ tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time);
+ } else if (t->dirty) {
+ if (is_inside_right_area(tp, t))
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time);
+ else if (is_inside_left_area(tp, t))
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time);
+ else
+ tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time);
+ }
+ if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE)
+ tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time);
+ if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
+ tp_button_handle_event(tp, t, BUTTON_EVENT_PRESS, time);
+ }
+
+ return 0;
+}
+
+static int
+tp_button_handle_timeout(struct tp_dispatch *tp, uint32_t now)
+{
+ struct tp_touch *t;
+ uint32_t min_timeout = INT_MAX;
+
+ tp_for_each_touch(tp, t) {
+ if (t->button.timeout != 0 && t->button.timeout <= now) {
+ tp_button_clear_timer(tp, t);
+ tp_button_handle_event(tp, t, BUTTON_EVENT_TIMEOUT, now);
+ }
+ if (t->button.timeout != 0)
+ min_timeout = min(t->button.timeout, min_timeout);
+ }
+
+ return min_timeout == INT_MAX ? 0 : min_timeout;
+}
int
tp_process_button(struct tp_dispatch *tp,
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)
tp->buttons.motion_dist = diagonal * DEFAULT_BUTTON_MOTION_THRESHOLD;
+ if (libevdev_get_id_vendor(device->evdev) == 0x5ac) /* Apple */
+ tp->buttons.use_clickfinger = true;
+
+ tp->buttons.use_softbuttons = !tp->buttons.use_clickfinger &&
+ !tp->buttons.has_buttons;
+
+ if (tp->buttons.use_softbuttons) {
+ tp->buttons.area.top_edge = height * .8 + device->abs.min_y;
+ tp->buttons.area.rightbutton_left_edge = width/2 + device->abs.min_x;
+ tp->buttons.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+
+ if (tp->buttons.timer_fd == -1)
+ return -1;
+
+ tp->buttons.source =
+ libinput_add_fd(tp->device->base.seat->libinput,
+ tp->buttons.timer_fd,
+ tp_button_timeout_handler,
+ tp);
+ if (tp->buttons.source == NULL)
+ return -1;
+ } else {
+ tp->buttons.area.top_edge = INT_MAX;
+ }
+
return 0;
}
+void
+tp_destroy_buttons(struct tp_dispatch *tp)
+{
+ if (tp->buttons.source) {
+ libinput_remove_source(tp->device->base.seat->libinput,
+ tp->buttons.source);
+ tp->buttons.source = NULL;
+ }
+ if (tp->buttons.timer_fd > -1) {
+ close(tp->buttons.timer_fd);
+ tp->buttons.timer_fd = -1;
+ }
+}
+
static int
tp_post_clickfinger_buttons(struct tp_dispatch *tp, uint32_t time)
{
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)
{
- int rc;
+ int rc = 0;
if ((tp->queued &
(TOUCHPAD_EVENT_BUTTON_PRESS|TOUCHPAD_EVENT_BUTTON_RELEASE)) == 0)
if (tp->buttons.has_buttons)
rc = tp_post_physical_buttons(tp, time);
- else
+ 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;
}