summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/gebuf.h3
-rw-r--r--src/gestures-combo.c82
-rw-r--r--src/gestures-rotate.c71
-rw-r--r--src/gestures-scroll.c74
-rw-r--r--src/gestures-zoom.c71
-rw-r--r--src/grail-api.c22
-rw-r--r--src/grail-bits.c94
-rw-r--r--src/grail-gestures.c150
-rw-r--r--src/grail-gestures.h85
-rw-r--r--src/grail-inserter.c248
-rw-r--r--src/grail-inserter.h46
-rw-r--r--src/grail-recognizer.c76
-rw-r--r--src/grail-recognizer.h13
-rw-r--r--src/mtdev-details.h19
-rw-r--r--src/mtdev-plumbing.h105
-rw-r--r--src/touch-dev.c45
-rw-r--r--src/touch-engine.c42
18 files changed, 935 insertions, 318 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 5835897..d0a7d26 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,7 +7,13 @@ libgrail_la_LDFLAGS = \
7libgrail_la_SOURCES = \ 7libgrail_la_SOURCES = \
8 touch-dev.c \ 8 touch-dev.c \
9 touch-engine.c \ 9 touch-engine.c \
10 grail-bits.c \
10 grail-inserter.c \ 11 grail-inserter.c \
12 grail-gestures.c \
13 gestures-scroll.c \
14 gestures-zoom.c \
15 gestures-rotate.c \
16 gestures-combo.c \
11 grail-recognizer.c \ 17 grail-recognizer.c \
12 grail-api.c 18 grail-api.c
13 19
@@ -17,5 +23,6 @@ INCLUDES = -I$(top_srcdir)/include/
17 23
18libgrailincludedir = $(includedir) 24libgrailincludedir = $(includedir)
19libgrailinclude_HEADERS = \ 25libgrailinclude_HEADERS = \
26 $(top_srcdir)/include/grail-bits.h \
20 $(top_srcdir)/include/grail-touch.h \ 27 $(top_srcdir)/include/grail-touch.h \
21 $(top_srcdir)/include/grail.h 28 $(top_srcdir)/include/grail.h
diff --git a/src/gebuf.h b/src/gebuf.h
index 37e4a5c..3f71ab5 100644
--- a/src/gebuf.h
+++ b/src/gebuf.h
@@ -30,7 +30,10 @@
30#define DIM_GESTURE_EVENTS 512 30#define DIM_GESTURE_EVENTS 512
31 31
32struct gesture_event { 32struct gesture_event {
33 int ntouch;
34 int nprop;
33 touch_time_t time; 35 touch_time_t time;
36 struct grail_coord pos;
34 grail_prop_t prop[DIM_GRAIL_PROP]; 37 grail_prop_t prop[DIM_GRAIL_PROP];
35}; 38};
36 39
diff --git a/src/gestures-combo.c b/src/gestures-combo.c
new file mode 100644
index 0000000..4ecd1e4
--- /dev/null
+++ b/src/gestures-combo.c
@@ -0,0 +1,82 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-recognizer.h"
26#include <math.h>
27#include <stdio.h>
28
29void gru_reset_combo(struct grail *ge,
30 const struct touch_frame *frame)
31{
32 struct gesture_inserter *gin = ge->gin;
33 struct gesture_recognizer *gru = ge->gru;
34 struct combo_model *state = &gru->combo;
35 struct move_model *move = &gru->move;
36 gin_gesture_discard_type(gin, GRAIL_TYPE_SZRCOMBO);
37 state->r = move->r;
38 state->active = 0;
39 state->running = 0;
40}
41
42int gru_combo(struct grail *ge,
43 const struct touch_frame *frame)
44{
45 struct gesture_inserter *gin = ge->gin;
46 struct gesture_recognizer *gru = ge->gru;
47 struct combo_model *state = &gru->combo;
48 struct move_model *move = &gru->move;
49 grail_prop_t prop[DIM_GRAIL_PROP];
50 float dx, dy, dr, da;
51 int i;
52 dx = move->x - state->x;
53 dy = move->y - state->y;
54 dr = move->r - state->r;
55 da = move->a - state->a;
56 if (fabs(dx) < 1 && fabs(dy) < 1 && fabs(da) < 1 && fabs(dr) < 1)
57 return 0;
58 if (!state->active) {
59 i = gin_gesture_begin(ge,
60 GRAIL_TYPE_SZRCOMBO, 2,
61 frame->touches, sizeof(frame->touches));
62 state->geslot = i;
63 state->active = 1;
64 }
65 if (!state->running &&
66 fabs(dx) < move->xbar && fabs(dy) < move->ybar &&
67 fabs(da) < move->abar && fabs(dr) < move->rbar)
68 return 0;
69 state->running = 1;
70 prop[0] = dx;
71 prop[1] = dy;
72 prop[2] = dr;
73 prop[3] = da;
74 gin_gesture_event(gin, state->geslot,
75 move->x, move->y, move->ntouch,
76 prop, 4);
77 state->x = move->x;
78 state->y = move->y;
79 state->r = move->r;
80 state->a = move->a;
81 return 1;
82}
diff --git a/src/gestures-rotate.c b/src/gestures-rotate.c
new file mode 100644
index 0000000..6097347
--- /dev/null
+++ b/src/gestures-rotate.c
@@ -0,0 +1,71 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-recognizer.h"
26#include <math.h>
27#include <stdio.h>
28
29void gru_reset_rotate(struct grail *ge,
30 const struct touch_frame *frame)
31{
32 struct gesture_inserter *gin = ge->gin;
33 struct gesture_recognizer *gru = ge->gru;
34 struct rotate_model *state = &gru->rotate;
35 struct move_model *move = &gru->move;
36 gin_gesture_discard_type(gin, GRAIL_TYPE_ROTATE);
37 state->a = move->a;
38 state->active = 0;
39 state->rotating = 0;
40}
41
42int gru_rotate(struct grail *ge,
43 const struct touch_frame *frame)
44{
45 struct gesture_inserter *gin = ge->gin;
46 struct gesture_recognizer *gru = ge->gru;
47 struct rotate_model *state = &gru->rotate;
48 struct move_model *move = &gru->move;
49 grail_prop_t prop[DIM_GRAIL_PROP];
50 float da;
51 int i;
52 da = move->a - state->a;
53 if (fabs(da) < 1)
54 return 0;
55 if (!state->active) {
56 i = gin_gesture_begin(ge,
57 GRAIL_TYPE_ROTATE, 1,
58 frame->touches, sizeof(frame->touches));
59 state->geslot = i;
60 state->active = 1;
61 }
62 if (!state->rotating && fabs(da) < move->abar)
63 return 0;
64 state->rotating = 1;
65 prop[0] = da;
66 gin_gesture_event(gin, state->geslot,
67 move->x, move->y, move->ntouch,
68 prop, 1);
69 state->a = move->a;
70 return 1;
71}
diff --git a/src/gestures-scroll.c b/src/gestures-scroll.c
new file mode 100644
index 0000000..96e153b
--- /dev/null
+++ b/src/gestures-scroll.c
@@ -0,0 +1,74 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-recognizer.h"
26#include <math.h>
27
28void gru_reset_scroll(struct grail *ge,
29 const struct touch_frame *frame)
30{
31 struct gesture_inserter *gin = ge->gin;
32 struct gesture_recognizer *gru = ge->gru;
33 struct scroll_model *state = &gru->scroll;
34 struct move_model *move = &gru->move;
35 gin_gesture_discard_type(gin, GRAIL_TYPE_SCROLL);
36 state->x = move->x;
37 state->y = move->y;
38 state->active = 0;
39 state->moving = 0;
40}
41
42int gru_scroll(struct grail *ge,
43 const struct touch_frame *frame)
44{
45 struct gesture_inserter *gin = ge->gin;
46 struct gesture_recognizer *gru = ge->gru;
47 struct scroll_model *state = &gru->scroll;
48 struct move_model *move = &gru->move;
49 grail_prop_t prop[DIM_GRAIL_PROP];
50 float dx, dy;
51 int i;
52 dx = move->x - state->x;
53 dy = move->y - state->y;
54 if (fabs(dx) < 1 && fabs(dy) < 1)
55 return 0;
56 if (!state->active) {
57 i = gin_gesture_begin(ge, GRAIL_TYPE_SCROLL, 1,
58 frame->touches, sizeof(frame->touches));
59 state->geslot = i;
60 state->active = 1;
61 }
62 if (!state->moving && fabs(dx) < move->xbar && fabs(dy) < move->ybar)
63 return 0;
64 state->moving = 1;
65 prop[0] = dx;
66 prop[1] = dy;
67 gin_gesture_event(gin, state->geslot,
68 move->x, move->y, move->ntouch,
69 prop, 2);
70 state->x = move->x;
71 state->y = move->y;
72 return 1;
73}
74
diff --git a/src/gestures-zoom.c b/src/gestures-zoom.c
new file mode 100644
index 0000000..9e05b8b
--- /dev/null
+++ b/src/gestures-zoom.c
@@ -0,0 +1,71 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-recognizer.h"
26#include <math.h>
27#include <stdio.h>
28
29void gru_reset_zoom(struct grail *ge,
30 const struct touch_frame *frame)
31{
32 struct gesture_inserter *gin = ge->gin;
33 struct gesture_recognizer *gru = ge->gru;
34 struct zoom_model *state = &gru->zoom;
35 struct move_model *move = &gru->move;
36 gin_gesture_discard_type(gin, GRAIL_TYPE_ZOOM);
37 state->r = move->r;
38 state->active = 0;
39 state->zooming = 0;
40}
41
42int gru_zoom(struct grail *ge,
43 const struct touch_frame *frame)
44{
45 struct gesture_inserter *gin = ge->gin;
46 struct gesture_recognizer *gru = ge->gru;
47 struct zoom_model *state = &gru->zoom;
48 struct move_model *move = &gru->move;
49 grail_prop_t prop[DIM_GRAIL_PROP];
50 float dr;
51 int i;
52 dr = move->r - state->r;
53 if (fabs(dr) < 1)
54 return 0;
55 if (!state->active) {
56 i = gin_gesture_begin(ge,
57 GRAIL_TYPE_ZOOM, 1,
58 frame->touches, sizeof(frame->touches));
59 state->geslot = i;
60 state->active = 1;
61 }
62 if (!state->zooming && fabs(dr) < move->rbar)
63 return 0;
64 state->zooming = 1;
65 prop[0] = dr;
66 gin_gesture_event(gin, state->geslot,
67 move->x, move->y, move->ntouch,
68 prop, 1);
69 state->r = move->r;
70 return 1;
71}
diff --git a/src/grail-api.c b/src/grail-api.c
index e3b8125..cbd2da4 100644
--- a/src/grail-api.c
+++ b/src/grail-api.c
@@ -70,16 +70,16 @@ static void tp_sync(struct touch_engine *engine,
70 struct touch_frame *frame = &engine->frame; 70 struct touch_frame *frame = &engine->frame;
71 grail_mask_t filtered[DIM_EV_TYPE_BYTES]; 71 grail_mask_t filtered[DIM_EV_TYPE_BYTES];
72 int nevent = 0; 72 int nevent = 0;
73 gin_frame_begin(ge, frame->time); 73 gin_frame_begin(ge, frame);
74 gru_recognize(ge, frame); 74 gru_recognize(ge, frame);
75 gin_frame_end(ge, filtered); 75 gin_frame_end(ge, filtered, sizeof(filtered), frame);
76 if (!ge->event) { 76 if (!ge->event) {
77 evbuf_clear(&x->evbuf); 77 evbuf_clear(&x->evbuf);
78 return; 78 return;
79 } 79 }
80 while (!evbuf_empty(&x->evbuf)) { 80 while (!evbuf_empty(&x->evbuf)) {
81 evbuf_get(&x->evbuf, &ev); 81 evbuf_get(&x->evbuf, &ev);
82 if (grail_get_mask(filtered, ev.type)) 82 if (grail_mask_get(filtered, ev.type))
83 continue; 83 continue;
84 ge->event(ge, &ev); 84 ge->event(ge, &ev);
85 nevent++; 85 nevent++;
@@ -98,27 +98,27 @@ int grail_open(struct grail *ge, int fd)
98 ret = gin_init(ge); 98 ret = gin_init(ge);
99 if (ret) 99 if (ret)
100 goto freemem; 100 goto freemem;
101 ret = gru_init(ge);
102 if (ret)
103 goto freedev;
104 ret = touch_dev_open(&x->dev, fd); 101 ret = touch_dev_open(&x->dev, fd);
105 if (ret) 102 if (ret)
106 goto freegru; 103 goto freegin;
104 ret = gru_init(ge, &x->dev.caps);
105 if (ret)
106 goto freedev;
107 x->fd = fd; 107 x->fd = fd;
108 x->mutex = c_mutex; 108 x->mutex = c_mutex;
109 x->wait = c_wait; 109 x->wait = c_wait;
110 ret = pthread_create(&x->thread, NULL, grail_thread, x); 110 ret = pthread_create(&x->thread, NULL, grail_thread, x);
111 if (ret) 111 if (ret)
112 goto freetouch; 112 goto freegru;
113 ge->impl = x; 113 ge->impl = x;
114 touch_engine_init(&x->engine, tp_event, tp_sync, ge); 114 touch_engine_init(&x->engine, tp_event, tp_sync, ge);
115 touch_engine_attach(&x->engine, &x->dev); 115 touch_engine_attach(&x->engine, &x->dev);
116 return 0; 116 return 0;
117 freetouch:
118 touch_dev_close(&x->dev, fd);
119 freegru: 117 freegru:
120 gru_destroy(ge); 118 gru_destroy(ge);
121 freedev: 119 freedev:
120 touch_dev_close(&x->dev, fd);
121 freegin:
122 gin_destroy(ge); 122 gin_destroy(ge);
123 freemem: 123 freemem:
124 free(x); 124 free(x);
@@ -134,8 +134,8 @@ void grail_close(struct grail *ge, int fd)
134 pthread_cond_signal (&x->wait); 134 pthread_cond_signal (&x->wait);
135 pthread_mutex_unlock(&x->mutex); 135 pthread_mutex_unlock(&x->mutex);
136 touch_engine_detach(&x->engine, &x->dev); 136 touch_engine_detach(&x->engine, &x->dev);
137 touch_dev_close(&x->dev, fd);
138 gru_destroy(ge); 137 gru_destroy(ge);
138 touch_dev_close(&x->dev, fd);
139 gin_destroy(ge); 139 gin_destroy(ge);
140 free(ge->impl); 140 free(ge->impl);
141 ge->impl = 0; 141 ge->impl = 0;
diff --git a/src/grail-bits.c b/src/grail-bits.c
new file mode 100644
index 0000000..1b38d58
--- /dev/null
+++ b/src/grail-bits.c
@@ -0,0 +1,94 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-bits.h"
26
27static const int grail_bits_in_byte[256] =
28{
29 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
30 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
31 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
32 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
33 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
34 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
35 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
36 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
37};
38
39static const int grail_first_set_bit[256] =
40{
41 -1,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
42 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
43 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
44 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
45 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
46 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
47 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
48 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
49};
50
51void grail_mask_set_mask(grail_mask_t *a, const grail_mask_t *b, int bytes)
52{
53 while (bytes--)
54 *a++ |= *b++;
55}
56
57void grail_mask_clear_mask(grail_mask_t *a, const grail_mask_t *b, int bytes)
58{
59 while (bytes--)
60 *a++ &= ~*b++;
61}
62
63int grail_mask_count(const grail_mask_t *mask, int bytes)
64{
65 int count = 0;
66 while (bytes--)
67 count += grail_bits_in_byte[*mask++];
68 return count;
69}
70
71int grail_mask_get_first(const grail_mask_t *mask, int bytes)
72{
73 int k;
74 for (k = 0; k < bytes; k++)
75 if (mask[k])
76 return (k << 3) | grail_first_set_bit[mask[k]];
77 return -1;
78}
79
80int grail_mask_get_next(int i, const grail_mask_t *mask, int bytes)
81{
82 int k = ++i >> 3;
83 if (k < bytes) {
84 i = grail_first_set_bit[mask[k] & (~0 << (i & 7))];
85 if (i >= 0)
86 return (k << 3) | i;
87 while (++k < bytes) {
88 i = grail_first_set_bit[mask[k]];
89 if (i >= 0)
90 return (k << 3) | i;
91 }
92 }
93 return -1;
94}
diff --git a/src/grail-gestures.c b/src/grail-gestures.c
new file mode 100644
index 0000000..d79da96
--- /dev/null
+++ b/src/grail-gestures.c
@@ -0,0 +1,150 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#include "grail-recognizer.h"
26#include <math.h>
27
28static const float SN_MOVE = 50;
29static const float SN_ZOOM = 50;
30static const float SN_ROTATE = 200;
31static const float BAR_MOVE = 1;
32static const float BAR_ZOOM = 2;
33static const float BAR_ROTATE = 4;
34
35static void compute_position(float *x, float *y,
36 const struct touch_frame *frame)
37{
38 int i, n = frame->nactive;
39 *x = 0;
40 *y = 0;
41 if (n < 1)
42 return;
43 for (i = 0; i < n; i++) {
44 const struct touch *t = frame->active[i];
45 *x += t->prop[TP_POS_X];
46 *y += t->prop[TP_POS_Y];
47 }
48 *x /= n;
49 *y /= n;
50}
51
52static float compute_radius(float x, float y,
53 const struct touch_frame *frame)
54{
55 int i, n = frame->nactive;
56 float r = 0, r2 = 0;
57 if (n < 2)
58 return r;
59 for (i = 0; i < n; i++) {
60 const struct touch *t = frame->active[i];
61 float dx = t->prop[TP_POS_X] - x;
62 float dy = t->prop[TP_POS_Y] - y;
63 r2 += dx * dx + dy * dy;
64 }
65 r2 /= n;
66 r = sqrt(r2);
67 return r;
68}
69
70static float compute_rotation(float x, float y, float r,
71 const struct touch_frame *prev,
72 const struct touch_frame *frame)
73{
74 int i, n = frame->nactive;
75 float da = 0, da2 = 0;
76 if (n < 2)
77 return da;
78 for (i = 0; i < n; i++) {
79 const struct touch *t = frame->active[i];
80 const struct touch *ot = &prev->touch[t->slot];
81 float dx = t->prop[TP_POS_X] - x;
82 float dy = t->prop[TP_POS_Y] - y;
83 float mx = t->prop[TP_POS_X] - ot->prop[TP_POS_X];
84 float my = t->prop[TP_POS_Y] - ot->prop[TP_POS_Y];
85 da2 += dx * my - dy * mx;
86 }
87 da2 /= n;
88 da = da2 / r;
89 return da;
90}
91
92static float move_filter(float x, float x0, float fuzz)
93{
94 if (x > x0 - fuzz / 2 && x < x0 + fuzz / 2)
95 return x0;
96 if (x > x0 - fuzz && x < x0 + fuzz)
97 return (x0 * 3 + x) / 4;
98 if (x > x0 - fuzz * 2 && x < x0 + fuzz * 2)
99 return (x0 + x) / 2;
100 return x;
101}
102
103void gru_init_motion(struct grail *ge, const struct touch_dev_caps *caps)
104{
105 struct gesture_recognizer *gru = ge->gru;
106 struct move_model *state = &gru->move;
107 float x = caps->info[TP_POS_X].maximum - caps->info[TP_POS_X].minimum;
108 float y = caps->info[TP_POS_Y].maximum - caps->info[TP_POS_Y].minimum;
109 float r = sqrt(x * x + y * y);
110 state->xfuzz = x / SN_MOVE;
111 state->yfuzz = y / SN_MOVE;
112 state->rfuzz = r / SN_ZOOM;
113 state->afuzz = r / SN_ROTATE;
114 state->xbar = BAR_MOVE * state->xfuzz;
115 state->ybar = BAR_MOVE * state->yfuzz;
116 state->rbar = BAR_ZOOM * state->rfuzz;
117 state->abar = BAR_ROTATE * state->afuzz;
118}
119
120void gru_reset_motion(struct grail *ge,
121 const struct touch_frame *frame)
122{
123 struct gesture_recognizer *gru = ge->gru;
124 struct move_model *state = &gru->move;
125 compute_position(&state->x, &state->y, frame);
126 state->r = compute_radius(state->x, state->y, frame);
127 state->a = 0;
128 state->ntouch = frame->nactive;
129}
130
131void gru_compute_motion(struct grail *ge,
132 const struct touch_frame *frame)
133{
134 struct gesture_inserter *gin = ge->gin;
135 struct gesture_recognizer *gru = ge->gru;
136 struct move_model *state = &gru->move;
137 float x, y, r, a;
138 compute_position(&x, &y, frame);
139 x = move_filter(x, state->x, state->xfuzz);
140 y = move_filter(y, state->y, state->yfuzz);
141 r = compute_radius(x, y, frame);
142 r = move_filter(r, state->r, state->rfuzz);
143 a = state->a + compute_rotation(x, y, r, &gru->frame, frame);
144 a = move_filter(a, state->a, state->afuzz);
145 state->x = x;
146 state->y = y;
147 state->r = r;
148 state->a = a;
149 state->ntouch = frame->nactive;
150}
diff --git a/src/grail-gestures.h b/src/grail-gestures.h
new file mode 100644
index 0000000..70a5a98
--- /dev/null
+++ b/src/grail-gestures.h
@@ -0,0 +1,85 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Henrik Rydberg <rydberg@bitmath.org>
22 *
23 ****************************************************************************/
24
25#ifndef _GRAIL_GESTURES_H
26#define _GRAIL_GESTURES_H
27
28#include "grail-inserter.h"
29
30struct move_model {
31 float xfuzz, yfuzz, rfuzz, afuzz;
32 float xbar, ybar, rbar, abar;
33 float x, y, r, a;
34 int ntouch;
35};
36
37void gru_init_motion(struct grail *ge,
38 const struct touch_dev_caps *caps);
39void gru_reset_motion(struct grail *ge,
40 const struct touch_frame *frame);
41void gru_compute_motion(struct grail *ge,
42 const struct touch_frame *frame);
43
44struct scroll_model {
45 float x, y;
46 int active, moving, geslot;
47};
48
49void gru_reset_scroll(struct grail *ge,
50 const struct touch_frame *frame);
51int gru_scroll(struct grail *ge,
52 const struct touch_frame *frame);
53
54struct zoom_model {
55 float r;
56 int active, zooming, geslot;
57};
58
59void gru_reset_zoom(struct grail *ge,
60 const struct touch_frame *frame);
61int gru_zoom(struct grail *ge,
62 const struct touch_frame *frame);
63
64struct rotate_model {
65 float a;
66 int active, rotating, geslot;
67};
68
69void gru_reset_rotate(struct grail *ge,
70 const struct touch_frame *frame);
71int gru_rotate(struct grail *ge,
72 const struct touch_frame *frame);
73
74struct combo_model {
75 float x, y, r, a;
76 int active, running, geslot;
77};
78
79void gru_reset_combo(struct grail *ge,
80 const struct touch_frame *frame);
81int gru_combo(struct grail *ge,
82 const struct touch_frame *frame);
83
84#endif
85
diff --git a/src/grail-inserter.c b/src/grail-inserter.c
index b357e29..3927875 100644
--- a/src/grail-inserter.c
+++ b/src/grail-inserter.c
@@ -29,28 +29,6 @@
29 29
30static const int MAX_GESTURE_ID = 0xfff; 30static const int MAX_GESTURE_ID = 0xfff;
31 31
32int gin_init(struct grail *ge)
33{
34 struct gesture_inserter *x;
35 x = calloc(1, sizeof(*x));
36 if (!x)
37 return -ENOMEM;
38 ge->gin = x;
39 return 0;
40}
41
42void gin_destroy(struct grail *ge)
43{
44 free(ge->gin);
45 ge->gin = NULL;
46}
47
48void gin_frame_begin(struct grail *ge, grail_time_t time)
49{
50 struct gesture_inserter *gin = ge->gin;
51 gin->time = time;
52}
53
54static void send_event(struct grail *ge, struct slot_state *s, 32static void send_event(struct grail *ge, struct slot_state *s,
55 const struct gesture_event *ev) 33 const struct gesture_event *ev)
56{ 34{
@@ -61,124 +39,200 @@ static void send_event(struct grail *ge, struct slot_state *s,
61 gev.type = s->type; 39 gev.type = s->type;
62 gev.id = s->id; 40 gev.id = s->id;
63 gev.status = s->status; 41 gev.status = s->status;
64 gev.pos.x = s->x; 42 gev.ntouch = ev->ntouch;
65 gev.pos.y = s->x; 43 gev.nprop = ev->nprop;
66 gev.time = ev->time; 44 gev.time = ev->time;
67 memcpy(gev.prop, ev->prop, DIM_GRAIL_PROP * sizeof(gev.prop[0])); 45 gev.pos = ev->pos;
46 memcpy(gev.prop, ev->prop, ev->nprop * sizeof(grail_prop_t));
68 for (i = 0; i < s->nclient; i++) { 47 for (i = 0; i < s->nclient; i++) {
69 gev.client_id = s->client_id[i]; 48 gev.client_id = s->client_id[i];
70 ge->gesture(ge, &gev); 49 ge->gesture(ge, &gev);
71 } 50 }
72} 51}
73 52
74void gin_frame_end(struct grail *ge, grail_mask_t *filtered) 53// todo: spanning tree for multi-user case
54static void setup_new_gestures(struct grail *ge,
55 const struct touch_frame *frame)
75{ 56{
76 struct gesture_inserter *gin = ge->gin; 57 struct gesture_inserter *gin = ge->gin;
77 struct gesture_event ev; 58 grail_mask_t types[DIM_GRAIL_TYPE_BYTES];
78 grail_mask_t span[DIM_SLOT_BYTES]; 59 grail_mask_t span[DIM_TOUCH_BYTES];
79 int i, slot, spanned = 0; 60 struct grail_client_info info[DIM_CLIENT];
80 memset(filtered, 0, DIM_EV_TYPE_BYTES); 61 int i, j, nclient = 0;
62 int nfresh = grail_mask_count(gin->fresh, sizeof(gin->fresh));
63 if (!nfresh)
64 return;
65
66 memset(types, 0, sizeof(types));
81 memset(span, 0, sizeof(span)); 67 memset(span, 0, sizeof(span));
82 for (slot = 0; slot < DIM_SLOT; slot++) { 68
83 struct slot_state *s = &gin->state[slot]; 69 grail_mask_foreach(i, gin->fresh, sizeof(gin->fresh)) {
84 if (!grail_get_mask(gin->used, slot)) 70 struct slot_state *s = &gin->state[i];
85 continue; 71 grail_mask_set(types, s->type);
72 grail_mask_set_mask(span, s->span, sizeof(span));
73 }
74
75 if (ge->get_clients) {
76 struct grail_coord coord[DIM_CLIENT];
77 int ncoord = 0;
78 grail_mask_foreach(i, span, sizeof(span)) {
79 const struct touch *t = &frame->touch[i];
80 coord[ncoord].x = t->prop[TP_POS_X];
81 coord[ncoord].y = t->prop[TP_POS_Y];
82 ncoord++;
83 }
84 nclient = ge->get_clients(ge, info, DIM_CLIENT,
85 coord, ncoord, types, sizeof(types));
86 }
87
88 grail_mask_foreach(i, gin->fresh, sizeof(gin->fresh)) {
89 struct slot_state *s = &gin->state[i];
90 s->nclient = 0;
91 for (j = 0; j < nclient; j++) {
92 if (!grail_mask_get(info[j].mask, s->type))
93 continue;
94 s->client_id[s->nclient++] = info[j].id;
95 }
96 }
97
98 memset(gin->fresh, 0, sizeof(gin->fresh));
99}
100
101int gin_init(struct grail *ge)
102{
103 struct gesture_inserter *gin;
104 int i;
105 gin = calloc(1, sizeof(*gin));
106 if (!gin)
107 return -ENOMEM;
108 for (i = 0; i < DIM_INSTANCE; i++)
109 grail_mask_set(gin->unused, i);
110 ge->gin = gin;
111 return 0;
112}
113
114void gin_destroy(struct grail *ge)
115{
116 free(ge->gin);
117 ge->gin = NULL;
118}
119
120void gin_frame_begin(struct grail *ge, const struct touch_frame *frame)
121{
122 struct gesture_inserter *gin = ge->gin;
123 gin->time = frame->time;
124}
125
126void gin_frame_end(struct grail *ge,
127 grail_mask_t *filtered, int max_filtered,
128 const struct touch_frame *frame)
129{
130 struct gesture_inserter *gin = ge->gin;
131 grail_mask_t span[DIM_INSTANCE_BYTES];
132 int i, hold = 0, discard = 0;
133
134 memset(filtered, 0, max_filtered);
135 memset(span, 0, sizeof(span));
136
137 setup_new_gestures(ge, frame);
138
139 grail_mask_foreach(i, gin->used, sizeof(gin->used)) {
140 struct slot_state *s = &gin->state[i];
86 if (!s->nclient) 141 if (!s->nclient)
87 gin_gesture_discard(gin, slot); 142 continue;
143 if (s->priority > hold)
144 hold = s->priority;
145 if (s->status == GRAIL_STATUS_BEGIN)
146 continue;
147 if (s->priority > discard)
148 discard = s->priority;
88 } 149 }
89 for (slot = 0; slot < DIM_SLOT; slot++) { 150
90 struct slot_state *s = &gin->state[slot]; 151 grail_mask_foreach(i, gin->used, sizeof(gin->used)) {
91 if (!grail_get_mask(gin->used, slot)) 152 struct slot_state *s = &gin->state[i];
153 if (!s->nclient || s->priority < discard)
154 gin_gesture_discard(gin, i);
155 }
156
157 grail_mask_foreach(i, gin->used, sizeof(gin->used)) {
158 struct slot_state *s = &gin->state[i];
159 struct gesture_event ev;
160 grail_mask_set_mask(span, s->span, sizeof(span));
161 if (s->priority < hold)
92 continue; 162 continue;
93 for (i = 0; i < DIM_SLOT_BYTES; i++) {
94 span[i] |= s->span[i];
95 spanned += !!span[i];
96 }
97 while (!gebuf_empty(&s->buf)) { 163 while (!gebuf_empty(&s->buf)) {
98 gebuf_get(&s->buf, &ev); 164 gebuf_get(&s->buf, &ev);
99 send_event(ge, s, &ev); 165 send_event(ge, s, &ev);
100 } 166 }
101 if (s->status == GRAIL_STATUS_BEGIN)
102 s->status = GRAIL_STATUS_UPDATE;
103 } 167 }
104 if (spanned) 168
105 grail_set_mask(filtered, EV_ABS); 169 grail_mask_foreach(i, gin->used, sizeof(gin->used)) {
170 struct slot_state *s = &gin->state[i];
171 if (s->status == GRAIL_STATUS_END)
172 gin_gesture_discard(gin, i);
173 }
174
175 if (grail_mask_count(span, sizeof(span)))
176 grail_mask_set(filtered, EV_ABS);
106} 177}
107 178
108int gin_gesture_begin(struct grail *ge, const struct touch_frame *frame, 179int gin_gesture_begin(struct grail *ge, int type, int priority,
109 int type, int x, int y, const grail_mask_t *span) 180 const grail_mask_t *span, int nspan)
110{ 181{
111 struct grail_client_info info[DIM_CLIENT];
112 struct grail_coord coord[DIM_CLIENT];
113 struct gesture_inserter *gin = ge->gin; 182 struct gesture_inserter *gin = ge->gin;
114 struct slot_state *s; 183 struct slot_state *s;
115 int i, slot, nc, ncoord = 0; 184 int geslot = grail_mask_get_first(gin->unused, sizeof(gin->unused));
116 for (slot = 0; slot < DIM_SLOT; slot++) 185 if (geslot < 0)
117 if (!grail_get_mask(gin->used, slot))
118 break;
119 if (slot >= DIM_SLOT)
120 return -1; 186 return -1;
121 s = &gin->state[slot]; 187 s = &gin->state[geslot];
122 s->type = type; 188 s->type = type;
189 s->priority = priority;
123 s->id = gin->gestureid++ & MAX_GESTURE_ID; 190 s->id = gin->gestureid++ & MAX_GESTURE_ID;
124 s->status = GRAIL_STATUS_BEGIN; 191 s->status = GRAIL_STATUS_BEGIN;
125 s->x = x;
126 s->y = y;
127 s->nclient = 0; 192 s->nclient = 0;
128 if (ge->get_clients) { 193 memcpy(s->span, span, nspan);
129 grail_mask_t tps[DIM_GRAIL_TYPE_BYTES];
130 memset(tps, 0, sizeof(tps));
131 grail_set_mask(tps, type);
132 for (i = 0; i < DIM_SLOT; i++) {
133 const struct touch *t = &frame->touch[i];
134 if (!grail_get_mask(span, i))
135 continue;
136 coord[ncoord].x = t->prop[TP_POS_X];
137 coord[ncoord].y = t->prop[TP_POS_Y];
138 ncoord++;
139 }
140 nc = ge->get_clients(ge, info, DIM_CLIENT, coord, ncoord, tps);
141 for (i = 0; i < nc; i++) {
142 if (!grail_get_mask(info[i].mask, type))
143 continue;
144 s->client_id[s->nclient] = info[i].id;
145 s->nclient++;
146 }
147 }
148 memcpy(s->span, span, sizeof(s->span));
149 gebuf_clear(&s->buf); 194 gebuf_clear(&s->buf);
150 grail_set_mask(gin->used, slot); 195 grail_mask_clear(gin->unused, geslot);
151 return slot; 196 grail_mask_set(gin->fresh, geslot);
197 grail_mask_set(gin->used, geslot);
198 return geslot;
152} 199}
153 200
154void gin_gesture_end(struct gesture_inserter *gin, int slot) 201void gin_gesture_end(struct gesture_inserter *gin, int geslot)
155{ 202{
156 struct slot_state *s = &gin->state[slot]; 203 gin->state[geslot].status = GRAIL_STATUS_END;
157 s->status = GRAIL_STATUS_END;
158 grail_clear_mask(gin->used, slot);
159} 204}
160 205
161void gin_gesture_discard(struct gesture_inserter *gin, int slot) 206void gin_gesture_discard(struct gesture_inserter *gin, int geslot)
162{ 207{
163 struct slot_state *s = &gin->state[slot]; 208 gebuf_clear(&gin->state[geslot].buf);
164 gebuf_clear(&s->buf); 209 gin_gesture_end(gin, geslot);
165 gin_gesture_end(gin, slot); 210 grail_mask_clear(gin->used, geslot);
211 grail_mask_set(gin->unused, geslot);
166} 212}
167 213
168void gin_gesture_end_all(struct gesture_inserter *gin) 214void gin_gesture_discard_type(struct gesture_inserter *gin, int type)
169{ 215{
170 int slot; 216 int i;
171 for (slot = 0; slot < DIM_SLOT; slot++) 217 grail_mask_foreach(i, gin->used, sizeof(gin->used))
172 gin_gesture_end(gin, slot); 218 if (gin->state[i].type == type)
219 gin_gesture_discard(gin, i);
173} 220}
174 221
175void gin_gesture_event(struct gesture_inserter *gin, int slot, 222void gin_gesture_event(struct gesture_inserter *gin, int geslot,
176 const grail_prop_t *prop) 223 float x, float y, int ntouch,
224 const grail_prop_t *prop, int nprop)
177{ 225{
178 struct slot_state *s = &gin->state[slot]; 226 struct slot_state *s = &gin->state[geslot];
179 struct gesture_event ev; 227 struct gesture_event ev;
228 ev.ntouch = ntouch;
229 ev.nprop = nprop;
180 ev.time = gin->time; 230 ev.time = gin->time;
181 memcpy(ev.prop, prop, sizeof(ev.prop)); 231 ev.pos.x = x;
232 ev.pos.y = y;
233 memcpy(ev.prop, prop, nprop * sizeof(grail_prop_t));
234 if (s->status == GRAIL_STATUS_BEGIN)
235 s->status = GRAIL_STATUS_UPDATE;
182 gebuf_put(&s->buf, &ev); 236 gebuf_put(&s->buf, &ev);
183} 237}
184 238
diff --git a/src/grail-inserter.h b/src/grail-inserter.h
index 1e02d5f..cca71d2 100644
--- a/src/grail-inserter.h
+++ b/src/grail-inserter.h
@@ -22,31 +22,37 @@
22 * 22 *
23 ****************************************************************************/ 23 ****************************************************************************/
24 24
25#ifndef _GRAIL_GESTURE_H 25#ifndef _GRAIL_INSERTER_H
26#define _GRAIL_GESTURE_H 26#define _GRAIL_INSERTER_H
27 27
28#include <grail-touch.h> 28#include <grail-touch.h>
29#include <grail.h> 29#include <grail.h>
30#include "gebuf.h" 30#include "gebuf.h"
31 31
32#define DIM_EV_TYPE EV_CNT 32#define DIM_EV_TYPE EV_CNT
33#define DIM_EV_TYPE_BYTES (DIM_EV_TYPE >> 3) 33#define DIM_EV_TYPE_BYTES ((DIM_EV_TYPE + 7) >> 3)
34 34
35#define DIM_SLOT 32 35#define DIM_INSTANCE 32
36#define DIM_SLOT_BYTES (DIM_SLOT >> 3) 36#define DIM_INSTANCE_BYTES ((DIM_INSTANCE + 7) >> 3)
37 37
38#define DIM_CLIENT 32 38#define DIM_CLIENT 32
39 39
40struct slot_state { 40struct slot_state {
41 int type, id, status, x, y, nclient; 41 int type;
42 int priority;
43 int id;
44 int status;
45 int nclient;
42 struct grail_client_id client_id[DIM_CLIENT]; 46 struct grail_client_id client_id[DIM_CLIENT];
43 grail_mask_t span[DIM_SLOT_BYTES]; 47 grail_mask_t span[DIM_TOUCH_BYTES];
44 struct gebuf buf; 48 struct gebuf buf;
45}; 49};
46 50
47struct gesture_inserter { 51struct gesture_inserter {
48 struct slot_state state[DIM_SLOT]; 52 struct slot_state state[DIM_INSTANCE];
49 grail_mask_t used[DIM_SLOT_BYTES]; 53 grail_mask_t unused[DIM_INSTANCE_BYTES];
54 grail_mask_t fresh[DIM_INSTANCE_BYTES];
55 grail_mask_t used[DIM_INSTANCE_BYTES];
50 grail_time_t time; 56 grail_time_t time;
51 int gestureid; 57 int gestureid;
52}; 58};
@@ -54,16 +60,18 @@ struct gesture_inserter {
54int gin_init(struct grail *ge); 60int gin_init(struct grail *ge);
55void gin_destroy(struct grail *ge); 61void gin_destroy(struct grail *ge);
56 62
57void gin_frame_begin(struct grail *ge, grail_time_t time); 63void gin_frame_begin(struct grail *ge, const struct touch_frame *frame);
58void gin_frame_end(struct grail *ge, grail_mask_t *filtered); 64void gin_frame_end(struct grail *ge, grail_mask_t *filtered, int max_filtered,
65 const struct touch_frame *frame);
59 66
60int gin_gesture_begin(struct grail *ge, const struct touch_frame *frame, 67int gin_gesture_begin(struct grail *ge, int type, int priority,
61 int type, int x, int y, const grail_mask_t *span); 68 const grail_mask_t *span, int nspan);
62void gin_gesture_discard(struct gesture_inserter *gin, int slot); 69void gin_gesture_end(struct gesture_inserter *gin, int geslot);
63void gin_gesture_end(struct gesture_inserter *gin, int slot); 70void gin_gesture_discard(struct gesture_inserter *gin, int geslot);
64void gin_gesture_end_all(struct gesture_inserter *gin); 71void gin_gesture_discard_type(struct gesture_inserter *gin, int type);
65 72
66void gin_gesture_event(struct gesture_inserter *gin, int slot, 73void gin_gesture_event(struct gesture_inserter *gin, int geslot,
67 const grail_prop_t *prop); 74 float x, float y, int ntouch,
75 const grail_prop_t *prop, int nprop);
68 76
69#endif 77#endif
diff --git a/src/grail-recognizer.c b/src/grail-recognizer.c
index 9ede059..1e7a8bf 100644
--- a/src/grail-recognizer.c
+++ b/src/grail-recognizer.c
@@ -22,24 +22,21 @@
22 * 22 *
23 ****************************************************************************/ 23 ****************************************************************************/
24 24
25#include "grail-inserter.h" 25#include "grail-recognizer.h"
26#include <string.h> 26#include <string.h>
27#include <malloc.h> 27#include <malloc.h>
28#include <errno.h> 28#include <errno.h>
29 29
30struct gesture_recognizer { 30static const touch_time_t DROP_TIMEOUT_MS = 100;
31 struct touch_frame frame;
32 touch_time_t scroll_time;
33 int scroll_active;
34 int scroll_gslot;
35 int scroll_slots[2];
36};
37 31
38int gru_init(struct grail *ge) 32int gru_init(struct grail *ge, const struct touch_dev_caps *caps)
39{ 33{
40 ge->gru = calloc(1, sizeof(struct gesture_recognizer)); 34 struct gesture_recognizer *gru;
41 if (!ge->gru) 35 gru = calloc(1, sizeof(struct gesture_recognizer));
36 if (!gru)
42 return -ENOMEM; 37 return -ENOMEM;
38 ge->gru = gru;
39 gru_init_motion(ge, caps);
43 return 0; 40 return 0;
44} 41}
45 42
@@ -49,49 +46,32 @@ void gru_destroy(struct grail *ge)
49 ge->gru = NULL; 46 ge->gru = NULL;
50} 47}
51 48
49static void reset_gru_state(struct grail *ge, const struct touch_frame *frame)
50{
51 gru_reset_combo(ge, frame);
52 gru_reset_rotate(ge, frame);
53 gru_reset_zoom(ge, frame);
54 gru_reset_scroll(ge, frame);
55 gru_reset_motion(ge, frame);
56}
57
52void gru_recognize(struct grail *ge, const struct touch_frame *frame) 58void gru_recognize(struct grail *ge, const struct touch_frame *frame)
53{ 59{
54 struct gesture_inserter *gin = ge->gin;
55 struct gesture_recognizer *gru = ge->gru; 60 struct gesture_recognizer *gru = ge->gru;
56 grail_prop_t prop[DIM_GRAIL_PROP]; 61 struct move_model *move = &gru->move;
57 grail_mask_t span[16]; 62 if (!ge->gin || !ge->gru)
58 int slot, nslot, x, y, dx, dy;
59 if (!gru)
60 return;
61 if (frame->ncreate || frame->ndestroy) {
62 gin_gesture_end_all(gin);
63 gru->scroll_active = 0;
64 return; 63 return;
65 } 64 if (frame->nactive < move->ntouch &&
66 if (frame->ntouch != 2) 65 frame->time < gru->frame.time + DROP_TIMEOUT_MS)
67 return; 66 return;
68 memset(span, 0, sizeof(span)); 67 if (frame->nactive < 2 || frame->ncreate || frame->ndestroy) {
69 nslot = 0; 68 reset_gru_state(ge, frame);
70 x = y = dx = dy = 0;
71 for (slot = frame->slot; slot >= 0; slot = next_slot(frame, slot)) {
72 struct touch *ot = &gru->frame.touch[slot];
73 const struct touch *t = &frame->touch[slot];
74 gru->scroll_slots[nslot] = slot;
75 grail_set_mask(span, slot);
76 x += t->prop[TP_POS_X];
77 y += t->prop[TP_POS_Y];
78 dx += t->prop[TP_POS_X] - ot->prop[TP_POS_X];
79 dy += t->prop[TP_POS_Y] - ot->prop[TP_POS_Y];
80 nslot++;
81 }
82 x /= nslot;
83 y /= nslot;
84 dx /= nslot;
85 dy /= nslot;
86 if (!dy)
87 return; 69 return;
88 if (!gru->scroll_active) {
89 gru->scroll_gslot = gin_gesture_begin(ge, frame,
90 GRAIL_TYPE_VSCROLL,
91 x, y, span);
92 gru->scroll_active = 1;
93 } 70 }
94 prop[0] = dy; 71 gru_compute_motion(ge, frame);
95 gin_gesture_event(gin, gru->scroll_gslot, prop); 72 gru_scroll(ge, frame);
73 gru_zoom(ge, frame);
74 gru_rotate(ge, frame);
75 gru_combo(ge, frame);
96 gru->frame = *frame; 76 gru->frame = *frame;
97} 77}
diff --git a/src/grail-recognizer.h b/src/grail-recognizer.h
index 8c47162..247687d 100644
--- a/src/grail-recognizer.h
+++ b/src/grail-recognizer.h
@@ -25,9 +25,18 @@
25#ifndef _GRAIL_RECOGNIZER_H 25#ifndef _GRAIL_RECOGNIZER_H
26#define _GRAIL_RECOGNIZER_H 26#define _GRAIL_RECOGNIZER_H
27 27
28#include "grail-inserter.h" 28#include "grail-gestures.h"
29 29
30int gru_init(struct grail *ge); 30struct gesture_recognizer {
31 struct touch_frame frame;
32 struct move_model move;
33 struct scroll_model scroll;
34 struct zoom_model zoom;
35 struct rotate_model rotate;
36 struct combo_model combo;
37};
38
39int gru_init(struct grail *ge, const struct touch_dev_caps *caps);
31void gru_recognize(struct grail *ge, const struct touch_frame *frame); 40void gru_recognize(struct grail *ge, const struct touch_frame *frame);
32void gru_destroy(struct grail *ge); 41void gru_destroy(struct grail *ge);
33 42
diff --git a/src/mtdev-details.h b/src/mtdev-details.h
new file mode 100644
index 0000000..873528d
--- /dev/null
+++ b/src/mtdev-details.h
@@ -0,0 +1,19 @@
1#ifndef _MTDEV_DETAILS_H
2#define _MTDEV_DETAILS_H
3
4#define MTDEV_TRACKING_ID 9
5#define MTDEV_POSITION_X 5
6#define MTDEV_POSITION_Y 6
7#define MTDEV_TOUCH_MAJOR 0
8#define MTDEV_TOUCH_MINOR 1
9#define MTDEV_WIDTH_MAJOR 2
10#define MTDEV_WIDTH_MINOR 3
11#define MTDEV_ORIENTATION 4
12#define MTDEV_PRESSURE 10
13
14int mtdev_fetch_event(struct mtdev *dev, int fd, struct input_event *ev);
15void mtdev_put_event(struct mtdev *dev, const struct input_event *ev);
16int mtdev_empty(struct mtdev *dev);
17void mtdev_get_event(struct mtdev *dev, struct input_event* ev);
18
19#endif
diff --git a/src/mtdev-plumbing.h b/src/mtdev-plumbing.h
deleted file mode 100644
index c1d141e..0000000
--- a/src/mtdev-plumbing.h
+++ /dev/null
@@ -1,105 +0,0 @@
1/*****************************************************************************
2 *
3 * mtdev - Multitouch Protocol Translation Library (MIT license)
4 *
5 * Copyright (C) 2010 Henrik Rydberg <rydberg@bitmath.org>
6 * Copyright (C) 2010 Canonical Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 ****************************************************************************/
28
29#ifndef _MTDEV_PLUMBING_H
30#define _MTDEV_PLUMBING_H
31
32#include <mtdev.h>
33
34/**
35 * mtdev_init - initialize mtdev converter
36 * @dev: the mtdev to initialize
37 *
38 * Sets up the internal data structures.
39 *
40 * Returns zero on success, negative error number otherwise.
41 */
42int mtdev_init(struct mtdev *dev);
43
44/**
45 * mtdev_configure - configure the mtdev converter
46 * @dev: the mtdev to configure
47 * @fd: file descriptor of the kernel device
48 *
49 * Reads the device properties to set up the protocol capabilities.
50 * If preferred, this can be done by hand, omitting this call.
51 *
52 * Returns zero on success, negative error number otherwise.
53 */
54int mtdev_configure(struct mtdev *dev, int fd);
55
56/**
57 * mtdev_fetch_event - fetch an event from the kernel device
58 * @dev: the mtdev in use
59 * @fd: file descriptor of the kernel device
60 * @ev: the kernel input event to fill
61 *
62 * Fetch a kernel event from the kernel device. The read operation
63 * behaves as dictated by the file descriptor; if O_NONBLOCK is not
64 * set, the read will block until an event is available.
65 *
66 * On success, returns the number of events read (0 or 1). Otherwise,
67 * a standard negative error number is returned.
68 */
69int mtdev_fetch_event(struct mtdev *dev, int fd, struct input_event *ev);
70
71/**
72 * mtdev_put_event - put an event into the converter
73 * @dev: the mtdev in use
74 * @ev: the kernel input event to put
75 *
76 * Put a kernel event into the mtdev converter. The event should
77 * come straight from the device.
78 *
79 * This call does not block; if the buffer becomes full, older events
80 * are dropped. The buffer is guaranteed to handle several complete MT
81 * packets.
82 */
83void mtdev_put_event(struct mtdev *dev, const struct input_event *ev);
84
85/**
86 * mtdev_empty - check if there are events to get
87 * @dev: the mtdev in use
88 *
89 * Returns true if the processed event queue is empty, false otherwise.
90 */
91int mtdev_empty(struct mtdev *dev);
92
93/**
94 * mtdev_get_event - get processed events from mtdev
95 * @dev: the mtdev in use
96 * @ev: the input event to fill
97 *
98 * Get a processed event from mtdev. The events appear as if they came
99 * from a type B device emitting MT slot events.
100 *
101 * The queue must be non-empty before calling this function.
102 */
103void mtdev_get_event(struct mtdev *dev, struct input_event* ev);
104
105#endif
diff --git a/src/touch-dev.c b/src/touch-dev.c
index b7efb35..3c3036c 100644
--- a/src/touch-dev.c
+++ b/src/touch-dev.c
@@ -27,20 +27,46 @@
27#include <string.h> 27#include <string.h>
28#include <errno.h> 28#include <errno.h>
29#include "evbuf.h" 29#include "evbuf.h"
30#include "mtdev-plumbing.h" 30#include "mtdev-details.h"
31
32#define DIM_TOUCH 32
33 31
34struct touch_dev_impl { 32struct touch_dev_impl {
35 int id[DIM_TOUCH]; 33 int id[DIM_TOUCH];
36 int slot; 34 int slot;
37 int status; 35 int status;
38 touch_prop_mask_t mask[1]; 36 grail_mask_t mask[DIM_TOUCH_BYTES];
39 touch_prop_t prop[DIM_TOUCH_PROP]; 37 touch_prop_t prop[DIM_TOUCH_PROP];
40 struct evbuf evbuf; 38 struct evbuf evbuf;
41 struct mtdev mtdev; 39 struct mtdev mtdev;
42}; 40};
43 41
42static inline void set_info(struct touch_dev_caps *caps, int code,
43 const struct input_absinfo *info)
44{
45 grail_mask_set(caps->mask, code);
46 caps->info[code] = *info;
47}
48
49static void set_caps(struct touch_dev_caps *caps,
50 const struct mtdev_caps *mtcaps)
51{
52 if (mtcaps->has_abs[MTDEV_POSITION_X])
53 set_info(caps, TP_POS_X, &mtcaps->abs[MTDEV_POSITION_X]);
54 if (mtcaps->has_abs[MTDEV_POSITION_Y])
55 set_info(caps, TP_POS_Y, &mtcaps->abs[MTDEV_POSITION_Y]);
56 if (mtcaps->has_abs[MTDEV_TOUCH_MAJOR])
57 set_info(caps, TP_TOUCH_MAJOR, &mtcaps->abs[MTDEV_TOUCH_MAJOR]);
58 if (mtcaps->has_abs[MTDEV_TOUCH_MINOR])
59 set_info(caps, TP_TOUCH_MINOR, &mtcaps->abs[MTDEV_TOUCH_MINOR]);
60 if (mtcaps->has_abs[MTDEV_WIDTH_MAJOR])
61 set_info(caps, TP_WIDTH_MAJOR, &mtcaps->abs[MTDEV_WIDTH_MAJOR]);
62 if (mtcaps->has_abs[MTDEV_WIDTH_MINOR])
63 set_info(caps, TP_WIDTH_MINOR, &mtcaps->abs[MTDEV_WIDTH_MINOR]);
64 if (mtcaps->has_abs[MTDEV_ORIENTATION])
65 set_info(caps, TP_ORIENTATION, &mtcaps->abs[MTDEV_ORIENTATION]);
66 if (mtcaps->has_abs[MTDEV_PRESSURE])
67 set_info(caps, TP_PRESSURE, &mtcaps->abs[MTDEV_PRESSURE]);
68}
69
44static int touch_get_fetched(struct touch_dev_impl *x, 70static int touch_get_fetched(struct touch_dev_impl *x,
45 struct input_event* ev, int ev_max) 71 struct input_event* ev, int ev_max)
46{ 72{
@@ -58,14 +84,9 @@ static int touch_get_fetched(struct touch_dev_impl *x,
58 return count; 84 return count;
59} 85}
60 86
61static inline int has_any_prop(const touch_prop_mask_t *mask)
62{
63 return mask[0] != 0;
64}
65
66static inline void set_prop(struct touch_dev_impl *x, int code, int value) 87static inline void set_prop(struct touch_dev_impl *x, int code, int value)
67{ 88{
68 x->mask[code >> 5] |= 1U << (code & 31); 89 grail_mask_set(x->mask, code);
69 x->prop[code] = value; 90 x->prop[code] = value;
70} 91}
71 92
@@ -76,7 +97,8 @@ static void finish_touch(struct touch_dev *dev,
76 dev->destroy(dev, x->slot); 97 dev->destroy(dev, x->slot);
77 if (x->status > 0 && dev->create) 98 if (x->status > 0 && dev->create)
78 dev->create(dev, x->slot, x->mask, x->prop); 99 dev->create(dev, x->slot, x->mask, x->prop);
79 if (x->status == 0 && has_any_prop(x->mask) && dev->modify) 100 if (x->status == 0 && dev->modify &&
101 grail_mask_count(x->mask, sizeof(x->mask)))
80 dev->modify(dev, x->slot, x->mask, x->prop); 102 dev->modify(dev, x->slot, x->mask, x->prop);
81 x->status = 0; 103 x->status = 0;
82 memset(x->mask, 0, sizeof(x->mask)); 104 memset(x->mask, 0, sizeof(x->mask));
@@ -157,6 +179,7 @@ int touch_dev_open(struct touch_dev *dev, int fd)
157 goto error; 179 goto error;
158 for (i = 0; i < DIM_TOUCH; i++) 180 for (i = 0; i < DIM_TOUCH; i++)
159 x->id[i] = MT_ID_NULL; 181 x->id[i] = MT_ID_NULL;
182 set_caps(&dev->caps, &x->mtdev.caps);
160 dev->impl = x; 183 dev->impl = x;
161 return 0; 184 return 0;
162 error: 185 error:
diff --git a/src/touch-engine.c b/src/touch-engine.c
index 28b68da..3e6870e 100644
--- a/src/touch-engine.c
+++ b/src/touch-engine.c
@@ -34,40 +34,29 @@ static void tp_event(struct touch_dev *dev, const struct input_event *ev)
34 34
35static void update_frame(struct touch_frame *frame, 35static void update_frame(struct touch_frame *frame,
36 int slot, int active, 36 int slot, int active,
37 const touch_prop_mask_t *mask, 37 const grail_mask_t *mask,
38 const touch_prop_t *prop) 38 const touch_prop_t *prop)
39{ 39{
40 struct touch *touch = &frame->touch[slot]; 40 struct touch *t = &frame->touch[slot];
41 touch->active = active; 41 int i;
42 if (mask && prop) { 42 t->active = active;
43 int i; 43 grail_mask_modify(frame->touches, slot, active);
44 for (i = 0; i < DIM_TOUCH_PROP; i++) 44 if (mask && prop)
45 if (has_prop(mask, i)) 45 grail_mask_foreach(i, mask, DIM_TOUCH_PROP_BYTES)
46 touch->prop[i] = prop[i]; 46 t->prop[i] = prop[i];
47 }
48} 47}
49 48
50static void finalize_frame(struct touch_frame *frame, touch_time_t time) 49static void finalize_frame(struct touch_frame *frame, touch_time_t time)
51{ 50{
52 int i, last = -1; 51 int i, nslot = 0;
53 frame->ntouch = 0; 52 grail_mask_foreach(i, frame->touches, DIM_TOUCH_BYTES)
54 frame->slot = -1; 53 frame->active[nslot++] = &frame->touch[i];
55 for (i = 0; i < DIM_TOUCH_PROP; i++) { 54 frame->nactive = nslot;
56 frame->touch[i].next = -1;
57 if (frame->touch[i].active) {
58 if (last < 0)
59 frame->slot = i;
60 else
61 frame->touch[last].next = i;
62 last = i;
63 frame->ntouch++;
64 }
65 }
66 frame->time = time; 55 frame->time = time;
67} 56}
68 57
69static void tp_create(struct touch_dev *dev, int slot, 58static void tp_create(struct touch_dev *dev, int slot,
70 const touch_prop_mask_t *mask, const touch_prop_t *prop) 59 const grail_mask_t *mask, const touch_prop_t *prop)
71{ 60{
72 struct touch_engine *engine = dev->priv; 61 struct touch_engine *engine = dev->priv;
73 if (engine) { 62 if (engine) {
@@ -77,7 +66,7 @@ static void tp_create(struct touch_dev *dev, int slot,
77} 66}
78 67
79static void tp_modify(struct touch_dev *dev, int slot, 68static void tp_modify(struct touch_dev *dev, int slot,
80 const touch_prop_mask_t *mask, const touch_prop_t *prop) 69 const grail_mask_t *mask, const touch_prop_t *prop)
81{ 70{
82 struct touch_engine *engine = dev->priv; 71 struct touch_engine *engine = dev->priv;
83 if (engine) { 72 if (engine) {
@@ -117,7 +106,10 @@ void touch_engine_init(struct touch_engine *engine,
117 const struct input_event *ev), 106 const struct input_event *ev),
118 void *priv) 107 void *priv)
119{ 108{
109 int i;
120 memset(engine, 0, sizeof(*engine)); 110 memset(engine, 0, sizeof(*engine));
111 for (i = 0; i < DIM_TOUCH; i++)
112 engine->frame.touch[i].slot = i;
121 engine->event = event; 113 engine->event = event;
122 engine->sync = sync; 114 engine->sync = sync;
123 engine->priv = priv; 115 engine->priv = priv;