chiark / gitweb /
xf86-input-mtrack (0.3.1-1) unstable; urgency=medium
[xf86-input-mtrack.git] / src / gestures.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  * Gestures
8  * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>
9  * Copyright (C) 2010 Arturo Castro <mail@arturocastro.net>
10  * Copyright (C) 2011 Ryan Bourgeois <bluedragonx@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
25  *
26  **************************************************************************/
27
28 #include "gestures.h"
29 #include "mtouch.h"
30 #include "trig.h"
31
32 #define IS_VALID_BUTTON(x) (x >= 0 && x <= 31)
33
34 static void trigger_button_up(struct Gestures* gs, int button)
35 {
36         if (IS_VALID_BUTTON(button)) {
37                 if (button == 0 && gs->button_emulate > 0) {
38                         button = gs->button_emulate;
39                         gs->button_emulate = 0;
40                 }
41                 CLEARBIT(gs->buttons, button);
42 #ifdef DEBUG_GESTURES
43                 xf86Msg(X_INFO, "trigger_button_up: %d up\n", button);
44 #endif
45         }
46 }
47
48 static void trigger_button_down(struct Gestures* gs, int button)
49 {
50         struct timeval epoch;
51         timerclear(&epoch);
52
53         if (IS_VALID_BUTTON(button) && (button != gs->button_delayed || timercmp(&gs->button_delayed_time, &epoch, ==))) {
54                 SETBIT(gs->buttons, button);
55 #ifdef DEBUG_GESTURES
56                 xf86Msg(X_INFO, "trigger_button_down: %d down\n", button);
57 #endif
58         }
59 #ifdef DEBUG_GESTURES
60         else if (IS_VALID_BUTTON(button))
61                 xf86Msg(X_INFO, "trigger_button_down: %d down ignored, in delayed mode\n", button);
62 #endif
63 }
64
65 static void trigger_button_emulation(struct Gestures* gs, int button)
66 {
67         if (IS_VALID_BUTTON(button) && GETBIT(gs->buttons, 0)) {
68                 CLEARBIT(gs->buttons, 0);
69                 SETBIT(gs->buttons, button);
70                 gs->button_emulate = button;
71 #ifdef DEBUG_GESTURES
72                 xf86Msg(X_INFO, "trigger_button_emulation: %d emulated\n", button);
73 #endif
74         }
75 }
76
77 static void trigger_button_click(struct Gestures* gs,
78                         int button, struct timeval* trigger_up_time)
79 {
80         struct timeval epoch;
81         timerclear(&epoch);
82
83         if (IS_VALID_BUTTON(button) && timercmp(&gs->button_delayed_time, &epoch, ==)) {
84                 trigger_button_down(gs, button);
85                 gs->button_delayed = button;
86                 timercp(&gs->button_delayed_time, trigger_up_time);
87                 timerclear(&gs->button_delayed_delta);
88 #ifdef DEBUG_GESTRUES
89                 xf86Msg(X_INFO, "trigger_button_click: %d placed in delayed mode\n");
90 #endif
91         }
92 #ifdef DEBUG_GESTURES
93         else if (IS_VALID_BUTTON(button))
94                 xf86Msg(X_INFO, "trigger_button_click: %d ignored, in delayed mode\n", button);
95 #endif
96 }
97
98 static void trigger_drag_ready(struct Gestures* gs,
99                         const struct MConfig* cfg)
100 {
101         gs->move_drag = GS_DRAG_READY;
102         timeraddms(&gs->time, cfg->drag_timeout, &gs->move_drag_expire);
103 #ifdef DEBUG_GESTURES
104         xf86Msg(X_INFO, "trigger_drag_ready: drag is ready\n");
105 #endif
106 }
107
108 static int trigger_drag_start(struct Gestures* gs,
109                         const struct MConfig* cfg,
110                         int dx, int dy)
111 {
112         if (gs->move_drag == GS_DRAG_READY) {
113                 timerclear(&gs->move_drag_expire);
114                 if (cfg->drag_wait == 0) {
115                         gs->move_drag = GS_DRAG_ACTIVE;
116                         trigger_button_down(gs, 0);
117 #ifdef DEBUG_GESTURES
118                         xf86Msg(X_INFO, "trigger_drag_start: drag is active\n");
119 #endif
120                 }
121                 else {
122                         gs->move_drag = GS_DRAG_WAIT;
123                         gs->move_drag_dx = dx;
124                         gs->move_drag_dy = dy;
125                         timeraddms(&gs->time, cfg->drag_wait, &gs->move_drag_wait);
126 #ifdef DEBUG_GESTURES
127                         xf86Msg(X_INFO, "trigger_drag_start: drag in wait\n");
128 #endif
129                 }
130         }
131         else if (gs->move_drag == GS_DRAG_WAIT) {
132                 gs->move_drag_dx += dx;
133                 gs->move_drag_dy += dy;
134                 if (!timercmp(&gs->time, &gs->move_drag_wait, <)) {
135                         gs->move_drag = GS_DRAG_ACTIVE;
136                         trigger_button_down(gs, 0);
137 #ifdef DEBUG_GESTURES
138                         xf86Msg(X_INFO, "trigger_drag_start: drag is active\n");
139 #endif
140                 }
141                 else if (dist2(gs->move_drag_dx, gs->move_drag_dy) > SQRVAL(cfg->drag_dist)) {
142                         gs->move_drag = GS_NONE;
143 #ifdef DEBUG_GESTURES
144                         xf86Msg(X_INFO, "trigger_drag_start: drag canceled, moved too far\n");
145 #endif
146                 }
147         }
148         return gs->move_drag != GS_DRAG_WAIT;
149 }
150
151 static void trigger_drag_stop(struct Gestures* gs, int force)
152 {
153         if (gs->move_drag == GS_DRAG_READY && force) {
154                 gs->move_drag = GS_NONE;
155                 timerclear(&gs->move_drag_expire);
156 #ifdef DEBUG_GESTURES
157                 xf86Msg(X_INFO, "trigger_drag_stop: drag canceled\n");
158 #endif
159         }
160         else if (gs->move_drag == GS_DRAG_ACTIVE) {
161                 gs->move_drag = GS_NONE;
162                 timerclear(&gs->move_drag_expire);
163                 trigger_button_up(gs, 0);
164 #ifdef DEBUG_GESTURES
165                 xf86Msg(X_INFO, "trigger_drag_stop: drag stopped\n");
166 #endif
167         }
168 }
169
170 static void buttons_update(struct Gestures* gs,
171                         const struct MConfig* cfg,
172                         const struct HWState* hs,
173                         struct MTState* ms)
174 {
175         if (!cfg->button_enable || cfg->trackpad_disable >= 3)
176                 return;
177
178         static bitmask_t button_prev = 0U;
179         int i, down, emulate, touching;
180         down = 0;
181         emulate = GETBIT(hs->button, 0) && !GETBIT(button_prev, 0);
182
183         for (i = 0; i < 32; i++) {
184                 if (GETBIT(hs->button, i) == GETBIT(button_prev, i))
185                         continue;
186                 if (GETBIT(hs->button, i)) {
187                         down++;
188                         trigger_button_down(gs, i);
189                 }
190                 else
191                         trigger_button_up(gs, i);
192         }
193         button_prev = hs->button;
194
195         if (down) {
196                 int earliest, latest, moving = 0;
197                 gs->move_type = GS_NONE;
198                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
199                 earliest = -1;
200                 latest = -1;
201                 foreach_bit(i, ms->touch_used) {
202                         if (GETBIT(ms->touch[i].state, MT_INVALID))
203                                 continue;
204                         if (cfg->button_integrated && !GETBIT(ms->touch[i].flags, GS_BUTTON))
205                                 SETBIT(ms->touch[i].flags, GS_BUTTON);
206                         if (earliest == -1 || timercmp(&ms->touch[i].down, &ms->touch[earliest].down, <))
207                                 earliest = i;
208                         if (latest == -1 || timercmp(&ms->touch[i].down, &ms->touch[latest].down, >))
209                                 latest = i;
210                 }
211
212                 if (emulate) {
213                         if (cfg->button_zones && earliest >= 0) {
214                                 int zones, left, right, pos;
215                                 double width;
216
217                                 zones = 0;
218                                 if (cfg->button_1touch > 0)
219                                         zones++;
220                                 if (cfg->button_2touch > 0)
221                                         zones++;
222                                 if (cfg->button_3touch > 0)
223                                         zones++;
224
225                                 if (zones > 0) {
226                                         width = ((double)cfg->pad_width)/((double)zones);
227                                         pos = cfg->pad_width / 2 + ms->touch[earliest].x;
228 #ifdef DEBUG_GESTURES
229                                         xf86Msg(X_INFO, "buttons_update: pad width %d, zones %d, zone width %f, x %d\n",
230                                                 cfg->pad_width, zones, width, pos);
231 #endif
232                                         for (i = 0; i < zones; i++) {
233                                                 left = width*i;
234                                                 right = width*(i+1);
235                                                 if (pos >= left && pos <= right) {
236 #ifdef DEBUG_GESTURES
237                                                         xf86Msg(X_INFO, "buttons_update: button %d, left %d, right %d (found)\n", i, left, right);
238 #endif
239                                                         break;
240                                                 }
241 #ifdef DEBUG_GESTURES
242                                                 else
243                                                         xf86Msg(X_INFO, "buttons_update: button %d, left %d, right %d\n", i, left, right);
244 #endif
245                                         }
246
247                                         if (i == 0)
248                                                 trigger_button_emulation(gs, cfg->button_1touch - 1);
249                                         else if (i == 1)
250                                                 trigger_button_emulation(gs, cfg->button_2touch - 1);
251                                         else
252                                                 trigger_button_emulation(gs, cfg->button_3touch - 1);
253                                 }
254                         }
255                         else if (latest >= 0) {
256                                 touching = 0;
257                                 struct timeval expire;
258                                 foreach_bit(i, ms->touch_used) {
259                                         timeraddms(&ms->touch[i].down, cfg->button_expire, &expire);
260                                         if (cfg->button_move || cfg->button_expire == 0 || timercmp(&ms->touch[latest].down, &expire, <))
261                                                 touching++;
262                                 }
263
264                                 if (cfg->button_integrated)
265                                         touching--;
266
267                                 if (touching == 1 && cfg->button_1touch > 0)
268                                         trigger_button_emulation(gs, cfg->button_1touch - 1);
269                                 else if (touching == 2 && cfg->button_2touch > 0)
270                                         trigger_button_emulation(gs, cfg->button_2touch - 1);
271                                 else if (touching == 3 && cfg->button_3touch > 0)
272                                         trigger_button_emulation(gs, cfg->button_3touch - 1);
273                         }
274                 }
275         }
276 }
277
278 static void tapping_update(struct Gestures* gs,
279                         const struct MConfig* cfg,
280                         struct MTState* ms)
281 {
282         int i, n, dist, released_max;
283         struct timeval tv_tmp;
284         struct timeval epoch;
285
286         if (cfg->trackpad_disable >= 1)
287                 return;
288
289         if (cfg->tap_4touch > 0)
290                 released_max = 4;
291         else if (cfg->tap_3touch > 0)
292                 released_max = 3;
293         else if (cfg->tap_2touch > 0)
294                 released_max = 2;
295         else if (cfg->tap_1touch > 0)
296                 released_max = 1;
297         else
298                 return;
299
300         timerclear(&epoch);
301         timeraddms(&gs->tap_time_down, cfg->tap_timeout, &tv_tmp);
302         if (!timercmp(&gs->tap_time_down, &epoch, ==) && !timercmp(&gs->time, &tv_tmp, <)) {
303                 gs->tap_touching = 0;
304                 gs->tap_released = 0;
305                 timerclear(&gs->tap_time_down);
306
307                 foreach_bit(i, ms->touch_used) {
308                         if (GETBIT(ms->touch[i].flags, GS_TAP))
309                                 CLEARBIT(ms->touch[i].flags, GS_TAP);
310                 }
311         }
312         else {
313                 foreach_bit(i, ms->touch_used) {
314                         if (GETBIT(ms->touch[i].state, MT_INVALID) || GETBIT(ms->touch[i].flags, GS_BUTTON)) {
315                                 if (GETBIT(ms->touch[i].flags, GS_TAP)) {
316                                         CLEARBIT(ms->touch[i].flags, GS_TAP);
317                                         gs->tap_touching--;
318 #ifdef DEBUG_GESTURES
319                                         xf86Msg(X_INFO, "tapping_update: tap_touching-- (%d): invalid or button\n", gs->tap_touching);
320 #endif
321                                 }
322                         }
323                         else {
324                                 if (GETBIT(ms->touch[i].state, MT_NEW)) {
325                                         SETBIT(ms->touch[i].flags, GS_TAP);
326                                         gs->tap_touching++;
327 #ifdef DEBUG_GESTURES
328                                         xf86Msg(X_INFO, "tapping_update: tap_touching++ (%d): new touch\n", gs->tap_touching);
329 #endif
330                                         timerclear(&tv_tmp);
331                                         if (timercmp(&gs->tap_time_down, &epoch, ==))
332                                                 timercp(&gs->tap_time_down, &gs->time);
333                                 }
334
335                                 if (GETBIT(ms->touch[i].flags, GS_TAP)) {
336                                         dist = dist2(ms->touch[i].total_dx, ms->touch[i].total_dy);
337                                         if (dist >= SQRVAL(cfg->tap_dist)) {
338                                                 CLEARBIT(ms->touch[i].flags, GS_TAP);
339                                                 gs->tap_touching--;
340 #ifdef DEBUG_GESTURES
341                                         xf86Msg(X_INFO, "tapping_update: tap_touching-- (%d): moved too far\n", gs->tap_touching);
342 #endif
343                                         }
344                                         else if (GETBIT(ms->touch[i].state, MT_RELEASED)) {
345                                                 gs->tap_touching--;
346                                                 gs->tap_released++;
347 #ifdef DEBUG_GESTURES
348                                         xf86Msg(X_INFO, "tapping_update: tap_touching-- (%d): released\n", gs->tap_touching);
349                                         xf86Msg(X_INFO, "tapping_update: tap_released++ (%d) (max %d): released\n", gs->tap_released, released_max);
350 #endif
351                                         }
352                                 }
353                         }
354                 }
355         }
356
357         if ((gs->tap_touching == 0 && gs->tap_released > 0) || gs->tap_released >= released_max) {
358                 foreach_bit(i, ms->touch_used) {
359                         if (GETBIT(ms->touch[i].flags, GS_TAP))
360                                 CLEARBIT(ms->touch[i].flags, GS_TAP);
361                 }
362
363                 if (gs->tap_released == 1)
364                         n = cfg->tap_1touch - 1;
365                 else if (gs->tap_released == 2)
366                         n = cfg->tap_2touch - 1;
367                 else if (gs->tap_released == 3)
368                         n = cfg->tap_3touch - 1;
369                 else
370                         n = cfg->tap_4touch - 1;
371
372                 trigger_button_click(gs, n, &tv_tmp);
373                 if (cfg->drag_enable && n == 0)
374                         trigger_drag_ready(gs, cfg);
375
376                 gs->move_type = GS_NONE;
377                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
378
379                 gs->tap_touching = 0;
380                 gs->tap_released = 0;
381                 timerclear(&gs->tap_time_down);
382         }
383 }
384
385 static void trigger_move(struct Gestures* gs,
386                         const struct MConfig* cfg,
387                         int dx, int dy)
388 {
389         if ((gs->move_type == GS_MOVE || !timercmp(&gs->time, &gs->move_wait, <)) && (dx != 0 || dy != 0)) {
390                 if (trigger_drag_start(gs, cfg, dx, dy)) {
391                         gs->move_dx = (int)(dx*cfg->sensitivity);
392                         gs->move_dy = (int)(dy*cfg->sensitivity);
393                         gs->move_type = GS_MOVE;
394                         gs->move_dist = 0;
395                         gs->move_dir = TR_NONE;
396                         gs->move_speed = hypot(gs->move_dx, gs->move_dy)/timertomicro(&gs->dt);
397                         timerclear(&gs->move_wait);
398 #ifdef DEBUG_GESTURES
399                         xf86Msg(X_INFO, "trigger_move: %d, %d (speed %f)\n",
400                                 gs->move_dx, gs->move_dy, gs->move_speed);
401 #endif
402                 }
403         }
404 }
405
406 static void trigger_scroll(struct Gestures* gs,
407                         const struct MConfig* cfg,
408                         double dist, int dir)
409 {
410         if (gs->move_type == GS_SCROLL || !timercmp(&gs->time, &gs->move_wait, <)) {
411                 struct timeval tv_tmp;
412                 trigger_drag_stop(gs, 1);
413                 if (gs->move_type != GS_SCROLL || gs->move_dir != dir)
414                         gs->move_dist = 0;
415                 gs->move_dx = 0;
416                 gs->move_dy = 0;
417                 gs->move_type = GS_SCROLL;
418                 gs->move_dist += (int)ABSVAL(dist);
419                 gs->move_dir = dir;
420                 gs->move_speed = dist/timertomicro(&gs->dt);
421                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
422
423                 if (gs->move_dist >= cfg->scroll_dist) {
424                         gs->move_dist = MODVAL(gs->move_dist, cfg->scroll_dist);
425                         timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);
426                         if (dir == TR_DIR_UP)
427                                 trigger_button_click(gs, cfg->scroll_up_btn - 1, &tv_tmp);
428                         else if (dir == TR_DIR_DN)
429                                 trigger_button_click(gs, cfg->scroll_dn_btn - 1, &tv_tmp);
430                         else if (dir == TR_DIR_LT)
431                                 trigger_button_click(gs, cfg->scroll_lt_btn - 1, &tv_tmp);
432                         else if (dir == TR_DIR_RT)
433                                 trigger_button_click(gs, cfg->scroll_rt_btn - 1, &tv_tmp);
434                 }
435 #ifdef DEBUG_GESTURES
436                 xf86Msg(X_INFO, "trigger_scroll: scrolling %+f in direction %d (at %d of %d) (speed %f)\n",
437                         dist, dir, gs->move_dist, cfg->scroll_dist, gs->move_speed);
438 #endif
439         }
440 }
441
442 static void trigger_swipe(struct Gestures* gs,
443                         const struct MConfig* cfg,
444                         double dist, int dir, int isfour)
445 {
446         if (gs->move_type == GS_SWIPE || !timercmp(&gs->time, &gs->move_wait, <)) {
447                 struct timeval tv_tmp;
448                 trigger_drag_stop(gs, 1);
449                 if (gs->move_type != GS_SWIPE || gs->move_dir != dir)
450                         gs->move_dist = 0;
451                 gs->move_dx = 0;
452                 gs->move_dy = 0;
453                 gs->move_type = GS_SWIPE;
454                 gs->move_dist += (int)ABSVAL(dist);
455                 gs->move_dir = dir;
456                 gs->move_speed = dist/timertomicro(&gs->dt);
457                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
458                 timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);
459
460                 if (isfour) {
461                         if (cfg->swipe4_dist > 0 && gs->move_dist >= cfg->swipe4_dist) {
462                                 gs->move_dist = MODVAL(gs->move_dist, cfg->swipe4_dist);
463                                 if (dir == TR_DIR_UP)
464                                         trigger_button_click(gs, cfg->swipe4_up_btn - 1, &tv_tmp);
465                                 else if (dir == TR_DIR_DN)
466                                         trigger_button_click(gs, cfg->swipe4_dn_btn - 1, &tv_tmp);
467                                 else if (dir == TR_DIR_LT)
468                                         trigger_button_click(gs, cfg->swipe4_lt_btn - 1, &tv_tmp);
469                                 else if (dir == TR_DIR_RT)
470                                         trigger_button_click(gs, cfg->swipe4_rt_btn - 1, &tv_tmp);
471                         }
472 #ifdef DEBUG_GESTURES
473                         xf86Msg(X_INFO, "trigger_swipe4: swiping %+f in direction %d (at %d of %d) (speed %f)\n",
474                                 dist, dir, gs->move_dist, cfg->swipe_dist, gs->move_speed);
475 #endif
476                 }
477                 else {
478                         if (cfg->swipe_dist > 0 && gs->move_dist >= cfg->swipe_dist) {
479                                 gs->move_dist = MODVAL(gs->move_dist, cfg->swipe_dist);
480                                 if (dir == TR_DIR_UP)
481                                         trigger_button_click(gs, cfg->swipe_up_btn - 1, &tv_tmp);
482                                 else if (dir == TR_DIR_DN)
483                                         trigger_button_click(gs, cfg->swipe_dn_btn - 1, &tv_tmp);
484                                 else if (dir == TR_DIR_LT)
485                                         trigger_button_click(gs, cfg->swipe_lt_btn - 1, &tv_tmp);
486                                 else if (dir == TR_DIR_RT)
487                                         trigger_button_click(gs, cfg->swipe_rt_btn - 1, &tv_tmp);
488                         }
489 #ifdef DEBUG_GESTURES
490                         xf86Msg(X_INFO, "trigger_swipe: swiping %+f in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->swipe_dist);
491 #endif
492                 }
493         }
494 }
495
496 static void trigger_scale(struct Gestures* gs,
497                         const struct MConfig* cfg,
498                         double dist, int dir)
499 {
500         if (gs->move_type == GS_SCALE || !timercmp(&gs->time, &gs->move_wait, <)) {
501                 struct timeval tv_tmp;
502                 trigger_drag_stop(gs, 1);
503                 if (gs->move_type != GS_SCALE || gs->move_dir != dir)
504                         gs->move_dist = 0;
505                 gs->move_dx = 0;
506                 gs->move_dy = 0;
507                 gs->move_type = GS_SCALE;
508                 gs->move_dist += (int)ABSVAL(dist);
509                 gs->move_dir = dir;
510                 gs->move_speed = dist/timertomicro(&gs->dt);
511                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
512                 if (gs->move_dist >= cfg->scale_dist) {
513                         gs->move_dist = MODVAL(gs->move_dist, cfg->scale_dist);
514                         timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);
515                         if (dir == TR_DIR_UP)
516                                 trigger_button_click(gs, cfg->scale_up_btn - 1, &tv_tmp);
517                         else if (dir == TR_DIR_DN)
518                                 trigger_button_click(gs, cfg->scale_dn_btn - 1, &tv_tmp);
519                 }
520 #ifdef DEBUG_GESTURES
521                 xf86Msg(X_INFO, "trigger_scale: scaling %+f in direction %d (at %d of %d) (speed %f)\n",
522                         dist, dir, gs->move_dist, cfg->scale_dist, gs->move_speed);
523 #endif
524         }
525 }
526
527 static void trigger_rotate(struct Gestures* gs,
528                         const struct MConfig* cfg,
529                         double dist, int dir)
530 {
531         if (gs->move_type == GS_ROTATE || !timercmp(&gs->time, &gs->move_wait, <)) {
532                 struct timeval tv_tmp;
533                 trigger_drag_stop(gs, 1);
534                 if (gs->move_type != GS_ROTATE || gs->move_dir != dir)
535                         gs->move_dist = 0;
536                 gs->move_dx = 0;
537                 gs->move_dy = 0;
538                 gs->move_type = GS_ROTATE;
539                 gs->move_dist += (int)ABSVAL(dist);
540                 gs->move_dir = dir;
541                 gs->move_speed = dist/timertomicro(&gs->dt);
542                 timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait);
543                 if (gs->move_dist >= cfg->rotate_dist) {
544                         gs->move_dist = MODVAL(gs->move_dist, cfg->rotate_dist);
545                         timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp);
546                         if (dir == TR_DIR_LT)
547                                 trigger_button_click(gs, cfg->rotate_lt_btn - 1, &tv_tmp);
548                         else if (dir == TR_DIR_RT)
549                                 trigger_button_click(gs, cfg->rotate_rt_btn - 1, &tv_tmp);
550                 }
551 #ifdef DEBUG_GESTURES
552                 xf86Msg(X_INFO, "trigger_rotate: rotating %+f in direction %d (at %d of %d) (speed %f)\n",
553                         dist, dir, gs->move_dist, cfg->rotate_dist, gs->move_speed);
554 #endif
555         }
556 }
557
558 static void trigger_reset(struct Gestures* gs)
559 {
560         trigger_drag_stop(gs, 0);
561         gs->move_dx = 0;
562         gs->move_dy = 0;
563         gs->move_type = GS_NONE;
564         gs->move_dist = 0;
565         gs->move_dir = TR_NONE;
566         timerclear(&gs->move_wait);
567 }
568
569 static int get_scroll_dir(const struct Touch* t1,
570                         const struct Touch* t2)
571 {
572         if (trig_angles_acute(t1->direction, t2->direction) < 2.0) {
573                 double angles[2];
574                 angles[0] = t1->direction;
575                 angles[1] = t2->direction;
576                 return trig_generalize(trig_angles_avg(angles, 2));
577         }
578         return TR_NONE;
579 }
580
581 static int get_rotate_dir(const struct Touch* t1,
582                         const struct Touch* t2)
583 {
584         double v, d1, d2;
585         v = trig_direction(t2->x - t1->x, t2->y - t1->y);
586         d1 = trig_angles_add(v, 2);
587         d2 = trig_angles_sub(v, 2);
588         if (trig_angles_acute(t1->direction, d1) < 2 && trig_angles_acute(t2->direction, d2) < 2)
589                 return TR_DIR_RT;
590         else if (trig_angles_acute(t1->direction, d2) < 2 && trig_angles_acute(t2->direction, d1) < 2)
591                 return TR_DIR_LT;
592         return TR_NONE;
593 }
594
595 static int get_scale_dir(const struct Touch* t1,
596                         const struct Touch* t2)
597 {
598         double v;
599         if (trig_angles_acute(t1->direction, t2->direction) >= 2) {
600                 v = trig_direction(t2->x - t1->x, t2->y - t1->y);
601                 if (trig_angles_acute(v, t1->direction) < 2)
602                         return TR_DIR_DN;
603                 else
604                         return TR_DIR_UP;
605         }
606         return TR_NONE;
607 }
608
609 static int get_swipe_dir(const struct Touch* t1,
610                         const struct Touch* t2,
611                         const struct Touch* t3)
612 {
613         double angles[3];
614         angles[0] = t1->direction;
615         angles[1] = t2->direction;
616         angles[2] = t3->direction;
617         return trig_generalize(trig_angles_avg(angles, 3));
618 }
619
620 static int get_swipe4_dir(const struct Touch* t1,
621                         const struct Touch* t2,
622                         const struct Touch* t3,
623                         const struct Touch* t4)
624 {
625         double angles[4];
626         angles[0] = t1->direction;
627         angles[1] = t2->direction;
628         angles[2] = t3->direction;
629         angles[3] = t4->direction;
630         return trig_generalize(trig_angles_avg(angles, 4));
631 }
632
633 static void moving_update(struct Gestures* gs,
634                         const struct MConfig* cfg,
635                         struct MTState* ms)
636 {
637         int i, count, btn_count, dx, dy, dir;
638         double dist;
639         struct Touch* touches[4];
640         count = btn_count = 0;
641         dx = dy = 0;
642         dir = 0;
643
644         // Reset movement.
645         gs->move_dx = 0;
646         gs->move_dy = 0;
647
648         // Count touches and aggregate touch movements.
649         foreach_bit(i, ms->touch_used) {
650                 if (GETBIT(ms->touch[i].state, MT_INVALID))
651                         continue;
652                 else if (GETBIT(ms->touch[i].flags, GS_BUTTON)) {
653                         btn_count++;
654                         dx += ms->touch[i].dx;
655                         dy += ms->touch[i].dy;
656                 }
657                 else if (!GETBIT(ms->touch[i].flags, GS_TAP)) {
658                         if (count < 4)
659                                 touches[count++] = &ms->touch[i];
660                 }
661         }
662
663         // Determine gesture type.
664         if (count == 0) {
665                 if (btn_count >= 1 && cfg->trackpad_disable < 2)
666                         trigger_move(gs, cfg, dx, dy);
667                 else if (btn_count < 1)
668                         trigger_reset(gs);
669         }
670         else if (count == 1 && cfg->trackpad_disable < 2) {
671                 dx += touches[0]->dx;
672                 dy += touches[0]->dy;
673                 trigger_move(gs, cfg, dx, dy);
674         }
675         else if (count == 2 && cfg->trackpad_disable < 1) {
676                 // scroll, scale, or rotate
677                 if ((dir = get_scroll_dir(touches[0], touches[1])) != TR_NONE) {
678                         dist = hypot(
679                                 touches[0]->dx + touches[1]->dx,
680                                 touches[0]->dy + touches[1]->dy);
681                         trigger_scroll(gs, cfg, dist/2, dir);
682                 }
683                 else if ((dir = get_rotate_dir(touches[0], touches[1])) != TR_NONE) {
684                         dist = ABSVAL(hypot(touches[0]->dx, touches[0]->dy)) +
685                                 ABSVAL(hypot(touches[1]->dx, touches[1]->dy));
686                         trigger_rotate(gs, cfg, dist/2, dir);
687                 }
688                 else if ((dir = get_scale_dir(touches[0], touches[1])) != TR_NONE) {
689                         dist = ABSVAL(hypot(touches[0]->dx, touches[0]->dy)) +
690                                 ABSVAL(hypot(touches[1]->dx, touches[1]->dy));
691                         trigger_scale(gs, cfg, dist/2, dir);
692                 }
693         }
694         else if (count == 3 && cfg->trackpad_disable < 1) {
695                 if ((dir = get_swipe_dir(touches[0], touches[1], touches[2])) != TR_NONE) {
696                         dist = hypot(
697                                 touches[0]->dx + touches[1]->dx + touches[2]->dx,
698                                 touches[0]->dy + touches[1]->dy + touches[2]->dy);
699                         trigger_swipe(gs, cfg, dist/3, dir, 0);
700                 }
701         }
702         else if (count == 4 && cfg->trackpad_disable < 1) {
703                 if ((dir = get_swipe4_dir(touches[0], touches[1], touches[2], touches[3])) != TR_NONE) {
704                         dist = hypot(
705                                 touches[0]->dx + touches[1]->dx + touches[2]->dx + touches[3]->dx,
706                                 touches[0]->dy + touches[1]->dy + touches[2]->dy + touches[3]->dy);
707                         trigger_swipe(gs, cfg, dist/4, dir, 1);
708                 }
709         }
710 }
711
712 static void dragging_update(struct Gestures* gs)
713 {
714         if (gs->move_drag == GS_DRAG_READY && timercmp(&gs->time, &gs->move_drag_expire, >)) {
715 #ifdef DEBUG_GESTURES
716                 xf86Msg(X_INFO, "dragging_update: drag expired\n");
717 #endif
718                 trigger_drag_stop(gs, 1);
719         }
720 }
721
722 static void delayed_update(struct Gestures* gs)
723 {
724         struct timeval epoch;
725         timerclear(&epoch);
726
727         if (timercmp(&gs->button_delayed_time, &epoch, ==))
728                 return;
729
730         if (!timercmp(&gs->time, &gs->button_delayed_time, <)) {
731 #ifdef DEBUG_GESTURES
732                 xf86Msg(X_INFO, "delayed_update: %d delay expired, triggering up\n", gs->button_delayed);
733 #endif
734                 trigger_button_up(gs, gs->button_delayed);
735                 gs->button_delayed = 0;
736                 timerclear(&gs->button_delayed_time);
737                 timerclear(&gs->button_delayed_delta);
738         }
739         else {
740                 timersub(&gs->button_delayed_time, &gs->time, &gs->button_delayed_delta);
741         }
742 }
743
744 void gestures_init(struct MTouch* mt)
745 {
746         memset(&mt->gs, 0, sizeof(struct Gestures));
747 }
748
749 void gestures_extract(struct MTouch* mt)
750 {
751         timersub(&mt->hs.evtime, &mt->gs.time, &mt->gs.dt);
752         timercp(&mt->gs.time, &mt->hs.evtime);
753
754         dragging_update(&mt->gs);
755         buttons_update(&mt->gs, &mt->cfg, &mt->hs, &mt->state);
756         tapping_update(&mt->gs, &mt->cfg, &mt->state);
757         moving_update(&mt->gs, &mt->cfg, &mt->state);
758         delayed_update(&mt->gs);
759 }
760
761 static int gestures_sleep(struct MTouch* mt, const struct timeval* sleep)
762 {
763         if (mtdev_empty(&mt->dev)) {
764                 struct timeval now;
765                 mtdev_idle(&mt->dev, mt->fd, timertoms(sleep));
766                 microtime(&now);
767                 timersub(&now, &mt->gs.time, &mt->gs.dt);
768                 timercp(&mt->gs.time, &now);
769                 return 1;
770         }
771         return 0;
772 }
773
774 int gestures_delayed(struct MTouch* mt)
775 {
776         struct Gestures* gs = &mt->gs;
777         struct timeval epoch;
778         timerclear(&epoch);
779
780         if (timercmp(&gs->button_delayed_time, &epoch, >)) {
781                 if (gestures_sleep(mt, &gs->button_delayed_delta)) {
782 #ifdef DEBUG_GESTURES
783                         xf86Msg(X_INFO, "gestures_delayed: %d up, timer expired\n", gs->button_delayed);
784 #endif
785                         trigger_button_up(gs, gs->button_delayed);
786                         gs->move_dx = 0;
787                         gs->move_dy = 0;
788                         gs->button_delayed = 0;
789                         timerclear(&gs->button_delayed_time);
790                         timerclear(&gs->button_delayed_delta);
791                         return 1;
792                 }
793         }
794         return 0;
795 }
796