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