chiark / gitweb /
Offsets: Use ymin value for bottom edge features
[xf86-input-mtrack.git] / src / mtstate.c
1 /***************************************************************************
2  *
3  * Multitouch X driver
4  * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>
5  * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  **************************************************************************/
22
23 #include "mtstate.h"
24 #include "trig.h"
25
26 static int inline percentage(int dividend, int divisor)
27 {
28         return (double)dividend / (double)divisor * 100;
29 }
30
31 static int inline touch_range_ratio(const struct MConfig* cfg, int value)
32 {
33         return (double)(value - cfg->touch_min) / (double)(cfg->touch_max - cfg->touch_min) * 100;
34 }
35
36 /* Check if a finger is touching the trackpad.
37  */
38 static int is_touch(const struct MConfig* cfg,
39                         const struct FingerState* hw)
40 {
41         if (cfg->touch_type == MCFG_SCALE)
42                 return percentage(hw->touch_major, hw->width_major) > cfg->touch_down;
43         else if (cfg->touch_type == MCFG_SIZE)
44                 return touch_range_ratio(cfg, hw->touch_major) > cfg->touch_down;
45         else if (cfg->touch_type == MCFG_PRESSURE)
46                 return touch_range_ratio(cfg, hw->pressure) > cfg->touch_down;
47         else
48                 return 1;
49 }
50
51 /* Check if a finger is released from the touchpad.
52  */
53 static int is_release(const struct MConfig* cfg,
54                         const struct FingerState* hw)
55 {
56         if (cfg->touch_type == MCFG_SCALE)
57                 return percentage(hw->touch_major, hw->width_major) < cfg->touch_up;
58         else if (cfg->touch_type == MCFG_SIZE)
59                 return touch_range_ratio(cfg, hw->touch_major) < cfg->touch_up;
60         else if (cfg->touch_type == MCFG_PRESSURE)
61                 return touch_range_ratio(cfg, hw->pressure) < cfg->touch_up;
62         else
63                 return 0;
64 }
65
66 static int is_thumb(const struct MConfig* cfg,
67                         const struct FingerState* hw)
68 {
69         if (!cfg->touch_minor)
70                 return 0;
71
72         int min = MINVAL(hw->touch_minor, hw->touch_major);
73         int max = MAXVAL(hw->touch_minor, hw->touch_major);
74         int pct = percentage(min, max);
75         int size = touch_range_ratio(cfg, hw->touch_major);
76
77         if (pct < cfg->thumb_ratio && size > cfg->thumb_size) {
78 #if DEBUG_MTSTATE
79                 xf86Msg(X_INFO, "is_thumb: yes %d > %d && %d > %d\n",
80                         pct, cfg->thumb_ratio, size, cfg->thumb_size);
81 #endif
82                 return 1;
83         }
84         else {
85 #if DEBUG_MTSTATE
86                 xf86Msg(X_INFO, "is_thumb: no  %d > %d && %d > %d\n",
87                         pct, cfg->thumb_ratio, size, cfg->thumb_size);
88 #endif
89                 return 0;
90         }
91 }
92
93 static int is_palm(const struct MConfig* cfg,
94                         const struct FingerState* hw)
95 {
96         if (cfg->touch_type != MCFG_SCALE && cfg->touch_type != MCFG_SIZE)
97                 return 0;
98
99         int size = touch_range_ratio(cfg, hw->touch_major);
100         if (size > cfg->palm_size) {
101 #if DEBUG_MTSTATE
102                 xf86Msg(X_INFO, "is_palm: yes %d > %d\n", size, cfg->palm_size);
103 #endif
104                 return 1;
105         }
106         else {
107 #if DEBUG_MTSTATE
108                 xf86Msg(X_INFO, "is_palm: no  %d > %d\n", size, cfg->palm_size);
109 #endif
110                 return 0;
111         }
112 }
113
114 /* Find a touch by its tracking ID.  Return -1 if not found.
115  */
116 static int find_touch(struct MTState* ms,
117                         int tracking_id)
118 {
119         int i;
120         foreach_bit(i, ms->touch_used) {
121                 if (ms->touch[i].tracking_id == tracking_id)
122                         return i;
123         }
124         return -1;
125 }
126
127 /* Add a touch to the MTState.  Return the new index of the touch.
128  */
129 static int touch_append(struct MTState* ms,
130                         const struct MConfig* cfg,
131                         const struct Capabilities* caps,
132                         const struct HWState* hs,
133                         int fn)
134 {
135         int x, y;
136         int n = firstbit(~ms->touch_used);
137         const struct FingerState* fs = &hs->data[fn];
138         if (n < 0)
139                 xf86Msg(X_WARNING, "Too many touches to track. Ignoring touch %d.\n", fs->tracking_id);
140         else {
141                 x = cfg->axis_x_invert ? get_cap_xflip(caps, fs->position_x) : fs->position_x;
142                 y = cfg->axis_y_invert ? get_cap_yflip(caps, fs->position_y) : fs->position_y;
143                 ms->touch[n].state = 0U;
144                 ms->touch[n].flags = 0U;
145                 timercp(&ms->touch[n].down, &hs->evtime);
146                 ms->touch[n].direction = TR_NONE;
147                 ms->touch[n].tracking_id = fs->tracking_id;
148                 ms->touch[n].x = x;
149                 ms->touch[n].y = y;
150                 ms->touch[n].dx = 0;
151                 ms->touch[n].dy = 0;
152                 ms->touch[n].total_dx = 0;
153                 ms->touch[n].total_dy = 0;
154                 SETBIT(ms->touch[n].state, MT_NEW);
155                 SETBIT(ms->touch_used, n);
156         }
157         return n;
158 }
159
160 /* Update a touch.
161  */
162 static void touch_update(struct MTState* ms,
163                         const struct MConfig* cfg,
164                         const struct Capabilities* caps,
165                         const struct FingerState* fs,
166                         int touch)
167 {
168         int x, y;
169         x = cfg->axis_x_invert ? get_cap_xflip(caps, fs->position_x) : fs->position_x;
170         y = cfg->axis_y_invert ? get_cap_yflip(caps, fs->position_y) : fs->position_y;
171         ms->touch[touch].dx = x - ms->touch[touch].x;
172         ms->touch[touch].dy = y - ms->touch[touch].y;
173         ms->touch[touch].total_dx += ms->touch[touch].dx;
174         ms->touch[touch].total_dy += ms->touch[touch].dy;
175         ms->touch[touch].x = x;
176         ms->touch[touch].y = y;
177         ms->touch[touch].direction = trig_direction(ms->touch[touch].dx, ms->touch[touch].dy);
178         CLEARBIT(ms->touch[touch].state, MT_NEW);
179 }
180
181 /* Release a touch.
182  */
183 static void touch_release(struct MTState* ms,
184                         int touch)
185 {
186         ms->touch[touch].dx = 0;
187         ms->touch[touch].dy = 0;
188         ms->touch[touch].direction = TR_NONE;
189         CLEARBIT(ms->touch[touch].state, MT_NEW);
190         SETBIT(ms->touch[touch].state, MT_RELEASED);
191 }
192
193 /* Invalidate all touches.
194  */
195 static void touches_invalidate(struct MTState* ms)
196 {
197         int i;
198         foreach_bit(i, ms->touch_used)
199                 SETBIT(ms->touch[i].state, MT_INVALID);
200 }
201
202 /* Update all touches.
203  */
204 static void touches_update(struct MTState* ms,
205                         const struct MConfig* cfg,
206                         const struct HWState* hs,
207                         const struct Capabilities* caps)
208 {
209         int i, n, disable = 0;
210         // Release missing touches.
211         foreach_bit(i, ms->touch_used) {
212                 if (find_finger(hs, ms->touch[i].tracking_id) == -1)
213                         touch_release(ms, i);
214         }
215
216         // Add and update touches.
217         foreach_bit(i, hs->used) {
218                 n = find_touch(ms, hs->data[i].tracking_id);
219                 if (n >= 0) {
220                         if (is_release(cfg, &hs->data[i]))
221                                 touch_release(ms, n);
222                         else
223                                 touch_update(ms, cfg, caps, &hs->data[i], n);
224                 }
225                 else if (is_touch(cfg, &hs->data[i]))
226                         n = touch_append(ms, cfg, caps, hs, i);
227
228                 if (n >= 0) {
229                         // Track and invalidate thumb, palm, and bottom edge touches.
230                         if (is_thumb(cfg, &hs->data[i]))
231                                 SETBIT(ms->touch[n].state, MT_THUMB);
232                         else
233                                 CLEARBIT(ms->touch[n].state, MT_THUMB);
234                         
235                         if (is_palm(cfg, &hs->data[i]))
236                                 SETBIT(ms->touch[n].state, MT_PALM);
237                         else
238                                 CLEARBIT(ms->touch[n].state, MT_PALM);
239                         
240                         if ((ms->touch[n].y - get_cap_ymin(caps)) > (100 - cfg->bottom_edge)*cfg->pad_height/100) {
241                                 if (GETBIT(ms->touch[n].state, MT_NEW))
242                                         SETBIT(ms->touch[n].state, MT_BOTTOM_EDGE);
243                         }
244                         else
245                                 CLEARBIT(ms->touch[n].state, MT_BOTTOM_EDGE);
246                         
247                         MODBIT(ms->touch[n].state, MT_INVALID,
248                                 GETBIT(ms->touch[n].state, MT_THUMB) && cfg->ignore_thumb ||
249                                 GETBIT(ms->touch[n].state, MT_PALM) && cfg->ignore_palm ||
250                                 GETBIT(ms->touch[n].state, MT_BOTTOM_EDGE));
251                         
252                         disable |= cfg->disable_on_thumb && GETBIT(ms->touch[n].state, MT_THUMB);
253                         disable |= cfg->disable_on_palm && GETBIT(ms->touch[n].state, MT_PALM);
254                 }
255         }
256
257         if (disable)
258                 touches_invalidate(ms);
259 }
260
261 /* Remove released touches.
262  */
263 static void touches_clean(struct MTState* ms)
264 {
265         int i, used;
266         used = ms->touch_used;
267         foreach_bit(i, used) {
268                 if (GETBIT(ms->touch[i].state, MT_RELEASED))
269                         CLEARBIT(ms->touch_used, i);
270         }
271 }
272
273 #if DEBUG_MTSTATE
274 static void mtstate_output(const struct MTState* ms,
275                         const struct HWState* hs)
276 {
277         int i, n;
278         char* type;
279         struct timeval tv;
280         n = bitcount(ms->touch_used);
281         if (bitcount(ms->touch_used) > 0) {
282                 microtime(&tv);
283                 xf86Msg(X_INFO, "mtstate: %d touches at event time %llu (rt %llu)\n",
284                         n, timertoms(&hs->evtime), timertoms(&tv));
285         }
286         foreach_bit(i, ms->touch_used) {
287                 if (GETBIT(ms->touch[i].state, MT_RELEASED)) {
288                         timersub(&hs->evtime, &ms->touch[i].down, &tv);
289                         xf86Msg(X_INFO, "  released p(%d, %d) d(%d, %d) dir(%f) down(%llu) state("PRBITMASK") time(%lld)\n",
290                                                 ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,
291                                                 ms->touch[i].direction, timertoms(&ms->touch[i].down), ms->touch[i].state, timertoms(&tv));
292                 }
293                 else if (GETBIT(ms->touch[i].state, MT_NEW)) {
294                         xf86Msg(X_INFO, "  new      p(%d, %d) d(%d, %d) dir(%f) down(%llu) state("PRBITMASK")\n",
295                                                 ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,
296                                                 ms->touch[i].direction, timertoms(&ms->touch[i].down), ms->touch[i].state);
297                 }
298                 else if (GETBIT(ms->touch[i].state, MT_INVALID)) {
299                         timersub(&hs->evtime, &ms->touch[i].down, &tv);
300                         xf86Msg(X_INFO, "  invalid  p(%d, %d) d(%d, %d) dir(%f) down(%llu) state("PRBITMASK") time(%lld)\n",
301                                                 ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,
302                                                 ms->touch[i].direction, timertoms(&ms->touch[i].down), ms->touch[i].state, timertoms(&tv));
303                 }
304                 else {
305                         xf86Msg(X_INFO, "  touching p(%d, %d) d(%d, %d) dir(%f) down(%llu) state("PRBITMASK")\n",
306                                                 ms->touch[i].x, ms->touch[i].y, ms->touch[i].dx, ms->touch[i].dy,
307                                                 ms->touch[i].direction, timertoms(&ms->touch[i].down), ms->touch[i].state);
308                 }
309         }
310 }
311 #endif
312
313 void mtstate_init(struct MTState* ms)
314 {
315         memset(ms, 0, sizeof(struct MTState));
316 }
317
318 // Process changes in touch state.
319 void mtstate_extract(struct MTState* ms,
320                         const struct MConfig* cfg,
321                         const struct HWState* hs,
322                         const struct Capabilities* caps)
323 {
324         ms->state = 0;
325
326         touches_clean(ms);
327         touches_update(ms, cfg, hs, caps);
328
329 #if DEBUG_MTSTATE
330         mtstate_output(ms, hs);
331 #endif
332 }
333