summaryrefslogtreecommitdiff
path: root/mtdev
diff options
context:
space:
mode:
Diffstat (limited to 'mtdev')
-rw-r--r--mtdev/core.c402
-rw-r--r--mtdev/test.c79
2 files changed, 481 insertions, 0 deletions
diff --git a/mtdev/core.c b/mtdev/core.c
new file mode 100644
index 0000000..c8eae37
--- /dev/null
+++ b/mtdev/core.c
@@ -0,0 +1,402 @@
1/***************************************************************************
2 *
3 * Multitouch X driver
4 * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 **************************************************************************/
21
22#include "mtdev.h"
23#include "mtbit.h"
24
25/**
26 * struct MTSlot - represents the state of an input MT slot
27 * @abs: current values of ABS_MT axes for this slot
28 */
29struct MTSlot {
30 int abs[MT_ABS_SIZE];
31};
32
33/**
34 * struct MTDevState - MT slot parsing
35 * @data: array of scratch slot data
36 * @used: bitmask of currently used slots
37 * @slot: slot currently being modified
38 * @lastid: last used tracking id
39 */
40struct MTDevState {
41 struct MTSlot data[DIM_FINGER];
42 bitmask_t used;
43 bitmask_t slot;
44 bitmask_t lastid;
45};
46
47/**
48 * mtdev_init - init MT device
49 * @dev: device to initialize
50 * @caps: device capabilities
51 */
52int mtdev_init(struct MTDev *dev, const struct Capabilities *caps)
53{
54 memset(dev, 0, sizeof(struct MTDev));
55 if (!caps->has_mtdata)
56 return -ENODEV;
57 if (!caps->has_slot) {
58 dev->priv = calloc(1, sizeof(struct MTDevState));
59 if (!dev->priv)
60 return -ENOMEM;
61 }
62 return 0;
63}
64
65static inline int istouch(const struct MTSlot *data,
66 const struct Capabilities *caps)
67{
68 return data->abs[BIT_TOUCH_MAJOR] || !caps->has_abs[BIT_TOUCH_MAJOR];
69}
70
71/* Dmitry Torokhov's code from kernel/driver/input/input.c */
72static int defuzz(int value, int old_val, int fuzz)
73{
74 if (fuzz) {
75 if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
76 return old_val;
77
78 if (value > old_val - fuzz && value < old_val + fuzz)
79 return (old_val * 3 + value) / 4;
80
81 if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
82 return (old_val + value) / 2;
83 }
84
85 return value;
86}
87
88/*
89 * solve - solve contact matching problem
90 * @priv: parsing state
91 * @caps: device capabilities
92 * @sid: array of current tracking ids
93 * @sx: array of current position x
94 * @sy: array of current position y
95 * @sn: number of current contacts
96 * @nid: array of new or matched tracking ids, to be filled
97 * @nx: array of new position x
98 * @ny: array of new position y
99 * @nn: number of new contacts
100 * @touch: which of the new contacts to fill
101 */
102static void solve(struct MTDevState *priv, const struct Capabilities *caps,
103 const int *sid, const int *sx, const int *sy, int sn,
104 int *nid, const int *nx, const int *ny, int nn,
105 bitmask_t touch)
106{
107 int A[DIM2_FINGER], *row;
108 int n2s[DIM_FINGER];
109 int id, i, j;
110
111 /* setup distance matrix for contact matching */
112 for (j = 0; j < sn; j++) {
113 row = A + nn * j;
114 for (i = 0; i < nn; i++)
115 row[i] = dist2(nx[i] - sx[j], ny[i] - sy[j]);
116 }
117
118 match_fingers(n2s, A, nn, sn);
119
120 /* update matched contacts and create new ones */
121 foreach_bit(i, touch) {
122 j = n2s[i];
123 id = j >= 0 ? sid[j] : caps->nullid;
124 while (id == caps->nullid)
125 id = ++priv->lastid;
126 nid[i] = id;
127 }
128}
129
130/*
131 * assign_tracking_id - assign tracking ids to all contacts
132 * @priv: parsing state
133 * @caps: device capabilities
134 * @data: array of all present contacts, to be filled
135 * @prop: array of all set contacts properties
136 * @size: number of contacts in array
137 * @touch: which of the contacts are actual touches
138 */
139static void assign_tracking_id(struct MTDevState *priv,
140 const struct Capabilities *caps,
141 struct MTSlot *data, bitmask_t *prop,
142 int size, bitmask_t touch)
143{
144 int sid[DIM_FINGER], sx[DIM_FINGER], sy[DIM_FINGER], sn = 0;
145 int nid[DIM_FINGER], nx[DIM_FINGER], ny[DIM_FINGER], i;
146 foreach_bit(i, priv->used) {
147 sid[sn] = priv->data[i].abs[BIT_TRACKING_ID];
148 sx[sn] = priv->data[i].abs[BIT_POSITION_X];
149 sy[sn] = priv->data[i].abs[BIT_POSITION_Y];
150 sn++;
151 }
152 for (i = 0; i < size; i++) {
153 nx[i] = data[i].abs[BIT_POSITION_X];
154 ny[i] = data[i].abs[BIT_POSITION_Y];
155 }
156 solve(priv, caps, sid, sx, sy, sn, nid, nx, ny, size, touch);
157 for (i = 0; i < size; i++) {
158 data[i].abs[BIT_TRACKING_ID] =
159 GETBIT(touch, i) ? nid[i] : caps->nullid;
160 prop[i] |= BITMASK(BIT_TRACKING_ID);
161 }
162}
163
164/*
165 * process_typeA - consume MT events and update parsing state
166 * @dev: MT device
167 * @data: array of all present contacts, to be filled
168 * @prop: array of all set contacts properties, to be filled
169 *
170 * This function is called when a SYN_REPORT is seen, right before
171 * that event is pushed to the queue.
172 *
173 * Returns -1 if the packet is not MT related and should not affect
174 * the current parsing state.
175 */
176static int process_typeA(struct MTDev *dev,
177 struct MTSlot *data, bitmask_t *prop)
178{
179 struct input_event ev;
180 int consumed, mtcode;
181 int mtcnt = 0, size = 0;
182 prop[size] = 0;
183 while (!evbuf_empty(&dev->inbuf)) {
184 evbuf_pop(&dev->inbuf, &ev);
185 consumed = 0;
186 switch (ev.type) {
187 case EV_SYN:
188 switch (ev.code) {
189 case SYN_MT_REPORT:
190 if (size < DIM_FINGER &&
191 GETBIT(prop[size], BIT_POSITION_X) &&
192 GETBIT(prop[size], BIT_POSITION_Y))
193 size++;
194 if (size < DIM_FINGER)
195 prop[size] = 0;
196 mtcnt++;
197 consumed = 1;
198 break;
199 }
200 break;
201 case EV_KEY:
202 switch (ev.code) {
203 case BTN_TOUCH:
204 mtcnt++;
205 break;
206 }
207 break;
208 case EV_ABS:
209 if (size < DIM_FINGER && has_abs2mt(ev.code)) {
210 mtcode = abs2mt(ev.code);
211 data[size].abs[mtcode] = ev.value;
212 prop[size] |= BITMASK(mtcode);
213 mtcnt++;
214 consumed = 1;
215 }
216 break;
217 }
218 if (!consumed)
219 evbuf_push(&dev->outbuf, &ev);
220 }
221 return mtcnt ? size : -1;
222}
223
224/*
225 * process_typeB - propagate events without parsing
226 * @dev: MT device
227 *
228 * This function is called when a SYN_REPORT is seen, right before
229 * that event is pushed to the queue.
230 */
231static void process_typeB(struct MTDev *dev)
232{
233 struct input_event ev;
234 while (!evbuf_empty(&dev->inbuf)) {
235 evbuf_pop(&dev->inbuf, &ev);
236 evbuf_push(&dev->outbuf, &ev);
237 }
238}
239
240/*
241 * filter_data - apply input filtering on new incoming data
242 * @priv: parsing state
243 * @caps: device capabilities
244 * @data: the incoming data to filter
245 * @prop: the properties to filter
246 * @slot: the slot the data refers to
247 */
248static void filter_data(const struct MTDevState *priv,
249 const struct Capabilities *caps,
250 struct MTSlot *data, bitmask_t prop,
251 int slot)
252{
253 int i;
254 foreach_bit(i, prop) {
255 int fuzz = caps->abs[i].fuzz;
256 int oldval = priv->data[slot].abs[i];
257 data->abs[i] = defuzz(data->abs[i], oldval, fuzz);
258 }
259}
260
261/*
262 * push_slot_changes - propagate state changes
263 * @dev: MT device
264 * @data: the incoming data to propagate
265 * @prop: the properties to propagate
266 * @slot: the slot the data refers to
267 * @syn: reference to the SYN_REPORT event
268 */
269static void push_slot_changes(struct MTDev *dev,
270 const struct MTSlot *data, bitmask_t prop,
271 int slot, const struct input_event *syn)
272{
273 struct MTDevState *priv = dev->priv;
274 struct input_event ev;
275 int i, count = 0;
276 foreach_bit(i, prop)
277 if (priv->data[slot].abs[i] != data->abs[i])
278 count++;
279 if (!count)
280 return;
281 ev.time = syn->time;
282 ev.type = EV_ABS;
283 ev.code = ABS_MT_SLOT;
284 ev.value = slot;
285 if (priv->slot != ev.value) {
286 evbuf_push(&dev->outbuf, &ev);
287 priv->slot = ev.value;
288 }
289 foreach_bit(i, prop) {
290 ev.code = mt2abs(i);
291 ev.value = data->abs[i];
292 if (priv->data[slot].abs[i] != ev.value) {
293 evbuf_push(&dev->outbuf, &ev);
294 priv->data[slot].abs[i] = ev.value;
295 }
296 }
297}
298
299/*
300 * apply_typeA_changes - parse and propagate state changes
301 * @dev: MT device
302 * @caps: device capabilities
303 * @data: array of data to apply
304 * @prop: array of properties to apply
305 * @size: number of contacts in array
306 * @syn: reference to the SYN_REPORT event
307 */
308static void apply_typeA_changes(struct MTDev *dev,
309 const struct Capabilities *caps,
310 struct MTSlot *data, const bitmask_t *prop,
311 int size, const struct input_event *syn)
312{
313 struct MTDevState *priv = dev->priv;
314 bitmask_t unused = ~priv->used;
315 bitmask_t used = 0;
316 int i, slot, id;
317 for (i = 0; i < size; i++) {
318 id = data[i].abs[BIT_TRACKING_ID];
319 foreach_bit(slot, priv->used) {
320 if (priv->data[slot].abs[BIT_TRACKING_ID] != id)
321 continue;
322 filter_data(priv, caps, &data[i], prop[i], slot);
323 push_slot_changes(dev, &data[i], prop[i], slot, syn);
324 SETBIT(used, slot);
325 id = caps->nullid;
326 break;
327 }
328 if (id != caps->nullid) {
329 slot = firstbit(unused);
330 push_slot_changes(dev, &data[i], prop[i], slot, syn);
331 SETBIT(used, slot);
332 CLEARBIT(unused, slot);
333 }
334 }
335
336 /* clear unused slots and update slot usage */
337 foreach_bit(slot, priv->used & ~used) {
338 struct MTSlot tdata = priv->data[slot];
339 bitmask_t tprop = BITMASK(BIT_TRACKING_ID);
340 tdata.abs[BIT_TRACKING_ID] = caps->nullid;
341 push_slot_changes(dev, &tdata, tprop, slot, syn);
342 }
343 priv->used = used;
344}
345
346/*
347 * convert_A_to_B - propagate a type A packet as a type B packet
348 * @dev: MT device
349 * @caps: device capabilities
350 * @syn: reference to the SYN_REPORT event
351 */
352static void convert_A_to_B(struct MTDev *dev,
353 const struct Capabilities *caps,
354 const struct input_event *syn)
355{
356 struct MTSlot data[DIM_FINGER];
357 bitmask_t prop[DIM_FINGER];
358 int size = process_typeA(dev, data, prop);
359 if (size < 0)
360 return;
361 if (!caps->has_abs[BIT_TRACKING_ID]) {
362 bitmask_t touch = 0;
363 int i;
364 for (i = 0; i < size; i++)
365 MODBIT(touch, i, istouch(&data[i], caps));
366 assign_tracking_id(dev->priv, caps, data, prop, size, touch);
367 }
368 apply_typeA_changes(dev, caps, data, prop, size, syn);
369}
370
371/**
372 * mtdev_push - insert event into MT device
373 * @dev: MT device
374 * @caps: device capabilities
375 * @syn: reference to the SYN_REPORT event
376 */
377void mtdev_push(struct MTDev *dev,
378 const struct Capabilities *caps,
379 const struct input_event *ev)
380{
381 if (ev->type == EV_SYN && ev->code == SYN_REPORT) {
382 bitmask_t head = dev->outbuf.head;
383 if (dev->priv)
384 convert_A_to_B(dev, caps, ev);
385 else
386 process_typeB(dev);
387 if (dev->outbuf.head != head)
388 evbuf_push(&dev->outbuf, ev);
389 } else {
390 evbuf_push(&dev->inbuf, ev);
391 }
392}
393
394/**
395 * mtdev_destroy - destroy MT device
396 * @dev: MT device
397 */
398void mtdev_destroy(struct MTDev *dev)
399{
400 free(dev->priv);
401 memset(dev, 0, sizeof(struct MTDev));
402}
diff --git a/mtdev/test.c b/mtdev/test.c
new file mode 100644
index 0000000..bbe4829
--- /dev/null
+++ b/mtdev/test.c
@@ -0,0 +1,79 @@
1/***************************************************************************
2 *
3 * Multitouch X driver
4 * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 **************************************************************************/
21
22#include "mtdev-iobuf.h"
23#include "mtdev.h"
24#include <fcntl.h>
25#include <xbypass.h>
26
27static void print_event(const struct input_event *ev)
28{
29 static const mstime_t ms = 1000;
30 static int slot;
31 mstime_t evtime = ev->time.tv_usec / ms + ev->time.tv_sec * ms;
32 if (ev->type == EV_ABS && ev->code == ABS_MT_SLOT)
33 slot = ev->value;
34 fprintf(stderr, "%012llx: %04d: %04x %04x %d\n",
35 evtime, slot, ev->type, ev->code, ev->value);
36}
37
38static void loop_device(int fd)
39{
40 struct Capabilities caps;
41 struct IOBuffer iobuf;
42 struct MTDev mtdev;
43 const struct input_event *ev;
44 struct input_event event;
45 if (read_capabilities(&caps, fd)) {
46 fprintf(stderr, "error: could not read device capabilities\n");
47 return;
48 }
49 output_capabilities(&caps);
50 if (mtdev_init(&mtdev, &caps)) {
51 fprintf(stderr, "error: could not initialize device\n");
52 return;
53 }
54 init_iobuf(&iobuf);
55 while (ev = get_iobuf_event(&iobuf, fd)) {
56 mtdev_push(&mtdev, &caps, ev);
57 while (!mtdev_empty(&mtdev)) {
58 mtdev_pop(&mtdev, &event);
59 print_event(&event);
60 }
61 }
62 mtdev_destroy(&mtdev);
63}
64
65int main(int argc, char *argv[])
66{
67 if (argc < 2) {
68 fprintf(stderr, "Usage: test <mtdev>\n");
69 return -1;
70 }
71 int fd = open(argv[1], O_RDONLY);
72 if (fd < 0) {
73 fprintf(stderr, "error: could not open file\n");
74 return -1;
75 }
76 loop_device(fd);
77 close(fd);
78 return 0;
79}