1a46c0ec8Sopenharmony_ci/* 2a46c0ec8Sopenharmony_ci * Copyright © 2014-2015 Red Hat, Inc. 3a46c0ec8Sopenharmony_ci * 4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10a46c0ec8Sopenharmony_ci * 11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13a46c0ec8Sopenharmony_ci * Software. 14a46c0ec8Sopenharmony_ci * 15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 22a46c0ec8Sopenharmony_ci */ 23a46c0ec8Sopenharmony_ci 24a46c0ec8Sopenharmony_ci#include "config.h" 25a46c0ec8Sopenharmony_ci 26a46c0ec8Sopenharmony_ci#include <limits.h> 27a46c0ec8Sopenharmony_ci#include <math.h> 28a46c0ec8Sopenharmony_ci#include <string.h> 29a46c0ec8Sopenharmony_ci#include "linux/input.h" 30a46c0ec8Sopenharmony_ci 31a46c0ec8Sopenharmony_ci#include "util-input-event.h" 32a46c0ec8Sopenharmony_ci#include "evdev-mt-touchpad.h" 33a46c0ec8Sopenharmony_ci 34a46c0ec8Sopenharmony_ci#define DEFAULT_BUTTON_ENTER_TIMEOUT ms2us(100) 35a46c0ec8Sopenharmony_ci#define DEFAULT_BUTTON_LEAVE_TIMEOUT ms2us(300) 36a46c0ec8Sopenharmony_ci 37a46c0ec8Sopenharmony_ci/***************************************** 38a46c0ec8Sopenharmony_ci * BEFORE YOU EDIT THIS FILE, look at the state diagram in 39a46c0ec8Sopenharmony_ci * doc/touchpad-softbutton-state-machine.svg (generated with 40a46c0ec8Sopenharmony_ci * https://www.diagrams.net). 41a46c0ec8Sopenharmony_ci * Any changes in this file must be represented in the diagram. 42a46c0ec8Sopenharmony_ci * 43a46c0ec8Sopenharmony_ci * The state machine only affects the soft button area code. 44a46c0ec8Sopenharmony_ci */ 45a46c0ec8Sopenharmony_ci 46a46c0ec8Sopenharmony_cistatic inline const char* 47a46c0ec8Sopenharmony_cibutton_state_to_str(enum button_state state) 48a46c0ec8Sopenharmony_ci{ 49a46c0ec8Sopenharmony_ci switch(state) { 50a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_NONE); 51a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_AREA); 52a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_BOTTOM); 53a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_TOP); 54a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_TOP_NEW); 55a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_TOP_TO_IGNORE); 56a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_STATE_IGNORE); 57a46c0ec8Sopenharmony_ci } 58a46c0ec8Sopenharmony_ci return NULL; 59a46c0ec8Sopenharmony_ci} 60a46c0ec8Sopenharmony_ci 61a46c0ec8Sopenharmony_cistatic inline const char* 62a46c0ec8Sopenharmony_cibutton_event_to_str(enum button_event event) 63a46c0ec8Sopenharmony_ci{ 64a46c0ec8Sopenharmony_ci switch(event) { 65a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R); 66a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_M); 67a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L); 68a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_R); 69a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_M); 70a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_TOP_L); 71a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_IN_AREA); 72a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_UP); 73a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_PRESS); 74a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_RELEASE); 75a46c0ec8Sopenharmony_ci CASE_RETURN_STRING(BUTTON_EVENT_TIMEOUT); 76a46c0ec8Sopenharmony_ci } 77a46c0ec8Sopenharmony_ci return NULL; 78a46c0ec8Sopenharmony_ci} 79a46c0ec8Sopenharmony_ci 80a46c0ec8Sopenharmony_cistatic inline bool 81a46c0ec8Sopenharmony_ciis_inside_bottom_button_area(const struct tp_dispatch *tp, 82a46c0ec8Sopenharmony_ci const struct tp_touch *t) 83a46c0ec8Sopenharmony_ci{ 84a46c0ec8Sopenharmony_ci return t->point.y >= tp->buttons.bottom_area.top_edge; 85a46c0ec8Sopenharmony_ci} 86a46c0ec8Sopenharmony_ci 87a46c0ec8Sopenharmony_cistatic inline bool 88a46c0ec8Sopenharmony_ciis_inside_bottom_right_area(const struct tp_dispatch *tp, 89a46c0ec8Sopenharmony_ci const struct tp_touch *t) 90a46c0ec8Sopenharmony_ci{ 91a46c0ec8Sopenharmony_ci return is_inside_bottom_button_area(tp, t) && 92a46c0ec8Sopenharmony_ci t->point.x > tp->buttons.bottom_area.rightbutton_left_edge; 93a46c0ec8Sopenharmony_ci} 94a46c0ec8Sopenharmony_ci 95a46c0ec8Sopenharmony_cistatic inline bool 96a46c0ec8Sopenharmony_ciis_inside_bottom_middle_area(const struct tp_dispatch *tp, 97a46c0ec8Sopenharmony_ci const struct tp_touch *t) 98a46c0ec8Sopenharmony_ci{ 99a46c0ec8Sopenharmony_ci return is_inside_bottom_button_area(tp, t) && 100a46c0ec8Sopenharmony_ci !is_inside_bottom_right_area(tp, t) && 101a46c0ec8Sopenharmony_ci t->point.x > tp->buttons.bottom_area.middlebutton_left_edge; 102a46c0ec8Sopenharmony_ci} 103a46c0ec8Sopenharmony_ci 104a46c0ec8Sopenharmony_cistatic inline bool 105a46c0ec8Sopenharmony_ciis_inside_top_button_area(const struct tp_dispatch *tp, 106a46c0ec8Sopenharmony_ci const struct tp_touch *t) 107a46c0ec8Sopenharmony_ci{ 108a46c0ec8Sopenharmony_ci return t->point.y <= tp->buttons.top_area.bottom_edge; 109a46c0ec8Sopenharmony_ci} 110a46c0ec8Sopenharmony_ci 111a46c0ec8Sopenharmony_cistatic inline bool 112a46c0ec8Sopenharmony_ciis_inside_top_right_area(const struct tp_dispatch *tp, 113a46c0ec8Sopenharmony_ci const struct tp_touch *t) 114a46c0ec8Sopenharmony_ci{ 115a46c0ec8Sopenharmony_ci return is_inside_top_button_area(tp, t) && 116a46c0ec8Sopenharmony_ci t->point.x > tp->buttons.top_area.rightbutton_left_edge; 117a46c0ec8Sopenharmony_ci} 118a46c0ec8Sopenharmony_ci 119a46c0ec8Sopenharmony_cistatic inline bool 120a46c0ec8Sopenharmony_ciis_inside_top_middle_area(const struct tp_dispatch *tp, 121a46c0ec8Sopenharmony_ci const struct tp_touch *t) 122a46c0ec8Sopenharmony_ci{ 123a46c0ec8Sopenharmony_ci return is_inside_top_button_area(tp, t) && 124a46c0ec8Sopenharmony_ci t->point.x >= tp->buttons.top_area.leftbutton_right_edge && 125a46c0ec8Sopenharmony_ci t->point.x <= tp->buttons.top_area.rightbutton_left_edge; 126a46c0ec8Sopenharmony_ci} 127a46c0ec8Sopenharmony_ci 128a46c0ec8Sopenharmony_cistatic void 129a46c0ec8Sopenharmony_citp_button_set_enter_timer(struct tp_dispatch *tp, 130a46c0ec8Sopenharmony_ci struct tp_touch *t, 131a46c0ec8Sopenharmony_ci uint64_t time) 132a46c0ec8Sopenharmony_ci{ 133a46c0ec8Sopenharmony_ci libinput_timer_set(&t->button.timer, 134a46c0ec8Sopenharmony_ci time + DEFAULT_BUTTON_ENTER_TIMEOUT); 135a46c0ec8Sopenharmony_ci} 136a46c0ec8Sopenharmony_ci 137a46c0ec8Sopenharmony_cistatic void 138a46c0ec8Sopenharmony_citp_button_set_leave_timer(struct tp_dispatch *tp, 139a46c0ec8Sopenharmony_ci struct tp_touch *t, 140a46c0ec8Sopenharmony_ci uint64_t time) 141a46c0ec8Sopenharmony_ci{ 142a46c0ec8Sopenharmony_ci libinput_timer_set(&t->button.timer, 143a46c0ec8Sopenharmony_ci time + DEFAULT_BUTTON_LEAVE_TIMEOUT); 144a46c0ec8Sopenharmony_ci} 145a46c0ec8Sopenharmony_ci 146a46c0ec8Sopenharmony_ci/* 147a46c0ec8Sopenharmony_ci * tp_button_set_state, change state and implement on-entry behavior 148a46c0ec8Sopenharmony_ci * as described in the state machine diagram. 149a46c0ec8Sopenharmony_ci */ 150a46c0ec8Sopenharmony_cistatic void 151a46c0ec8Sopenharmony_citp_button_set_state(struct tp_dispatch *tp, 152a46c0ec8Sopenharmony_ci struct tp_touch *t, 153a46c0ec8Sopenharmony_ci enum button_state new_state, 154a46c0ec8Sopenharmony_ci enum button_event event, 155a46c0ec8Sopenharmony_ci uint64_t time) 156a46c0ec8Sopenharmony_ci{ 157a46c0ec8Sopenharmony_ci libinput_timer_cancel(&t->button.timer); 158a46c0ec8Sopenharmony_ci 159a46c0ec8Sopenharmony_ci t->button.state = new_state; 160a46c0ec8Sopenharmony_ci 161a46c0ec8Sopenharmony_ci switch (t->button.state) { 162a46c0ec8Sopenharmony_ci case BUTTON_STATE_NONE: 163a46c0ec8Sopenharmony_ci t->button.current = 0; 164a46c0ec8Sopenharmony_ci break; 165a46c0ec8Sopenharmony_ci case BUTTON_STATE_AREA: 166a46c0ec8Sopenharmony_ci t->button.current = BUTTON_EVENT_IN_AREA; 167a46c0ec8Sopenharmony_ci break; 168a46c0ec8Sopenharmony_ci case BUTTON_STATE_BOTTOM: 169a46c0ec8Sopenharmony_ci t->button.current = event; 170a46c0ec8Sopenharmony_ci break; 171a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP: 172a46c0ec8Sopenharmony_ci break; 173a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_NEW: 174a46c0ec8Sopenharmony_ci t->button.current = event; 175a46c0ec8Sopenharmony_ci tp_button_set_enter_timer(tp, t, time); 176a46c0ec8Sopenharmony_ci break; 177a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_TO_IGNORE: 178a46c0ec8Sopenharmony_ci tp_button_set_leave_timer(tp, t, time); 179a46c0ec8Sopenharmony_ci break; 180a46c0ec8Sopenharmony_ci case BUTTON_STATE_IGNORE: 181a46c0ec8Sopenharmony_ci t->button.current = 0; 182a46c0ec8Sopenharmony_ci break; 183a46c0ec8Sopenharmony_ci } 184a46c0ec8Sopenharmony_ci} 185a46c0ec8Sopenharmony_ci 186a46c0ec8Sopenharmony_cistatic void 187a46c0ec8Sopenharmony_citp_button_none_handle_event(struct tp_dispatch *tp, 188a46c0ec8Sopenharmony_ci struct tp_touch *t, 189a46c0ec8Sopenharmony_ci enum button_event event, 190a46c0ec8Sopenharmony_ci uint64_t time) 191a46c0ec8Sopenharmony_ci{ 192a46c0ec8Sopenharmony_ci switch (event) { 193a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 194a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 195a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 196a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event, time); 197a46c0ec8Sopenharmony_ci break; 198a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 199a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 200a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 201a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, event, time); 202a46c0ec8Sopenharmony_ci break; 203a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 204a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_AREA, event, time); 205a46c0ec8Sopenharmony_ci break; 206a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 207a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 208a46c0ec8Sopenharmony_ci break; 209a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 210a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 211a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 212a46c0ec8Sopenharmony_ci break; 213a46c0ec8Sopenharmony_ci } 214a46c0ec8Sopenharmony_ci} 215a46c0ec8Sopenharmony_ci 216a46c0ec8Sopenharmony_cistatic void 217a46c0ec8Sopenharmony_citp_button_area_handle_event(struct tp_dispatch *tp, 218a46c0ec8Sopenharmony_ci struct tp_touch *t, 219a46c0ec8Sopenharmony_ci enum button_event event, 220a46c0ec8Sopenharmony_ci uint64_t time) 221a46c0ec8Sopenharmony_ci{ 222a46c0ec8Sopenharmony_ci switch (event) { 223a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 224a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 225a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 226a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 227a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 228a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 229a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 230a46c0ec8Sopenharmony_ci break; 231a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 232a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 233a46c0ec8Sopenharmony_ci break; 234a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 235a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 236a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 237a46c0ec8Sopenharmony_ci break; 238a46c0ec8Sopenharmony_ci } 239a46c0ec8Sopenharmony_ci} 240a46c0ec8Sopenharmony_ci 241a46c0ec8Sopenharmony_ci/** 242a46c0ec8Sopenharmony_ci * Release any button in the bottom area, provided it started within a 243a46c0ec8Sopenharmony_ci * threshold around start_time (i.e. simultaneously with the other touch 244a46c0ec8Sopenharmony_ci * that triggered this call). 245a46c0ec8Sopenharmony_ci */ 246a46c0ec8Sopenharmony_cistatic inline void 247a46c0ec8Sopenharmony_citp_button_release_other_bottom_touches(struct tp_dispatch *tp, 248a46c0ec8Sopenharmony_ci uint64_t other_start_time) 249a46c0ec8Sopenharmony_ci{ 250a46c0ec8Sopenharmony_ci struct tp_touch *t; 251a46c0ec8Sopenharmony_ci 252a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 253a46c0ec8Sopenharmony_ci uint64_t tdelta; 254a46c0ec8Sopenharmony_ci 255a46c0ec8Sopenharmony_ci if (t->button.state != BUTTON_STATE_BOTTOM || 256a46c0ec8Sopenharmony_ci t->button.has_moved) 257a46c0ec8Sopenharmony_ci continue; 258a46c0ec8Sopenharmony_ci 259a46c0ec8Sopenharmony_ci if (other_start_time > t->button.initial_time) 260a46c0ec8Sopenharmony_ci tdelta = other_start_time - t->button.initial_time; 261a46c0ec8Sopenharmony_ci else 262a46c0ec8Sopenharmony_ci tdelta = t->button.initial_time - other_start_time; 263a46c0ec8Sopenharmony_ci 264a46c0ec8Sopenharmony_ci if (tdelta > ms2us(80)) 265a46c0ec8Sopenharmony_ci continue; 266a46c0ec8Sopenharmony_ci 267a46c0ec8Sopenharmony_ci t->button.has_moved = true; 268a46c0ec8Sopenharmony_ci } 269a46c0ec8Sopenharmony_ci} 270a46c0ec8Sopenharmony_ci 271a46c0ec8Sopenharmony_cistatic void 272a46c0ec8Sopenharmony_citp_button_bottom_handle_event(struct tp_dispatch *tp, 273a46c0ec8Sopenharmony_ci struct tp_touch *t, 274a46c0ec8Sopenharmony_ci enum button_event event, 275a46c0ec8Sopenharmony_ci uint64_t time) 276a46c0ec8Sopenharmony_ci{ 277a46c0ec8Sopenharmony_ci switch (event) { 278a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 279a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 280a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 281a46c0ec8Sopenharmony_ci if (event != t->button.current) 282a46c0ec8Sopenharmony_ci tp_button_set_state(tp, 283a46c0ec8Sopenharmony_ci t, 284a46c0ec8Sopenharmony_ci BUTTON_STATE_BOTTOM, 285a46c0ec8Sopenharmony_ci event, 286a46c0ec8Sopenharmony_ci time); 287a46c0ec8Sopenharmony_ci break; 288a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 289a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 290a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 291a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 292a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_AREA, event, time); 293a46c0ec8Sopenharmony_ci 294a46c0ec8Sopenharmony_ci /* We just transitioned one finger from BOTTOM to AREA, 295a46c0ec8Sopenharmony_ci * if there are other fingers in BOTTOM that started 296a46c0ec8Sopenharmony_ci * simultaneously with this finger, release those fingers 297a46c0ec8Sopenharmony_ci * because they're part of a gesture. 298a46c0ec8Sopenharmony_ci */ 299a46c0ec8Sopenharmony_ci tp_button_release_other_bottom_touches(tp, 300a46c0ec8Sopenharmony_ci t->button.initial_time); 301a46c0ec8Sopenharmony_ci break; 302a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 303a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 304a46c0ec8Sopenharmony_ci break; 305a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 306a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 307a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 308a46c0ec8Sopenharmony_ci break; 309a46c0ec8Sopenharmony_ci } 310a46c0ec8Sopenharmony_ci} 311a46c0ec8Sopenharmony_ci 312a46c0ec8Sopenharmony_cistatic void 313a46c0ec8Sopenharmony_citp_button_top_handle_event(struct tp_dispatch *tp, 314a46c0ec8Sopenharmony_ci struct tp_touch *t, 315a46c0ec8Sopenharmony_ci enum button_event event, 316a46c0ec8Sopenharmony_ci uint64_t time) 317a46c0ec8Sopenharmony_ci{ 318a46c0ec8Sopenharmony_ci switch (event) { 319a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 320a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 321a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 322a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event, time); 323a46c0ec8Sopenharmony_ci break; 324a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 325a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 326a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 327a46c0ec8Sopenharmony_ci if (event != t->button.current) 328a46c0ec8Sopenharmony_ci tp_button_set_state(tp, 329a46c0ec8Sopenharmony_ci t, 330a46c0ec8Sopenharmony_ci BUTTON_STATE_TOP_NEW, 331a46c0ec8Sopenharmony_ci event, 332a46c0ec8Sopenharmony_ci time); 333a46c0ec8Sopenharmony_ci break; 334a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 335a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event, time); 336a46c0ec8Sopenharmony_ci break; 337a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 338a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 339a46c0ec8Sopenharmony_ci break; 340a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 341a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 342a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 343a46c0ec8Sopenharmony_ci break; 344a46c0ec8Sopenharmony_ci } 345a46c0ec8Sopenharmony_ci} 346a46c0ec8Sopenharmony_ci 347a46c0ec8Sopenharmony_cistatic void 348a46c0ec8Sopenharmony_citp_button_top_new_handle_event(struct tp_dispatch *tp, 349a46c0ec8Sopenharmony_ci struct tp_touch *t, 350a46c0ec8Sopenharmony_ci enum button_event event, 351a46c0ec8Sopenharmony_ci uint64_t time) 352a46c0ec8Sopenharmony_ci{ 353a46c0ec8Sopenharmony_ci switch(event) { 354a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 355a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 356a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 357a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_AREA, event, time); 358a46c0ec8Sopenharmony_ci break; 359a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 360a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 361a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 362a46c0ec8Sopenharmony_ci if (event != t->button.current) 363a46c0ec8Sopenharmony_ci tp_button_set_state(tp, 364a46c0ec8Sopenharmony_ci t, 365a46c0ec8Sopenharmony_ci BUTTON_STATE_TOP_NEW, 366a46c0ec8Sopenharmony_ci event, 367a46c0ec8Sopenharmony_ci time); 368a46c0ec8Sopenharmony_ci break; 369a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 370a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_AREA, event, time); 371a46c0ec8Sopenharmony_ci break; 372a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 373a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 374a46c0ec8Sopenharmony_ci break; 375a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 376a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_TOP, event, time); 377a46c0ec8Sopenharmony_ci break; 378a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 379a46c0ec8Sopenharmony_ci break; 380a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 381a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_TOP, event, time); 382a46c0ec8Sopenharmony_ci break; 383a46c0ec8Sopenharmony_ci } 384a46c0ec8Sopenharmony_ci} 385a46c0ec8Sopenharmony_ci 386a46c0ec8Sopenharmony_cistatic void 387a46c0ec8Sopenharmony_citp_button_top_to_ignore_handle_event(struct tp_dispatch *tp, 388a46c0ec8Sopenharmony_ci struct tp_touch *t, 389a46c0ec8Sopenharmony_ci enum button_event event, 390a46c0ec8Sopenharmony_ci uint64_t time) 391a46c0ec8Sopenharmony_ci{ 392a46c0ec8Sopenharmony_ci switch(event) { 393a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 394a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 395a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 396a46c0ec8Sopenharmony_ci if (event == t->button.current) 397a46c0ec8Sopenharmony_ci tp_button_set_state(tp, 398a46c0ec8Sopenharmony_ci t, 399a46c0ec8Sopenharmony_ci BUTTON_STATE_TOP, 400a46c0ec8Sopenharmony_ci event, 401a46c0ec8Sopenharmony_ci time); 402a46c0ec8Sopenharmony_ci else 403a46c0ec8Sopenharmony_ci tp_button_set_state(tp, 404a46c0ec8Sopenharmony_ci t, 405a46c0ec8Sopenharmony_ci BUTTON_STATE_TOP_NEW, 406a46c0ec8Sopenharmony_ci event, 407a46c0ec8Sopenharmony_ci time); 408a46c0ec8Sopenharmony_ci break; 409a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 410a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 411a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 412a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 413a46c0ec8Sopenharmony_ci break; 414a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 415a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 416a46c0ec8Sopenharmony_ci break; 417a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 418a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 419a46c0ec8Sopenharmony_ci break; 420a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 421a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_IGNORE, event, time); 422a46c0ec8Sopenharmony_ci break; 423a46c0ec8Sopenharmony_ci } 424a46c0ec8Sopenharmony_ci} 425a46c0ec8Sopenharmony_ci 426a46c0ec8Sopenharmony_cistatic void 427a46c0ec8Sopenharmony_citp_button_ignore_handle_event(struct tp_dispatch *tp, 428a46c0ec8Sopenharmony_ci struct tp_touch *t, 429a46c0ec8Sopenharmony_ci enum button_event event, 430a46c0ec8Sopenharmony_ci uint64_t time) 431a46c0ec8Sopenharmony_ci{ 432a46c0ec8Sopenharmony_ci switch (event) { 433a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 434a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 435a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 436a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 437a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 438a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 439a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 440a46c0ec8Sopenharmony_ci break; 441a46c0ec8Sopenharmony_ci case BUTTON_EVENT_UP: 442a46c0ec8Sopenharmony_ci tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time); 443a46c0ec8Sopenharmony_ci break; 444a46c0ec8Sopenharmony_ci case BUTTON_EVENT_PRESS: 445a46c0ec8Sopenharmony_ci t->button.current = BUTTON_EVENT_IN_AREA; 446a46c0ec8Sopenharmony_ci break; 447a46c0ec8Sopenharmony_ci case BUTTON_EVENT_RELEASE: 448a46c0ec8Sopenharmony_ci break; 449a46c0ec8Sopenharmony_ci case BUTTON_EVENT_TIMEOUT: 450a46c0ec8Sopenharmony_ci break; 451a46c0ec8Sopenharmony_ci } 452a46c0ec8Sopenharmony_ci} 453a46c0ec8Sopenharmony_ci 454a46c0ec8Sopenharmony_cistatic void 455a46c0ec8Sopenharmony_citp_button_handle_event(struct tp_dispatch *tp, 456a46c0ec8Sopenharmony_ci struct tp_touch *t, 457a46c0ec8Sopenharmony_ci enum button_event event, 458a46c0ec8Sopenharmony_ci uint64_t time) 459a46c0ec8Sopenharmony_ci{ 460a46c0ec8Sopenharmony_ci enum button_state current = t->button.state; 461a46c0ec8Sopenharmony_ci 462a46c0ec8Sopenharmony_ci switch(t->button.state) { 463a46c0ec8Sopenharmony_ci case BUTTON_STATE_NONE: 464a46c0ec8Sopenharmony_ci tp_button_none_handle_event(tp, t, event, time); 465a46c0ec8Sopenharmony_ci break; 466a46c0ec8Sopenharmony_ci case BUTTON_STATE_AREA: 467a46c0ec8Sopenharmony_ci tp_button_area_handle_event(tp, t, event, time); 468a46c0ec8Sopenharmony_ci break; 469a46c0ec8Sopenharmony_ci case BUTTON_STATE_BOTTOM: 470a46c0ec8Sopenharmony_ci tp_button_bottom_handle_event(tp, t, event, time); 471a46c0ec8Sopenharmony_ci break; 472a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP: 473a46c0ec8Sopenharmony_ci tp_button_top_handle_event(tp, t, event, time); 474a46c0ec8Sopenharmony_ci break; 475a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_NEW: 476a46c0ec8Sopenharmony_ci tp_button_top_new_handle_event(tp, t, event, time); 477a46c0ec8Sopenharmony_ci break; 478a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_TO_IGNORE: 479a46c0ec8Sopenharmony_ci tp_button_top_to_ignore_handle_event(tp, t, event, time); 480a46c0ec8Sopenharmony_ci break; 481a46c0ec8Sopenharmony_ci case BUTTON_STATE_IGNORE: 482a46c0ec8Sopenharmony_ci tp_button_ignore_handle_event(tp, t, event, time); 483a46c0ec8Sopenharmony_ci break; 484a46c0ec8Sopenharmony_ci } 485a46c0ec8Sopenharmony_ci 486a46c0ec8Sopenharmony_ci if (current != t->button.state) 487a46c0ec8Sopenharmony_ci evdev_log_debug(tp->device, 488a46c0ec8Sopenharmony_ci "button state: touch %d from %-20s event %-24s to %-20s\n", 489a46c0ec8Sopenharmony_ci t->index, 490a46c0ec8Sopenharmony_ci button_state_to_str(current), 491a46c0ec8Sopenharmony_ci button_event_to_str(event), 492a46c0ec8Sopenharmony_ci button_state_to_str(t->button.state)); 493a46c0ec8Sopenharmony_ci} 494a46c0ec8Sopenharmony_ci 495a46c0ec8Sopenharmony_cistatic inline void 496a46c0ec8Sopenharmony_citp_button_check_for_movement(struct tp_dispatch *tp, struct tp_touch *t) 497a46c0ec8Sopenharmony_ci{ 498a46c0ec8Sopenharmony_ci struct device_coords delta; 499a46c0ec8Sopenharmony_ci struct phys_coords mm; 500a46c0ec8Sopenharmony_ci double vector_length; 501a46c0ec8Sopenharmony_ci 502a46c0ec8Sopenharmony_ci if (t->button.has_moved) 503a46c0ec8Sopenharmony_ci return; 504a46c0ec8Sopenharmony_ci 505a46c0ec8Sopenharmony_ci switch (t->button.state) { 506a46c0ec8Sopenharmony_ci case BUTTON_STATE_NONE: 507a46c0ec8Sopenharmony_ci case BUTTON_STATE_AREA: 508a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP: 509a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_NEW: 510a46c0ec8Sopenharmony_ci case BUTTON_STATE_TOP_TO_IGNORE: 511a46c0ec8Sopenharmony_ci case BUTTON_STATE_IGNORE: 512a46c0ec8Sopenharmony_ci /* No point calculating if we're not going to use it */ 513a46c0ec8Sopenharmony_ci return; 514a46c0ec8Sopenharmony_ci case BUTTON_STATE_BOTTOM: 515a46c0ec8Sopenharmony_ci break; 516a46c0ec8Sopenharmony_ci } 517a46c0ec8Sopenharmony_ci 518a46c0ec8Sopenharmony_ci delta.x = t->point.x - t->button.initial.x; 519a46c0ec8Sopenharmony_ci delta.y = t->point.y - t->button.initial.y; 520a46c0ec8Sopenharmony_ci mm = evdev_device_unit_delta_to_mm(tp->device, &delta); 521a46c0ec8Sopenharmony_ci vector_length = hypot(mm.x, mm.y); 522a46c0ec8Sopenharmony_ci 523a46c0ec8Sopenharmony_ci if (vector_length > 5.0 /* mm */) { 524a46c0ec8Sopenharmony_ci t->button.has_moved = true; 525a46c0ec8Sopenharmony_ci 526a46c0ec8Sopenharmony_ci tp_button_release_other_bottom_touches(tp, 527a46c0ec8Sopenharmony_ci t->button.initial_time); 528a46c0ec8Sopenharmony_ci } 529a46c0ec8Sopenharmony_ci} 530a46c0ec8Sopenharmony_ci 531a46c0ec8Sopenharmony_civoid 532a46c0ec8Sopenharmony_citp_button_handle_state(struct tp_dispatch *tp, uint64_t time) 533a46c0ec8Sopenharmony_ci{ 534a46c0ec8Sopenharmony_ci struct tp_touch *t; 535a46c0ec8Sopenharmony_ci 536a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 537a46c0ec8Sopenharmony_ci if (t->state == TOUCH_NONE || t->state == TOUCH_HOVERING) 538a46c0ec8Sopenharmony_ci continue; 539a46c0ec8Sopenharmony_ci 540a46c0ec8Sopenharmony_ci if (t->state == TOUCH_BEGIN) { 541a46c0ec8Sopenharmony_ci t->button.initial = t->point; 542a46c0ec8Sopenharmony_ci t->button.initial_time = time; 543a46c0ec8Sopenharmony_ci t->button.has_moved = false; 544a46c0ec8Sopenharmony_ci } 545a46c0ec8Sopenharmony_ci 546a46c0ec8Sopenharmony_ci if (t->state == TOUCH_END) { 547a46c0ec8Sopenharmony_ci tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time); 548a46c0ec8Sopenharmony_ci } else if (t->dirty) { 549a46c0ec8Sopenharmony_ci enum button_event event; 550a46c0ec8Sopenharmony_ci 551a46c0ec8Sopenharmony_ci if (is_inside_bottom_button_area(tp, t)) { 552a46c0ec8Sopenharmony_ci if (is_inside_bottom_right_area(tp, t)) 553a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_BOTTOM_R; 554a46c0ec8Sopenharmony_ci else if (is_inside_bottom_middle_area(tp, t)) 555a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_BOTTOM_M; 556a46c0ec8Sopenharmony_ci else 557a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_BOTTOM_L; 558a46c0ec8Sopenharmony_ci 559a46c0ec8Sopenharmony_ci /* In the bottom area we check for movement 560a46c0ec8Sopenharmony_ci * within the area. Top area - meh */ 561a46c0ec8Sopenharmony_ci tp_button_check_for_movement(tp, t); 562a46c0ec8Sopenharmony_ci } else if (is_inside_top_button_area(tp, t)) { 563a46c0ec8Sopenharmony_ci if (is_inside_top_right_area(tp, t)) 564a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_TOP_R; 565a46c0ec8Sopenharmony_ci else if (is_inside_top_middle_area(tp, t)) 566a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_TOP_M; 567a46c0ec8Sopenharmony_ci else 568a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_TOP_L; 569a46c0ec8Sopenharmony_ci } else { 570a46c0ec8Sopenharmony_ci event = BUTTON_EVENT_IN_AREA; 571a46c0ec8Sopenharmony_ci } 572a46c0ec8Sopenharmony_ci 573a46c0ec8Sopenharmony_ci tp_button_handle_event(tp, t, event, time); 574a46c0ec8Sopenharmony_ci } 575a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE) 576a46c0ec8Sopenharmony_ci tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time); 577a46c0ec8Sopenharmony_ci if (tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) 578a46c0ec8Sopenharmony_ci tp_button_handle_event(tp, t, BUTTON_EVENT_PRESS, time); 579a46c0ec8Sopenharmony_ci } 580a46c0ec8Sopenharmony_ci} 581a46c0ec8Sopenharmony_ci 582a46c0ec8Sopenharmony_cistatic void 583a46c0ec8Sopenharmony_citp_button_handle_timeout(uint64_t now, void *data) 584a46c0ec8Sopenharmony_ci{ 585a46c0ec8Sopenharmony_ci struct tp_touch *t = data; 586a46c0ec8Sopenharmony_ci 587a46c0ec8Sopenharmony_ci tp_button_handle_event(t->tp, t, BUTTON_EVENT_TIMEOUT, now); 588a46c0ec8Sopenharmony_ci} 589a46c0ec8Sopenharmony_ci 590a46c0ec8Sopenharmony_civoid 591a46c0ec8Sopenharmony_citp_process_button(struct tp_dispatch *tp, 592a46c0ec8Sopenharmony_ci const struct input_event *e, 593a46c0ec8Sopenharmony_ci uint64_t time) 594a46c0ec8Sopenharmony_ci{ 595a46c0ec8Sopenharmony_ci uint32_t mask = bit(e->code - BTN_LEFT); 596a46c0ec8Sopenharmony_ci 597a46c0ec8Sopenharmony_ci /* Ignore other buttons on clickpads */ 598a46c0ec8Sopenharmony_ci if (tp->buttons.is_clickpad && e->code != BTN_LEFT) { 599a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(tp->device, 600a46c0ec8Sopenharmony_ci "received %s button event on a clickpad\n", 601a46c0ec8Sopenharmony_ci libevdev_event_code_get_name(EV_KEY, e->code)); 602a46c0ec8Sopenharmony_ci return; 603a46c0ec8Sopenharmony_ci } 604a46c0ec8Sopenharmony_ci 605a46c0ec8Sopenharmony_ci if (e->value) { 606a46c0ec8Sopenharmony_ci tp->buttons.state |= mask; 607a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_BUTTON_PRESS; 608a46c0ec8Sopenharmony_ci } else { 609a46c0ec8Sopenharmony_ci tp->buttons.state &= ~mask; 610a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE; 611a46c0ec8Sopenharmony_ci } 612a46c0ec8Sopenharmony_ci} 613a46c0ec8Sopenharmony_ci 614a46c0ec8Sopenharmony_civoid 615a46c0ec8Sopenharmony_citp_release_all_buttons(struct tp_dispatch *tp, 616a46c0ec8Sopenharmony_ci uint64_t time) 617a46c0ec8Sopenharmony_ci{ 618a46c0ec8Sopenharmony_ci if (tp->buttons.state) { 619a46c0ec8Sopenharmony_ci tp->buttons.state = 0; 620a46c0ec8Sopenharmony_ci tp->queued |= TOUCHPAD_EVENT_BUTTON_RELEASE; 621a46c0ec8Sopenharmony_ci } 622a46c0ec8Sopenharmony_ci} 623a46c0ec8Sopenharmony_ci 624a46c0ec8Sopenharmony_cistatic void 625a46c0ec8Sopenharmony_citp_init_softbuttons(struct tp_dispatch *tp, 626a46c0ec8Sopenharmony_ci struct evdev_device *device) 627a46c0ec8Sopenharmony_ci{ 628a46c0ec8Sopenharmony_ci double width, height; 629a46c0ec8Sopenharmony_ci struct device_coords edges; 630a46c0ec8Sopenharmony_ci int mb_le, mb_re; /* middle button left/right edge */ 631a46c0ec8Sopenharmony_ci struct phys_coords mm = { 0.0, 0.0 }; 632a46c0ec8Sopenharmony_ci 633a46c0ec8Sopenharmony_ci evdev_device_get_size(device, &width, &height); 634a46c0ec8Sopenharmony_ci 635a46c0ec8Sopenharmony_ci /* button height: 10mm or 15% or the touchpad height, 636a46c0ec8Sopenharmony_ci whichever is smaller */ 637a46c0ec8Sopenharmony_ci if (height * 0.15 > 10) 638a46c0ec8Sopenharmony_ci mm.y = height - 10; 639a46c0ec8Sopenharmony_ci else 640a46c0ec8Sopenharmony_ci mm.y = height * 0.85; 641a46c0ec8Sopenharmony_ci 642a46c0ec8Sopenharmony_ci mm.x = width * 0.5; 643a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 644a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.top_edge = edges.y; 645a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.rightbutton_left_edge = edges.x; 646a46c0ec8Sopenharmony_ci 647a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.middlebutton_left_edge = INT_MAX; 648a46c0ec8Sopenharmony_ci 649a46c0ec8Sopenharmony_ci /* if middlebutton emulation is enabled, don't init a software area */ 650a46c0ec8Sopenharmony_ci if (device->middlebutton.want_enabled) 651a46c0ec8Sopenharmony_ci return; 652a46c0ec8Sopenharmony_ci 653a46c0ec8Sopenharmony_ci /* The middle button is 25% of the touchpad and centered. Many 654a46c0ec8Sopenharmony_ci * touchpads don't have markings for the middle button at all so we 655a46c0ec8Sopenharmony_ci * need to make it big enough to reliably hit it but not too big so 656a46c0ec8Sopenharmony_ci * it takes away all the space. 657a46c0ec8Sopenharmony_ci * 658a46c0ec8Sopenharmony_ci * On touchpads with visible markings we reduce the size of the 659a46c0ec8Sopenharmony_ci * middle button since users have a visual guide. 660a46c0ec8Sopenharmony_ci */ 661a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(device, 662a46c0ec8Sopenharmony_ci QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER)) { 663a46c0ec8Sopenharmony_ci mm.x = width/2 - 5; /* 10mm wide */ 664a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 665a46c0ec8Sopenharmony_ci mb_le = edges.x; 666a46c0ec8Sopenharmony_ci 667a46c0ec8Sopenharmony_ci mm.x = width/2 + 5; /* 10mm wide */ 668a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 669a46c0ec8Sopenharmony_ci mb_re = edges.x; 670a46c0ec8Sopenharmony_ci } else { 671a46c0ec8Sopenharmony_ci mm.x = width * 0.375; 672a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 673a46c0ec8Sopenharmony_ci mb_le = edges.x; 674a46c0ec8Sopenharmony_ci 675a46c0ec8Sopenharmony_ci mm.x = width * 0.625; 676a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 677a46c0ec8Sopenharmony_ci mb_re = edges.x; 678a46c0ec8Sopenharmony_ci } 679a46c0ec8Sopenharmony_ci 680a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.middlebutton_left_edge = mb_le; 681a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.rightbutton_left_edge = mb_re; 682a46c0ec8Sopenharmony_ci} 683a46c0ec8Sopenharmony_ci 684a46c0ec8Sopenharmony_civoid 685a46c0ec8Sopenharmony_citp_init_top_softbuttons(struct tp_dispatch *tp, 686a46c0ec8Sopenharmony_ci struct evdev_device *device, 687a46c0ec8Sopenharmony_ci double topbutton_size_mult) 688a46c0ec8Sopenharmony_ci{ 689a46c0ec8Sopenharmony_ci struct device_coords edges; 690a46c0ec8Sopenharmony_ci 691a46c0ec8Sopenharmony_ci if (tp->buttons.has_topbuttons) { 692a46c0ec8Sopenharmony_ci /* T440s has the top button line 5mm from the top, event 693a46c0ec8Sopenharmony_ci analysis has shown events to start down to ~10mm from the 694a46c0ec8Sopenharmony_ci top - which maps to 15%. We allow the caller to enlarge the 695a46c0ec8Sopenharmony_ci area using a multiplier for the touchpad disabled case. */ 696a46c0ec8Sopenharmony_ci double topsize_mm = 10 * topbutton_size_mult; 697a46c0ec8Sopenharmony_ci struct phys_coords mm; 698a46c0ec8Sopenharmony_ci double width, height; 699a46c0ec8Sopenharmony_ci 700a46c0ec8Sopenharmony_ci evdev_device_get_size(device, &width, &height); 701a46c0ec8Sopenharmony_ci 702a46c0ec8Sopenharmony_ci mm.x = width * 0.60; 703a46c0ec8Sopenharmony_ci mm.y = topsize_mm; 704a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 705a46c0ec8Sopenharmony_ci tp->buttons.top_area.bottom_edge = edges.y; 706a46c0ec8Sopenharmony_ci tp->buttons.top_area.rightbutton_left_edge = edges.x; 707a46c0ec8Sopenharmony_ci 708a46c0ec8Sopenharmony_ci mm.x = width * 0.40; 709a46c0ec8Sopenharmony_ci edges = evdev_device_mm_to_units(device, &mm); 710a46c0ec8Sopenharmony_ci tp->buttons.top_area.leftbutton_right_edge = edges.x; 711a46c0ec8Sopenharmony_ci } else { 712a46c0ec8Sopenharmony_ci tp->buttons.top_area.bottom_edge = INT_MIN; 713a46c0ec8Sopenharmony_ci } 714a46c0ec8Sopenharmony_ci} 715a46c0ec8Sopenharmony_ci 716a46c0ec8Sopenharmony_cistatic inline uint32_t 717a46c0ec8Sopenharmony_citp_button_config_click_get_methods(struct libinput_device *device) 718a46c0ec8Sopenharmony_ci{ 719a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 720a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 721a46c0ec8Sopenharmony_ci uint32_t methods = LIBINPUT_CONFIG_CLICK_METHOD_NONE; 722a46c0ec8Sopenharmony_ci 723a46c0ec8Sopenharmony_ci if (tp->buttons.is_clickpad) { 724a46c0ec8Sopenharmony_ci methods |= LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; 725a46c0ec8Sopenharmony_ci if (tp->has_mt) 726a46c0ec8Sopenharmony_ci methods |= LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 727a46c0ec8Sopenharmony_ci } 728a46c0ec8Sopenharmony_ci 729a46c0ec8Sopenharmony_ci if (evdev->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) 730a46c0ec8Sopenharmony_ci methods |= LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 731a46c0ec8Sopenharmony_ci 732a46c0ec8Sopenharmony_ci return methods; 733a46c0ec8Sopenharmony_ci} 734a46c0ec8Sopenharmony_ci 735a46c0ec8Sopenharmony_cistatic void 736a46c0ec8Sopenharmony_citp_switch_click_method(struct tp_dispatch *tp) 737a46c0ec8Sopenharmony_ci{ 738a46c0ec8Sopenharmony_ci /* 739a46c0ec8Sopenharmony_ci * All we need to do when switching click methods is to change the 740a46c0ec8Sopenharmony_ci * bottom_area.top_edge so that when in clickfinger mode the bottom 741a46c0ec8Sopenharmony_ci * touchpad area is not dead wrt finger movement starting there. 742a46c0ec8Sopenharmony_ci * 743a46c0ec8Sopenharmony_ci * We do not need to take any state into account, fingers which are 744a46c0ec8Sopenharmony_ci * already down will simply keep the state / area they have assigned 745a46c0ec8Sopenharmony_ci * until they are released, and the post_button_events path is state 746a46c0ec8Sopenharmony_ci * agnostic. 747a46c0ec8Sopenharmony_ci */ 748a46c0ec8Sopenharmony_ci 749a46c0ec8Sopenharmony_ci switch (tp->buttons.click_method) { 750a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: 751a46c0ec8Sopenharmony_ci tp_init_softbuttons(tp, tp->device); 752a46c0ec8Sopenharmony_ci break; 753a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: 754a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_CLICK_METHOD_NONE: 755a46c0ec8Sopenharmony_ci tp->buttons.bottom_area.top_edge = INT_MAX; 756a46c0ec8Sopenharmony_ci break; 757a46c0ec8Sopenharmony_ci } 758a46c0ec8Sopenharmony_ci} 759a46c0ec8Sopenharmony_ci 760a46c0ec8Sopenharmony_cistatic enum libinput_config_status 761a46c0ec8Sopenharmony_citp_button_config_click_set_method(struct libinput_device *device, 762a46c0ec8Sopenharmony_ci enum libinput_config_click_method method) 763a46c0ec8Sopenharmony_ci{ 764a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 765a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 766a46c0ec8Sopenharmony_ci 767a46c0ec8Sopenharmony_ci tp->buttons.click_method = method; 768a46c0ec8Sopenharmony_ci tp_switch_click_method(tp); 769a46c0ec8Sopenharmony_ci 770a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 771a46c0ec8Sopenharmony_ci} 772a46c0ec8Sopenharmony_ci 773a46c0ec8Sopenharmony_cistatic enum libinput_config_click_method 774a46c0ec8Sopenharmony_citp_button_config_click_get_method(struct libinput_device *device) 775a46c0ec8Sopenharmony_ci{ 776a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 777a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 778a46c0ec8Sopenharmony_ci 779a46c0ec8Sopenharmony_ci return tp->buttons.click_method; 780a46c0ec8Sopenharmony_ci} 781a46c0ec8Sopenharmony_ci 782a46c0ec8Sopenharmony_cistatic enum libinput_config_click_method 783a46c0ec8Sopenharmony_citp_click_get_default_method(struct tp_dispatch *tp) 784a46c0ec8Sopenharmony_ci{ 785a46c0ec8Sopenharmony_ci struct evdev_device *device = tp->device; 786a46c0ec8Sopenharmony_ci 787a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(device, QUIRK_MODEL_CHROMEBOOK) || 788a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_SYSTEM76_BONOBO) || 789a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_SYSTEM76_GALAGO) || 790a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_SYSTEM76_KUDU) || 791a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_CLEVO_W740SU) || 792a46c0ec8Sopenharmony_ci evdev_device_has_model_quirk(device, QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON)) 793a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 794a46c0ec8Sopenharmony_ci 795a46c0ec8Sopenharmony_ci if (!tp->buttons.is_clickpad) 796a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_CLICK_METHOD_NONE; 797a46c0ec8Sopenharmony_ci 798a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(device, QUIRK_MODEL_APPLE_TOUCHPAD)) 799a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 800a46c0ec8Sopenharmony_ci 801a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; 802a46c0ec8Sopenharmony_ci} 803a46c0ec8Sopenharmony_ci 804a46c0ec8Sopenharmony_cistatic enum libinput_config_click_method 805a46c0ec8Sopenharmony_citp_button_config_click_get_default_method(struct libinput_device *device) 806a46c0ec8Sopenharmony_ci{ 807a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 808a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch; 809a46c0ec8Sopenharmony_ci 810a46c0ec8Sopenharmony_ci return tp_click_get_default_method(tp); 811a46c0ec8Sopenharmony_ci} 812a46c0ec8Sopenharmony_ci 813a46c0ec8Sopenharmony_civoid 814a46c0ec8Sopenharmony_citp_clickpad_middlebutton_apply_config(struct evdev_device *device) 815a46c0ec8Sopenharmony_ci{ 816a46c0ec8Sopenharmony_ci struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; 817a46c0ec8Sopenharmony_ci 818a46c0ec8Sopenharmony_ci if (!tp->buttons.is_clickpad || 819a46c0ec8Sopenharmony_ci tp->buttons.state != 0) 820a46c0ec8Sopenharmony_ci return; 821a46c0ec8Sopenharmony_ci 822a46c0ec8Sopenharmony_ci if (device->middlebutton.want_enabled == 823a46c0ec8Sopenharmony_ci device->middlebutton.enabled) 824a46c0ec8Sopenharmony_ci return; 825a46c0ec8Sopenharmony_ci 826a46c0ec8Sopenharmony_ci device->middlebutton.enabled = device->middlebutton.want_enabled; 827a46c0ec8Sopenharmony_ci if (tp->buttons.click_method == 828a46c0ec8Sopenharmony_ci LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) 829a46c0ec8Sopenharmony_ci tp_init_softbuttons(tp, device); 830a46c0ec8Sopenharmony_ci} 831a46c0ec8Sopenharmony_ci 832a46c0ec8Sopenharmony_cistatic int 833a46c0ec8Sopenharmony_citp_clickpad_middlebutton_is_available(struct libinput_device *device) 834a46c0ec8Sopenharmony_ci{ 835a46c0ec8Sopenharmony_ci return evdev_middlebutton_is_available(device); 836a46c0ec8Sopenharmony_ci} 837a46c0ec8Sopenharmony_ci 838a46c0ec8Sopenharmony_cistatic enum libinput_config_status 839a46c0ec8Sopenharmony_citp_clickpad_middlebutton_set(struct libinput_device *device, 840a46c0ec8Sopenharmony_ci enum libinput_config_middle_emulation_state enable) 841a46c0ec8Sopenharmony_ci{ 842a46c0ec8Sopenharmony_ci struct evdev_device *evdev = evdev_device(device); 843a46c0ec8Sopenharmony_ci 844a46c0ec8Sopenharmony_ci switch (enable) { 845a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: 846a46c0ec8Sopenharmony_ci evdev->middlebutton.want_enabled = true; 847a46c0ec8Sopenharmony_ci break; 848a46c0ec8Sopenharmony_ci case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: 849a46c0ec8Sopenharmony_ci evdev->middlebutton.want_enabled = false; 850a46c0ec8Sopenharmony_ci break; 851a46c0ec8Sopenharmony_ci default: 852a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_INVALID; 853a46c0ec8Sopenharmony_ci } 854a46c0ec8Sopenharmony_ci 855a46c0ec8Sopenharmony_ci tp_clickpad_middlebutton_apply_config(evdev); 856a46c0ec8Sopenharmony_ci 857a46c0ec8Sopenharmony_ci return LIBINPUT_CONFIG_STATUS_SUCCESS; 858a46c0ec8Sopenharmony_ci} 859a46c0ec8Sopenharmony_ci 860a46c0ec8Sopenharmony_cistatic enum libinput_config_middle_emulation_state 861a46c0ec8Sopenharmony_citp_clickpad_middlebutton_get(struct libinput_device *device) 862a46c0ec8Sopenharmony_ci{ 863a46c0ec8Sopenharmony_ci return evdev_middlebutton_get(device); 864a46c0ec8Sopenharmony_ci} 865a46c0ec8Sopenharmony_ci 866a46c0ec8Sopenharmony_cistatic enum libinput_config_middle_emulation_state 867a46c0ec8Sopenharmony_citp_clickpad_middlebutton_get_default(struct libinput_device *device) 868a46c0ec8Sopenharmony_ci{ 869a46c0ec8Sopenharmony_ci return evdev_middlebutton_get_default(device); 870a46c0ec8Sopenharmony_ci} 871a46c0ec8Sopenharmony_ci 872a46c0ec8Sopenharmony_cistatic inline void 873a46c0ec8Sopenharmony_citp_init_clickpad_middlebutton_emulation(struct tp_dispatch *tp, 874a46c0ec8Sopenharmony_ci struct evdev_device *device) 875a46c0ec8Sopenharmony_ci{ 876a46c0ec8Sopenharmony_ci device->middlebutton.enabled_default = false; 877a46c0ec8Sopenharmony_ci device->middlebutton.want_enabled = false; 878a46c0ec8Sopenharmony_ci device->middlebutton.enabled = false; 879a46c0ec8Sopenharmony_ci 880a46c0ec8Sopenharmony_ci device->middlebutton.config.available = tp_clickpad_middlebutton_is_available; 881a46c0ec8Sopenharmony_ci device->middlebutton.config.set = tp_clickpad_middlebutton_set; 882a46c0ec8Sopenharmony_ci device->middlebutton.config.get = tp_clickpad_middlebutton_get; 883a46c0ec8Sopenharmony_ci device->middlebutton.config.get_default = tp_clickpad_middlebutton_get_default; 884a46c0ec8Sopenharmony_ci device->base.config.middle_emulation = &device->middlebutton.config; 885a46c0ec8Sopenharmony_ci} 886a46c0ec8Sopenharmony_ci 887a46c0ec8Sopenharmony_cistatic inline void 888a46c0ec8Sopenharmony_citp_init_middlebutton_emulation(struct tp_dispatch *tp, 889a46c0ec8Sopenharmony_ci struct evdev_device *device) 890a46c0ec8Sopenharmony_ci{ 891a46c0ec8Sopenharmony_ci bool enable_by_default, 892a46c0ec8Sopenharmony_ci want_config_option; 893a46c0ec8Sopenharmony_ci 894a46c0ec8Sopenharmony_ci /* On clickpads we provide the config option but disable by default. 895a46c0ec8Sopenharmony_ci When enabled, the middle software button disappears */ 896a46c0ec8Sopenharmony_ci if (tp->buttons.is_clickpad) { 897a46c0ec8Sopenharmony_ci tp_init_clickpad_middlebutton_emulation(tp, device); 898a46c0ec8Sopenharmony_ci return; 899a46c0ec8Sopenharmony_ci } 900a46c0ec8Sopenharmony_ci 901a46c0ec8Sopenharmony_ci /* init middle button emulation on non-clickpads, but only if we 902a46c0ec8Sopenharmony_ci * don't have a middle button. Exception: ALPS touchpads don't know 903a46c0ec8Sopenharmony_ci * if they have a middle button, so we always want the option there 904a46c0ec8Sopenharmony_ci * and enabled by default. 905a46c0ec8Sopenharmony_ci */ 906a46c0ec8Sopenharmony_ci if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) { 907a46c0ec8Sopenharmony_ci enable_by_default = true; 908a46c0ec8Sopenharmony_ci want_config_option = false; 909a46c0ec8Sopenharmony_ci } else if (evdev_device_has_model_quirk(device, 910a46c0ec8Sopenharmony_ci QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD)) { 911a46c0ec8Sopenharmony_ci enable_by_default = true; 912a46c0ec8Sopenharmony_ci want_config_option = true; 913a46c0ec8Sopenharmony_ci } else 914a46c0ec8Sopenharmony_ci return; 915a46c0ec8Sopenharmony_ci 916a46c0ec8Sopenharmony_ci evdev_init_middlebutton(tp->device, 917a46c0ec8Sopenharmony_ci enable_by_default, 918a46c0ec8Sopenharmony_ci want_config_option); 919a46c0ec8Sopenharmony_ci} 920a46c0ec8Sopenharmony_ci 921a46c0ec8Sopenharmony_cistatic bool 922a46c0ec8Sopenharmony_citp_guess_clickpad(const struct tp_dispatch *tp, struct evdev_device *device) 923a46c0ec8Sopenharmony_ci{ 924a46c0ec8Sopenharmony_ci bool is_clickpad; 925a46c0ec8Sopenharmony_ci bool has_left = libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT), 926a46c0ec8Sopenharmony_ci has_middle = libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE), 927a46c0ec8Sopenharmony_ci has_right = libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT); 928a46c0ec8Sopenharmony_ci 929a46c0ec8Sopenharmony_ci is_clickpad = libevdev_has_property(device->evdev, INPUT_PROP_BUTTONPAD); 930a46c0ec8Sopenharmony_ci 931a46c0ec8Sopenharmony_ci /* A non-clickpad without a right button is a clickpad, assume the 932a46c0ec8Sopenharmony_ci * kernel is wrong. 933a46c0ec8Sopenharmony_ci * Exceptions here: 934a46c0ec8Sopenharmony_ci * - The one-button Apple touchpad (discontinued in 2008) has a 935a46c0ec8Sopenharmony_ci * single physical button 936a46c0ec8Sopenharmony_ci * - Wacom touch devices have neither left nor right buttons 937a46c0ec8Sopenharmony_ci */ 938a46c0ec8Sopenharmony_ci if (!is_clickpad && has_left && !has_right && 939a46c0ec8Sopenharmony_ci (tp->device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) == 0) { 940a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(device, 941a46c0ec8Sopenharmony_ci "missing right button, assuming it is a clickpad.\n"); 942a46c0ec8Sopenharmony_ci is_clickpad = true; 943a46c0ec8Sopenharmony_ci } 944a46c0ec8Sopenharmony_ci 945a46c0ec8Sopenharmony_ci if (has_middle || has_right) { 946a46c0ec8Sopenharmony_ci if (is_clickpad) 947a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(device, 948a46c0ec8Sopenharmony_ci "clickpad advertising right button. " 949a46c0ec8Sopenharmony_ci "See %s/clickpad-with-right-button.html for details\n", 950a46c0ec8Sopenharmony_ci HTTP_DOC_LINK); 951a46c0ec8Sopenharmony_ci } else if (has_left & 952a46c0ec8Sopenharmony_ci !is_clickpad && 953a46c0ec8Sopenharmony_ci libevdev_get_id_vendor(device->evdev) != VENDOR_ID_APPLE) { 954a46c0ec8Sopenharmony_ci evdev_log_bug_kernel(device, 955a46c0ec8Sopenharmony_ci "non clickpad without right button?\n"); 956a46c0ec8Sopenharmony_ci } 957a46c0ec8Sopenharmony_ci 958a46c0ec8Sopenharmony_ci return is_clickpad; 959a46c0ec8Sopenharmony_ci} 960a46c0ec8Sopenharmony_ci 961a46c0ec8Sopenharmony_civoid 962a46c0ec8Sopenharmony_citp_init_buttons(struct tp_dispatch *tp, 963a46c0ec8Sopenharmony_ci struct evdev_device *device) 964a46c0ec8Sopenharmony_ci{ 965a46c0ec8Sopenharmony_ci struct tp_touch *t; 966a46c0ec8Sopenharmony_ci const struct input_absinfo *absinfo_x, *absinfo_y; 967a46c0ec8Sopenharmony_ci int i; 968a46c0ec8Sopenharmony_ci 969a46c0ec8Sopenharmony_ci tp->buttons.is_clickpad = tp_guess_clickpad(tp, device); 970a46c0ec8Sopenharmony_ci 971a46c0ec8Sopenharmony_ci tp->buttons.has_topbuttons = libevdev_has_property(device->evdev, 972a46c0ec8Sopenharmony_ci INPUT_PROP_TOPBUTTONPAD); 973a46c0ec8Sopenharmony_ci 974a46c0ec8Sopenharmony_ci absinfo_x = device->abs.absinfo_x; 975a46c0ec8Sopenharmony_ci absinfo_y = device->abs.absinfo_y; 976a46c0ec8Sopenharmony_ci 977a46c0ec8Sopenharmony_ci /* pinned-finger motion threshold, see tp_unpin_finger. */ 978a46c0ec8Sopenharmony_ci tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution; 979a46c0ec8Sopenharmony_ci tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution; 980a46c0ec8Sopenharmony_ci 981a46c0ec8Sopenharmony_ci tp->buttons.config_method.get_methods = tp_button_config_click_get_methods; 982a46c0ec8Sopenharmony_ci tp->buttons.config_method.set_method = tp_button_config_click_set_method; 983a46c0ec8Sopenharmony_ci tp->buttons.config_method.get_method = tp_button_config_click_get_method; 984a46c0ec8Sopenharmony_ci tp->buttons.config_method.get_default_method = tp_button_config_click_get_default_method; 985a46c0ec8Sopenharmony_ci tp->device->base.config.click_method = &tp->buttons.config_method; 986a46c0ec8Sopenharmony_ci 987a46c0ec8Sopenharmony_ci tp->buttons.click_method = tp_click_get_default_method(tp); 988a46c0ec8Sopenharmony_ci tp_switch_click_method(tp); 989a46c0ec8Sopenharmony_ci 990a46c0ec8Sopenharmony_ci tp_init_top_softbuttons(tp, device, 1.0); 991a46c0ec8Sopenharmony_ci 992a46c0ec8Sopenharmony_ci tp_init_middlebutton_emulation(tp, device); 993a46c0ec8Sopenharmony_ci 994a46c0ec8Sopenharmony_ci i = 0; 995a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 996a46c0ec8Sopenharmony_ci char timer_name[64]; 997a46c0ec8Sopenharmony_ci i++; 998a46c0ec8Sopenharmony_ci 999a46c0ec8Sopenharmony_ci snprintf(timer_name, 1000a46c0ec8Sopenharmony_ci sizeof(timer_name), 1001a46c0ec8Sopenharmony_ci "%s (%d) button", 1002a46c0ec8Sopenharmony_ci evdev_device_get_sysname(device), 1003a46c0ec8Sopenharmony_ci i); 1004a46c0ec8Sopenharmony_ci t->button.state = BUTTON_STATE_NONE; 1005a46c0ec8Sopenharmony_ci libinput_timer_init(&t->button.timer, 1006a46c0ec8Sopenharmony_ci tp_libinput_context(tp), 1007a46c0ec8Sopenharmony_ci timer_name, 1008a46c0ec8Sopenharmony_ci tp_button_handle_timeout, t); 1009a46c0ec8Sopenharmony_ci } 1010a46c0ec8Sopenharmony_ci} 1011a46c0ec8Sopenharmony_ci 1012a46c0ec8Sopenharmony_civoid 1013a46c0ec8Sopenharmony_citp_remove_buttons(struct tp_dispatch *tp) 1014a46c0ec8Sopenharmony_ci{ 1015a46c0ec8Sopenharmony_ci struct tp_touch *t; 1016a46c0ec8Sopenharmony_ci 1017a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1018a46c0ec8Sopenharmony_ci libinput_timer_cancel(&t->button.timer); 1019a46c0ec8Sopenharmony_ci libinput_timer_destroy(&t->button.timer); 1020a46c0ec8Sopenharmony_ci } 1021a46c0ec8Sopenharmony_ci} 1022a46c0ec8Sopenharmony_ci 1023a46c0ec8Sopenharmony_cistatic int 1024a46c0ec8Sopenharmony_citp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) 1025a46c0ec8Sopenharmony_ci{ 1026a46c0ec8Sopenharmony_ci uint32_t current, old, button; 1027a46c0ec8Sopenharmony_ci 1028a46c0ec8Sopenharmony_ci current = tp->buttons.state; 1029a46c0ec8Sopenharmony_ci old = tp->buttons.old_state; 1030a46c0ec8Sopenharmony_ci button = BTN_LEFT; 1031a46c0ec8Sopenharmony_ci 1032a46c0ec8Sopenharmony_ci while (current || old) { 1033a46c0ec8Sopenharmony_ci enum libinput_button_state state; 1034a46c0ec8Sopenharmony_ci 1035a46c0ec8Sopenharmony_ci if ((current & 0x1) ^ (old & 0x1)) { 1036a46c0ec8Sopenharmony_ci uint32_t b; 1037a46c0ec8Sopenharmony_ci 1038a46c0ec8Sopenharmony_ci if (!!(current & 0x1)) 1039a46c0ec8Sopenharmony_ci state = LIBINPUT_BUTTON_STATE_PRESSED; 1040a46c0ec8Sopenharmony_ci else 1041a46c0ec8Sopenharmony_ci state = LIBINPUT_BUTTON_STATE_RELEASED; 1042a46c0ec8Sopenharmony_ci 1043a46c0ec8Sopenharmony_ci b = evdev_to_left_handed(tp->device, button); 1044a46c0ec8Sopenharmony_ci evdev_pointer_notify_physical_button(tp->device, 1045a46c0ec8Sopenharmony_ci time, 1046a46c0ec8Sopenharmony_ci b, 1047a46c0ec8Sopenharmony_ci state); 1048a46c0ec8Sopenharmony_ci } 1049a46c0ec8Sopenharmony_ci 1050a46c0ec8Sopenharmony_ci button++; 1051a46c0ec8Sopenharmony_ci current >>= 1; 1052a46c0ec8Sopenharmony_ci old >>= 1; 1053a46c0ec8Sopenharmony_ci } 1054a46c0ec8Sopenharmony_ci 1055a46c0ec8Sopenharmony_ci return 0; 1056a46c0ec8Sopenharmony_ci} 1057a46c0ec8Sopenharmony_ci 1058a46c0ec8Sopenharmony_cistatic inline bool 1059a46c0ec8Sopenharmony_citp_clickfinger_within_distance(struct tp_dispatch *tp, 1060a46c0ec8Sopenharmony_ci struct tp_touch *t1, 1061a46c0ec8Sopenharmony_ci struct tp_touch *t2) 1062a46c0ec8Sopenharmony_ci{ 1063a46c0ec8Sopenharmony_ci double x, y; 1064a46c0ec8Sopenharmony_ci bool within_distance = false; 1065a46c0ec8Sopenharmony_ci int xres, yres; 1066a46c0ec8Sopenharmony_ci int bottom_threshold; 1067a46c0ec8Sopenharmony_ci 1068a46c0ec8Sopenharmony_ci if (!t1 || !t2) 1069a46c0ec8Sopenharmony_ci return 0; 1070a46c0ec8Sopenharmony_ci 1071a46c0ec8Sopenharmony_ci if (tp_thumb_ignored(tp, t1) || tp_thumb_ignored(tp, t2)) 1072a46c0ec8Sopenharmony_ci return 0; 1073a46c0ec8Sopenharmony_ci 1074a46c0ec8Sopenharmony_ci x = abs(t1->point.x - t2->point.x); 1075a46c0ec8Sopenharmony_ci y = abs(t1->point.y - t2->point.y); 1076a46c0ec8Sopenharmony_ci 1077a46c0ec8Sopenharmony_ci xres = tp->device->abs.absinfo_x->resolution; 1078a46c0ec8Sopenharmony_ci yres = tp->device->abs.absinfo_y->resolution; 1079a46c0ec8Sopenharmony_ci x /= xres; 1080a46c0ec8Sopenharmony_ci y /= yres; 1081a46c0ec8Sopenharmony_ci 1082a46c0ec8Sopenharmony_ci /* maximum horiz spread is 40mm horiz, 30mm vert, anything wider 1083a46c0ec8Sopenharmony_ci * than that is probably a gesture. */ 1084a46c0ec8Sopenharmony_ci if (x > 40 || y > 30) 1085a46c0ec8Sopenharmony_ci goto out; 1086a46c0ec8Sopenharmony_ci 1087a46c0ec8Sopenharmony_ci within_distance = true; 1088a46c0ec8Sopenharmony_ci 1089a46c0ec8Sopenharmony_ci /* if y spread is <= 20mm, they're definitely together. */ 1090a46c0ec8Sopenharmony_ci if (y <= 20) 1091a46c0ec8Sopenharmony_ci goto out; 1092a46c0ec8Sopenharmony_ci 1093a46c0ec8Sopenharmony_ci /* if they're vertically spread between 20-40mm, they're not 1094a46c0ec8Sopenharmony_ci * together if: 1095a46c0ec8Sopenharmony_ci * - the touchpad's vertical size is >50mm, anything smaller is 1096a46c0ec8Sopenharmony_ci * unlikely to have a thumb resting on it 1097a46c0ec8Sopenharmony_ci * - and one of the touches is in the bottom 20mm of the touchpad 1098a46c0ec8Sopenharmony_ci * and the other one isn't 1099a46c0ec8Sopenharmony_ci */ 1100a46c0ec8Sopenharmony_ci 1101a46c0ec8Sopenharmony_ci if (tp->device->abs.dimensions.y/yres < 50) 1102a46c0ec8Sopenharmony_ci goto out; 1103a46c0ec8Sopenharmony_ci 1104a46c0ec8Sopenharmony_ci bottom_threshold = tp->device->abs.absinfo_y->maximum - 20 * yres; 1105a46c0ec8Sopenharmony_ci if ((t1->point.y > bottom_threshold) != 1106a46c0ec8Sopenharmony_ci (t2->point.y > bottom_threshold)) 1107a46c0ec8Sopenharmony_ci within_distance = 0; 1108a46c0ec8Sopenharmony_ci 1109a46c0ec8Sopenharmony_ciout: 1110a46c0ec8Sopenharmony_ci return within_distance; 1111a46c0ec8Sopenharmony_ci} 1112a46c0ec8Sopenharmony_ci 1113a46c0ec8Sopenharmony_cistatic uint32_t 1114a46c0ec8Sopenharmony_citp_clickfinger_set_button(struct tp_dispatch *tp) 1115a46c0ec8Sopenharmony_ci{ 1116a46c0ec8Sopenharmony_ci uint32_t button; 1117a46c0ec8Sopenharmony_ci unsigned int nfingers = 0; 1118a46c0ec8Sopenharmony_ci struct tp_touch *t; 1119a46c0ec8Sopenharmony_ci struct tp_touch *first = NULL, 1120a46c0ec8Sopenharmony_ci *second = NULL; 1121a46c0ec8Sopenharmony_ci 1122a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1123a46c0ec8Sopenharmony_ci if (t->state != TOUCH_BEGIN && t->state != TOUCH_UPDATE) 1124a46c0ec8Sopenharmony_ci continue; 1125a46c0ec8Sopenharmony_ci 1126a46c0ec8Sopenharmony_ci if (tp_thumb_ignored(tp, t)) 1127a46c0ec8Sopenharmony_ci continue; 1128a46c0ec8Sopenharmony_ci 1129a46c0ec8Sopenharmony_ci if (t->palm.state != PALM_NONE) 1130a46c0ec8Sopenharmony_ci continue; 1131a46c0ec8Sopenharmony_ci 1132a46c0ec8Sopenharmony_ci nfingers++; 1133a46c0ec8Sopenharmony_ci 1134a46c0ec8Sopenharmony_ci if (!first) 1135a46c0ec8Sopenharmony_ci first = t; 1136a46c0ec8Sopenharmony_ci else if (!second) 1137a46c0ec8Sopenharmony_ci second = t; 1138a46c0ec8Sopenharmony_ci } 1139a46c0ec8Sopenharmony_ci 1140a46c0ec8Sopenharmony_ci /* Only check for finger distance when there are 2 fingers on the 1141a46c0ec8Sopenharmony_ci * touchpad */ 1142a46c0ec8Sopenharmony_ci if (nfingers != 2) 1143a46c0ec8Sopenharmony_ci goto out; 1144a46c0ec8Sopenharmony_ci 1145a46c0ec8Sopenharmony_ci if (tp_clickfinger_within_distance(tp, first, second)) 1146a46c0ec8Sopenharmony_ci nfingers = 2; 1147a46c0ec8Sopenharmony_ci else 1148a46c0ec8Sopenharmony_ci nfingers = 1; 1149a46c0ec8Sopenharmony_ci 1150a46c0ec8Sopenharmony_ciout: 1151a46c0ec8Sopenharmony_ci switch (nfingers) { 1152a46c0ec8Sopenharmony_ci case 0: 1153a46c0ec8Sopenharmony_ci case 1: button = BTN_LEFT; break; 1154a46c0ec8Sopenharmony_ci case 2: button = BTN_RIGHT; break; 1155a46c0ec8Sopenharmony_ci case 3: button = BTN_MIDDLE; break; 1156a46c0ec8Sopenharmony_ci default: 1157a46c0ec8Sopenharmony_ci button = 0; 1158a46c0ec8Sopenharmony_ci break; 1159a46c0ec8Sopenharmony_ci } 1160a46c0ec8Sopenharmony_ci 1161a46c0ec8Sopenharmony_ci return button; 1162a46c0ec8Sopenharmony_ci} 1163a46c0ec8Sopenharmony_ci 1164a46c0ec8Sopenharmony_cistatic int 1165a46c0ec8Sopenharmony_citp_notify_clickpadbutton(struct tp_dispatch *tp, 1166a46c0ec8Sopenharmony_ci uint64_t time, 1167a46c0ec8Sopenharmony_ci uint32_t button, 1168a46c0ec8Sopenharmony_ci uint32_t is_topbutton, 1169a46c0ec8Sopenharmony_ci enum libinput_button_state state) 1170a46c0ec8Sopenharmony_ci{ 1171a46c0ec8Sopenharmony_ci /* If we've a trackpoint, send top buttons through the trackpoint */ 1172a46c0ec8Sopenharmony_ci if (tp->buttons.trackpoint) { 1173a46c0ec8Sopenharmony_ci if (is_topbutton) { 1174a46c0ec8Sopenharmony_ci struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch; 1175a46c0ec8Sopenharmony_ci struct input_event event, syn_report; 1176a46c0ec8Sopenharmony_ci int value; 1177a46c0ec8Sopenharmony_ci 1178a46c0ec8Sopenharmony_ci value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0; 1179a46c0ec8Sopenharmony_ci event = input_event_init(time, EV_KEY, button, value); 1180a46c0ec8Sopenharmony_ci syn_report = input_event_init(time, EV_SYN, SYN_REPORT, 0); 1181a46c0ec8Sopenharmony_ci dispatch->interface->process(dispatch, 1182a46c0ec8Sopenharmony_ci tp->buttons.trackpoint, 1183a46c0ec8Sopenharmony_ci &event, 1184a46c0ec8Sopenharmony_ci time); 1185a46c0ec8Sopenharmony_ci dispatch->interface->process(dispatch, 1186a46c0ec8Sopenharmony_ci tp->buttons.trackpoint, 1187a46c0ec8Sopenharmony_ci &syn_report, 1188a46c0ec8Sopenharmony_ci time); 1189a46c0ec8Sopenharmony_ci return 1; 1190a46c0ec8Sopenharmony_ci } 1191a46c0ec8Sopenharmony_ci /* Ignore button events not for the trackpoint while suspended */ 1192a46c0ec8Sopenharmony_ci if (tp->device->is_suspended) 1193a46c0ec8Sopenharmony_ci return 0; 1194a46c0ec8Sopenharmony_ci } 1195a46c0ec8Sopenharmony_ci 1196a46c0ec8Sopenharmony_ci /* A button click always terminates edge scrolling, even if we 1197a46c0ec8Sopenharmony_ci * don't end up sending a button event. */ 1198a46c0ec8Sopenharmony_ci tp_edge_scroll_stop_events(tp, time); 1199a46c0ec8Sopenharmony_ci 1200a46c0ec8Sopenharmony_ci /* 1201a46c0ec8Sopenharmony_ci * If the user has requested clickfinger replace the button chosen 1202a46c0ec8Sopenharmony_ci * by the softbutton code with one based on the number of fingers. 1203a46c0ec8Sopenharmony_ci */ 1204a46c0ec8Sopenharmony_ci if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && 1205a46c0ec8Sopenharmony_ci state == LIBINPUT_BUTTON_STATE_PRESSED) { 1206a46c0ec8Sopenharmony_ci button = tp_clickfinger_set_button(tp); 1207a46c0ec8Sopenharmony_ci tp->buttons.active = button; 1208a46c0ec8Sopenharmony_ci 1209a46c0ec8Sopenharmony_ci if (!button) 1210a46c0ec8Sopenharmony_ci return 0; 1211a46c0ec8Sopenharmony_ci } 1212a46c0ec8Sopenharmony_ci 1213a46c0ec8Sopenharmony_ci evdev_pointer_notify_button(tp->device, time, button, state); 1214a46c0ec8Sopenharmony_ci return 1; 1215a46c0ec8Sopenharmony_ci} 1216a46c0ec8Sopenharmony_ci 1217a46c0ec8Sopenharmony_cistatic int 1218a46c0ec8Sopenharmony_citp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) 1219a46c0ec8Sopenharmony_ci{ 1220a46c0ec8Sopenharmony_ci uint32_t current, old, button, is_top; 1221a46c0ec8Sopenharmony_ci enum libinput_button_state state; 1222a46c0ec8Sopenharmony_ci enum { AREA = 0x01, LEFT = 0x02, MIDDLE = 0x04, RIGHT = 0x08 }; 1223a46c0ec8Sopenharmony_ci bool want_left_handed = true; 1224a46c0ec8Sopenharmony_ci 1225a46c0ec8Sopenharmony_ci current = tp->buttons.state; 1226a46c0ec8Sopenharmony_ci old = tp->buttons.old_state; 1227a46c0ec8Sopenharmony_ci is_top = 0; 1228a46c0ec8Sopenharmony_ci 1229a46c0ec8Sopenharmony_ci if (!tp->buttons.click_pending && current == old) 1230a46c0ec8Sopenharmony_ci return 0; 1231a46c0ec8Sopenharmony_ci 1232a46c0ec8Sopenharmony_ci if (current) { 1233a46c0ec8Sopenharmony_ci struct tp_touch *t; 1234a46c0ec8Sopenharmony_ci uint32_t area = 0; 1235a46c0ec8Sopenharmony_ci 1236a46c0ec8Sopenharmony_ci if (evdev_device_has_model_quirk(tp->device, 1237a46c0ec8Sopenharmony_ci QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS) && 1238a46c0ec8Sopenharmony_ci tp->nactive_slots == 0) { 1239a46c0ec8Sopenharmony_ci /* Some touchpads, notably those on the Dell XPS 15 9500, 1240a46c0ec8Sopenharmony_ci * are prone to registering touchpad clicks when the 1241a46c0ec8Sopenharmony_ci * case is sufficiently flexed. Ignore these by 1242a46c0ec8Sopenharmony_ci * disregarding any clicks that are registered without 1243a46c0ec8Sopenharmony_ci * touchpad touch. */ 1244a46c0ec8Sopenharmony_ci tp->buttons.click_pending = true; 1245a46c0ec8Sopenharmony_ci return 0; 1246a46c0ec8Sopenharmony_ci } 1247a46c0ec8Sopenharmony_ci 1248a46c0ec8Sopenharmony_ci tp_for_each_touch(tp, t) { 1249a46c0ec8Sopenharmony_ci switch (t->button.current) { 1250a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_AREA: 1251a46c0ec8Sopenharmony_ci area |= AREA; 1252a46c0ec8Sopenharmony_ci break; 1253a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_L: 1254a46c0ec8Sopenharmony_ci is_top = 1; 1255a46c0ec8Sopenharmony_ci _fallthrough_; 1256a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_L: 1257a46c0ec8Sopenharmony_ci area |= LEFT; 1258a46c0ec8Sopenharmony_ci break; 1259a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_M: 1260a46c0ec8Sopenharmony_ci is_top = 1; 1261a46c0ec8Sopenharmony_ci _fallthrough_; 1262a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_M: 1263a46c0ec8Sopenharmony_ci area |= MIDDLE; 1264a46c0ec8Sopenharmony_ci break; 1265a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_TOP_R: 1266a46c0ec8Sopenharmony_ci is_top = 1; 1267a46c0ec8Sopenharmony_ci _fallthrough_; 1268a46c0ec8Sopenharmony_ci case BUTTON_EVENT_IN_BOTTOM_R: 1269a46c0ec8Sopenharmony_ci area |= RIGHT; 1270a46c0ec8Sopenharmony_ci break; 1271a46c0ec8Sopenharmony_ci default: 1272a46c0ec8Sopenharmony_ci break; 1273a46c0ec8Sopenharmony_ci } 1274a46c0ec8Sopenharmony_ci } 1275a46c0ec8Sopenharmony_ci 1276a46c0ec8Sopenharmony_ci if (area == 0 && 1277a46c0ec8Sopenharmony_ci tp->buttons.click_method != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) { 1278a46c0ec8Sopenharmony_ci /* No touches, wait for a touch before processing */ 1279a46c0ec8Sopenharmony_ci tp->buttons.click_pending = true; 1280a46c0ec8Sopenharmony_ci return 0; 1281a46c0ec8Sopenharmony_ci } 1282a46c0ec8Sopenharmony_ci 1283a46c0ec8Sopenharmony_ci if ((tp->device->middlebutton.enabled || is_top) && 1284a46c0ec8Sopenharmony_ci (area & LEFT) && (area & RIGHT)) { 1285a46c0ec8Sopenharmony_ci button = BTN_MIDDLE; 1286a46c0ec8Sopenharmony_ci } else if (area & MIDDLE) { 1287a46c0ec8Sopenharmony_ci button = BTN_MIDDLE; 1288a46c0ec8Sopenharmony_ci } else if (area & RIGHT) { 1289a46c0ec8Sopenharmony_ci button = BTN_RIGHT; 1290a46c0ec8Sopenharmony_ci } else if (area & LEFT) { 1291a46c0ec8Sopenharmony_ci button = BTN_LEFT; 1292a46c0ec8Sopenharmony_ci } else { /* main or no area (for clickfinger) is always BTN_LEFT */ 1293a46c0ec8Sopenharmony_ci button = BTN_LEFT; 1294a46c0ec8Sopenharmony_ci want_left_handed = false; 1295a46c0ec8Sopenharmony_ci } 1296a46c0ec8Sopenharmony_ci 1297a46c0ec8Sopenharmony_ci if (is_top) 1298a46c0ec8Sopenharmony_ci want_left_handed = false; 1299a46c0ec8Sopenharmony_ci 1300a46c0ec8Sopenharmony_ci if (want_left_handed) 1301a46c0ec8Sopenharmony_ci button = evdev_to_left_handed(tp->device, button); 1302a46c0ec8Sopenharmony_ci 1303a46c0ec8Sopenharmony_ci tp->buttons.active = button; 1304a46c0ec8Sopenharmony_ci tp->buttons.active_is_topbutton = is_top; 1305a46c0ec8Sopenharmony_ci state = LIBINPUT_BUTTON_STATE_PRESSED; 1306a46c0ec8Sopenharmony_ci } else { 1307a46c0ec8Sopenharmony_ci button = tp->buttons.active; 1308a46c0ec8Sopenharmony_ci is_top = tp->buttons.active_is_topbutton; 1309a46c0ec8Sopenharmony_ci tp->buttons.active = 0; 1310a46c0ec8Sopenharmony_ci tp->buttons.active_is_topbutton = 0; 1311a46c0ec8Sopenharmony_ci state = LIBINPUT_BUTTON_STATE_RELEASED; 1312a46c0ec8Sopenharmony_ci } 1313a46c0ec8Sopenharmony_ci 1314a46c0ec8Sopenharmony_ci tp->buttons.click_pending = false; 1315a46c0ec8Sopenharmony_ci 1316a46c0ec8Sopenharmony_ci if (button) 1317a46c0ec8Sopenharmony_ci return tp_notify_clickpadbutton(tp, 1318a46c0ec8Sopenharmony_ci time, 1319a46c0ec8Sopenharmony_ci button, 1320a46c0ec8Sopenharmony_ci is_top, 1321a46c0ec8Sopenharmony_ci state); 1322a46c0ec8Sopenharmony_ci return 0; 1323a46c0ec8Sopenharmony_ci} 1324a46c0ec8Sopenharmony_ci 1325a46c0ec8Sopenharmony_ciint 1326a46c0ec8Sopenharmony_citp_post_button_events(struct tp_dispatch *tp, uint64_t time) 1327a46c0ec8Sopenharmony_ci{ 1328a46c0ec8Sopenharmony_ci if (tp->buttons.is_clickpad || 1329a46c0ec8Sopenharmony_ci tp->device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) 1330a46c0ec8Sopenharmony_ci return tp_post_clickpadbutton_buttons(tp, time); 1331a46c0ec8Sopenharmony_ci return tp_post_physical_buttons(tp, time); 1332a46c0ec8Sopenharmony_ci} 1333a46c0ec8Sopenharmony_ci 1334a46c0ec8Sopenharmony_cibool 1335a46c0ec8Sopenharmony_citp_button_touch_active(const struct tp_dispatch *tp, 1336a46c0ec8Sopenharmony_ci const struct tp_touch *t) 1337a46c0ec8Sopenharmony_ci{ 1338a46c0ec8Sopenharmony_ci return t->button.state == BUTTON_STATE_AREA || t->button.has_moved; 1339a46c0ec8Sopenharmony_ci} 1340a46c0ec8Sopenharmony_ci 1341a46c0ec8Sopenharmony_cibool 1342a46c0ec8Sopenharmony_citp_button_is_inside_softbutton_area(const struct tp_dispatch *tp, 1343a46c0ec8Sopenharmony_ci const struct tp_touch *t) 1344a46c0ec8Sopenharmony_ci{ 1345a46c0ec8Sopenharmony_ci return is_inside_top_button_area(tp, t) || 1346a46c0ec8Sopenharmony_ci is_inside_bottom_button_area(tp, t); 1347a46c0ec8Sopenharmony_ci} 1348