diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2010-08-19 20:03:15 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2010-08-19 20:03:15 +0200 |
| commit | a704984e03d28b4be40e12c43be1b163bc9e09e2 (patch) | |
| tree | c4720a59aa77918ffef742d7ec7ef2a7e2eda16e | |
| parent | cb2cad3b8c506a08a6bdad548a10ff47f85ef38b (diff) | |
Simplify touch machinery
To be able to model touch information uniformly across all supported
devices, the semantics of the touch attributes has to be treated
explicitly. Drop the generic touch property array and replace it
by an actual structure. In addition, the touch-engine abstraction
turned out to not be needed, so drop it as well.
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
| -rw-r--r-- | include/grail-touch.h | 65 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/grail-api.c | 46 | ||||
| -rw-r--r-- | src/grail-gestures.c | 24 | ||||
| -rw-r--r-- | src/grail-inserter.c | 4 | ||||
| -rw-r--r-- | src/touch-caps.c | 73 | ||||
| -rw-r--r-- | src/touch-dev.c | 164 | ||||
| -rw-r--r-- | src/touch-engine.c | 135 | ||||
| -rw-r--r-- | test/grail-touch.c | 29 |
9 files changed, 218 insertions, 324 deletions
diff --git a/include/grail-touch.h b/include/grail-touch.h index 9d96cc3..acfc779 100644 --- a/include/grail-touch.h +++ b/include/grail-touch.h | |||
| @@ -29,27 +29,20 @@ | |||
| 29 | #define DIM_TOUCH 32 | 29 | #define DIM_TOUCH 32 |
| 30 | #define DIM_TOUCH_BYTES ((DIM_TOUCH + 7) >> 3) | 30 | #define DIM_TOUCH_BYTES ((DIM_TOUCH + 7) >> 3) |
| 31 | 31 | ||
| 32 | typedef int touch_prop_t; | ||
| 33 | typedef __u64 touch_time_t; | 32 | typedef __u64 touch_time_t; |
| 34 | 33 | ||
| 35 | enum touch_prop_list { | ||
| 36 | TP_POS_X, | ||
| 37 | TP_POS_Y, | ||
| 38 | TP_TOUCH_MAJOR, | ||
| 39 | TP_TOUCH_MINOR, | ||
| 40 | TP_WIDTH_MAJOR, | ||
| 41 | TP_WIDTH_MINOR, | ||
| 42 | TP_ORIENTATION, | ||
| 43 | TP_PRESSURE, | ||
| 44 | DIM_TOUCH_PROP | ||
| 45 | }; | ||
| 46 | |||
| 47 | #define DIM_TOUCH_PROP_BYTES ((DIM_TOUCH_PROP + 7) >> 3) | ||
| 48 | |||
| 49 | struct touch { | 34 | struct touch { |
| 50 | int active; | 35 | int active; |
| 51 | int slot; | 36 | int slot; |
| 52 | touch_prop_t prop[DIM_TOUCH_PROP]; | 37 | int id; |
| 38 | int tool_type; | ||
| 39 | int x, y; | ||
| 40 | int touch_major; | ||
| 41 | int touch_minor; | ||
| 42 | int width_major; | ||
| 43 | int width_minor; | ||
| 44 | int orientation; | ||
| 45 | int pressure; | ||
| 53 | }; | 46 | }; |
| 54 | 47 | ||
| 55 | struct touch_frame { | 48 | struct touch_frame { |
| @@ -61,45 +54,31 @@ struct touch_frame { | |||
| 61 | struct touch touch[DIM_TOUCH]; | 54 | struct touch touch[DIM_TOUCH]; |
| 62 | }; | 55 | }; |
| 63 | 56 | ||
| 64 | struct touch_dev_caps { | 57 | struct touch_caps { |
| 65 | grail_mask_t mask[DIM_TOUCH_PROP_BYTES]; | 58 | float min_x, max_x; |
| 66 | struct input_absinfo info[DIM_TOUCH_PROP]; | 59 | float min_y, max_y; |
| 60 | float min_orient, max_orient; | ||
| 61 | float min_press, max_press; | ||
| 67 | }; | 62 | }; |
| 68 | 63 | ||
| 69 | struct touch_dev { | 64 | struct touch_dev { |
| 70 | void (*event)(struct touch_dev *dev, const struct input_event *ev); | 65 | void (*event)(struct touch_dev *dev, const struct input_event *ev); |
| 71 | void (*create)(struct touch_dev *dev, int slot, | ||
| 72 | const grail_mask_t *mask, const touch_prop_t *prop); | ||
| 73 | void (*modify)(struct touch_dev *dev, int slot, | ||
| 74 | const grail_mask_t *mask, const touch_prop_t *prop); | ||
| 75 | void (*destroy)(struct touch_dev *dev, int slot); | ||
| 76 | void (*sync)(struct touch_dev *dev, const struct input_event *syn); | 66 | void (*sync)(struct touch_dev *dev, const struct input_event *syn); |
| 77 | struct touch_dev_caps caps; | ||
| 78 | struct touch_dev_impl *impl; | ||
| 79 | void *priv; | 67 | void *priv; |
| 80 | }; | 68 | struct mtdev mtdev; |
| 81 | |||
| 82 | struct touch_engine { | ||
| 83 | void (*event)(struct touch_engine *engine, | ||
| 84 | const struct input_event *ev); | ||
| 85 | void (*sync)(struct touch_engine *engine, | ||
| 86 | const struct input_event *syn); | ||
| 87 | struct touch_frame frame; | 69 | struct touch_frame frame; |
| 88 | void *priv; | 70 | struct touch_caps caps; |
| 71 | int slot; | ||
| 72 | int state; | ||
| 89 | }; | 73 | }; |
| 90 | 74 | ||
| 75 | void touch_caps_init(struct touch_dev *dev); | ||
| 76 | float touch_angle(const struct touch_dev *dev, int orientation); | ||
| 77 | float touch_pressure(const struct touch_dev *dev, int pressure); | ||
| 78 | |||
| 91 | int touch_dev_open(struct touch_dev *dev, int fd); | 79 | int touch_dev_open(struct touch_dev *dev, int fd); |
| 92 | int touch_dev_idle(struct touch_dev *dev, int fd, int ms); | 80 | int touch_dev_idle(struct touch_dev *dev, int fd, int ms); |
| 93 | int touch_dev_pull(struct touch_dev *dev, int fd); | 81 | int touch_dev_pull(struct touch_dev *dev, int fd); |
| 94 | void touch_dev_close(struct touch_dev *dev, int fd); | 82 | void touch_dev_close(struct touch_dev *dev, int fd); |
| 95 | 83 | ||
| 96 | void touch_engine_init(struct touch_engine *engine, | ||
| 97 | void (*event)(struct touch_engine *engine, | ||
| 98 | const struct input_event *ev), | ||
| 99 | void (*sync)(struct touch_engine *engine, | ||
| 100 | const struct input_event *ev), | ||
| 101 | void *priv); | ||
| 102 | void touch_engine_attach(struct touch_engine *engine, struct touch_dev *dev); | ||
| 103 | void touch_engine_detach(struct touch_engine *engine, struct touch_dev *dev); | ||
| 104 | |||
| 105 | #endif | 84 | #endif |
diff --git a/src/Makefile.am b/src/Makefile.am index 3171edd..eec0eeb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
| @@ -9,7 +9,7 @@ libutouch_grail_la_SOURCES = \ | |||
| 9 | evbuf.h \ | 9 | evbuf.h \ |
| 10 | gebuf.h \ | 10 | gebuf.h \ |
| 11 | touch-dev.c \ | 11 | touch-dev.c \ |
| 12 | touch-engine.c \ | 12 | touch-caps.c \ |
| 13 | grail-bits.c \ | 13 | grail-bits.c \ |
| 14 | grail-inserter.c \ | 14 | grail-inserter.c \ |
| 15 | grail-inserter.h \ | 15 | grail-inserter.h \ |
diff --git a/src/grail-api.c b/src/grail-api.c index 7c054a9..afd97b9 100644 --- a/src/grail-api.c +++ b/src/grail-api.c | |||
| @@ -33,16 +33,15 @@ | |||
| 33 | 33 | ||
| 34 | struct grail_impl { | 34 | struct grail_impl { |
| 35 | struct touch_dev dev; | 35 | struct touch_dev dev; |
| 36 | struct touch_engine engine; | ||
| 37 | struct evbuf evbuf; | 36 | struct evbuf evbuf; |
| 38 | int filter_abs; | 37 | int filter_abs; |
| 39 | int hack_status; | 38 | int hack_status; |
| 40 | }; | 39 | }; |
| 41 | 40 | ||
| 42 | static void tp_event(struct touch_engine *engine, | 41 | static void tp_event(struct touch_dev *dev, |
| 43 | const struct input_event *ev) | 42 | const struct input_event *ev) |
| 44 | { | 43 | { |
| 45 | struct grail *ge = engine->priv; | 44 | struct grail *ge = dev->priv; |
| 46 | struct grail_impl *x = ge->impl; | 45 | struct grail_impl *x = ge->impl; |
| 47 | evbuf_put(&x->evbuf, ev); | 46 | evbuf_put(&x->evbuf, ev); |
| 48 | } | 47 | } |
| @@ -62,14 +61,14 @@ static int extra_filtered(const struct input_event *ev) | |||
| 62 | return 0; | 61 | return 0; |
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | static void tp_sync(struct touch_engine *engine, | 64 | static void tp_sync(struct touch_dev *dev, |
| 66 | const struct input_event *syn) | 65 | const struct input_event *syn) |
| 67 | { | 66 | { |
| 68 | struct input_event ev; | 67 | struct input_event ev; |
| 69 | struct grail *ge = engine->priv; | 68 | struct grail *ge = dev->priv; |
| 70 | struct grail_impl *x = ge->impl; | 69 | struct grail_impl *x = ge->impl; |
| 71 | struct gesture_recognizer *gru = ge->gru; | 70 | struct gesture_recognizer *gru = ge->gru; |
| 72 | struct touch_frame *frame = &engine->frame; | 71 | struct touch_frame *frame = &dev->frame; |
| 73 | grail_mask_t filtered[DIM_EV_TYPE_BYTES]; | 72 | grail_mask_t filtered[DIM_EV_TYPE_BYTES]; |
| 74 | int dofilt, hack, nevent = 0; | 73 | int dofilt, hack, nevent = 0; |
| 75 | gin_frame_begin(ge, frame); | 74 | gin_frame_begin(ge, frame); |
| @@ -120,22 +119,27 @@ int grail_open(struct grail *ge, int fd) | |||
| 120 | if (!x) | 119 | if (!x) |
| 121 | return -ENOMEM; | 120 | return -ENOMEM; |
| 122 | ge->impl = x; | 121 | ge->impl = x; |
| 123 | ret = gin_init(ge); | 122 | |
| 123 | ret = touch_dev_open(&x->dev, fd); | ||
| 124 | if (ret) | 124 | if (ret) |
| 125 | goto freemem; | 125 | goto freemem; |
| 126 | ret = touch_dev_open(&x->dev, fd); | 126 | x->dev.event = tp_event; |
| 127 | x->dev.sync = tp_sync; | ||
| 128 | x->dev.priv = ge; | ||
| 129 | |||
| 130 | ret = gin_init(ge); | ||
| 127 | if (ret) | 131 | if (ret) |
| 128 | goto freegin; | 132 | goto freedev; |
| 133 | |||
| 129 | ret = gru_init(ge); | 134 | ret = gru_init(ge); |
| 130 | if (ret) | 135 | if (ret) |
| 131 | goto freedev; | 136 | goto freegin; |
| 132 | touch_engine_init(&x->engine, tp_event, tp_sync, ge); | 137 | |
| 133 | touch_engine_attach(&x->engine, &x->dev); | ||
| 134 | return 0; | 138 | return 0; |
| 135 | freedev: | ||
| 136 | touch_dev_close(&x->dev, fd); | ||
| 137 | freegin: | 139 | freegin: |
| 138 | gin_destroy(ge); | 140 | gin_destroy(ge); |
| 141 | freedev: | ||
| 142 | touch_dev_close(&x->dev, fd); | ||
| 139 | freemem: | 143 | freemem: |
| 140 | free(x); | 144 | free(x); |
| 141 | ge->impl = 0; | 145 | ge->impl = 0; |
| @@ -146,10 +150,9 @@ void grail_close(struct grail *ge, int fd) | |||
| 146 | { | 150 | { |
| 147 | struct grail_impl *x = ge->impl; | 151 | struct grail_impl *x = ge->impl; |
| 148 | void *status; | 152 | void *status; |
| 149 | touch_engine_detach(&x->engine, &x->dev); | ||
| 150 | gru_destroy(ge); | 153 | gru_destroy(ge); |
| 151 | touch_dev_close(&x->dev, fd); | ||
| 152 | gin_destroy(ge); | 154 | gin_destroy(ge); |
| 155 | touch_dev_close(&x->dev, fd); | ||
| 153 | free(ge->impl); | 156 | free(ge->impl); |
| 154 | ge->impl = 0; | 157 | ge->impl = 0; |
| 155 | } | 158 | } |
| @@ -169,9 +172,10 @@ int grail_pull(struct grail *ge, int fd) | |||
| 169 | void grail_get_units(const struct grail *ge, | 172 | void grail_get_units(const struct grail *ge, |
| 170 | struct grail_coord *min, struct grail_coord *max) | 173 | struct grail_coord *min, struct grail_coord *max) |
| 171 | { | 174 | { |
| 172 | const struct touch_dev_caps *caps = &ge->impl->dev.caps; | 175 | const struct touch_caps *caps = &ge->impl->dev.caps; |
| 173 | min->x = caps->info[TP_POS_X].minimum; | 176 | min->x = caps->min_x; |
| 174 | min->y = caps->info[TP_POS_Y].minimum; | 177 | min->y = caps->min_y; |
| 175 | max->x = caps->info[TP_POS_X].maximum; | 178 | max->x = caps->max_x; |
| 176 | max->y = caps->info[TP_POS_Y].maximum; | 179 | max->y = caps->max_y; |
| 177 | } | 180 | } |
| 181 | |||
diff --git a/src/grail-gestures.c b/src/grail-gestures.c index 33d848c..95c1b57 100644 --- a/src/grail-gestures.c +++ b/src/grail-gestures.c | |||
| @@ -39,8 +39,8 @@ static void compute_position(float *x, float *y, | |||
| 39 | return; | 39 | return; |
| 40 | for (i = 0; i < n; i++) { | 40 | for (i = 0; i < n; i++) { |
| 41 | const struct touch *t = frame->active[i]; | 41 | const struct touch *t = frame->active[i]; |
| 42 | *x += t->prop[TP_POS_X]; | 42 | *x += t->x; |
| 43 | *y += t->prop[TP_POS_Y]; | 43 | *y += t->y; |
| 44 | } | 44 | } |
| 45 | *x /= n; | 45 | *x /= n; |
| 46 | *y /= n; | 46 | *y /= n; |
| @@ -55,8 +55,8 @@ static float compute_radius(float x, float y, | |||
| 55 | return r; | 55 | return r; |
| 56 | for (i = 0; i < n; i++) { | 56 | for (i = 0; i < n; i++) { |
| 57 | const struct touch *t = frame->active[i]; | 57 | const struct touch *t = frame->active[i]; |
| 58 | float dx = t->prop[TP_POS_X] - x; | 58 | float dx = t->x - x; |
| 59 | float dy = t->prop[TP_POS_Y] - y; | 59 | float dy = t->y - y; |
| 60 | r2 += dx * dx + dy * dy; | 60 | r2 += dx * dx + dy * dy; |
| 61 | } | 61 | } |
| 62 | r2 /= n; | 62 | r2 /= n; |
| @@ -75,10 +75,10 @@ static float compute_rotation(float x, float y, float r, | |||
| 75 | for (i = 0; i < n; i++) { | 75 | for (i = 0; i < n; i++) { |
| 76 | const struct touch *t = frame->active[i]; | 76 | const struct touch *t = frame->active[i]; |
| 77 | const struct touch *ot = &prev->touch[t->slot]; | 77 | const struct touch *ot = &prev->touch[t->slot]; |
| 78 | float dx = t->prop[TP_POS_X] - x; | 78 | float dx = t->x - x; |
| 79 | float dy = t->prop[TP_POS_Y] - y; | 79 | float dy = t->y - y; |
| 80 | float mx = t->prop[TP_POS_X] - ot->prop[TP_POS_X]; | 80 | float mx = t->x - ot->x; |
| 81 | float my = t->prop[TP_POS_Y] - ot->prop[TP_POS_Y]; | 81 | float my = t->y - ot->y; |
| 82 | darc2 += dx * my - dy * mx; | 82 | darc2 += dx * my - dy * mx; |
| 83 | } | 83 | } |
| 84 | darc2 /= n; | 84 | darc2 /= n; |
| @@ -225,13 +225,13 @@ void gru_compute_bbox(struct grail_coord *min, struct grail_coord *max, | |||
| 225 | int i; | 225 | int i; |
| 226 | if (frame->nactive < 1) | 226 | if (frame->nactive < 1) |
| 227 | return; | 227 | return; |
| 228 | x = frame->active[0]->prop[TP_POS_X]; | 228 | x = frame->active[0]->x; |
| 229 | y = frame->active[0]->prop[TP_POS_Y]; | 229 | y = frame->active[0]->y; |
| 230 | min->x = max->x = x; | 230 | min->x = max->x = x; |
| 231 | min->y = max->y = y; | 231 | min->y = max->y = y; |
| 232 | for (i = 1; i < frame->nactive; i++) { | 232 | for (i = 1; i < frame->nactive; i++) { |
| 233 | x = frame->active[i]->prop[TP_POS_X]; | 233 | x = frame->active[i]->x; |
| 234 | y = frame->active[i]->prop[TP_POS_Y]; | 234 | y = frame->active[i]->y; |
| 235 | if (x < min->x) | 235 | if (x < min->x) |
| 236 | min->x = x; | 236 | min->x = x; |
| 237 | if (y < min->y) | 237 | if (y < min->y) |
diff --git a/src/grail-inserter.c b/src/grail-inserter.c index 8b0296e..9769d94 100644 --- a/src/grail-inserter.c +++ b/src/grail-inserter.c | |||
| @@ -187,8 +187,8 @@ static void setup_new_gestures(struct grail *ge, | |||
| 187 | int ncoord = 0; | 187 | int ncoord = 0; |
| 188 | grail_mask_foreach(i, span, sizeof(span)) { | 188 | grail_mask_foreach(i, span, sizeof(span)) { |
| 189 | const struct touch *t = &frame->touch[i]; | 189 | const struct touch *t = &frame->touch[i]; |
| 190 | coord[ncoord].x = t->prop[TP_POS_X]; | 190 | coord[ncoord].x = t->x; |
| 191 | coord[ncoord].y = t->prop[TP_POS_Y]; | 191 | coord[ncoord].y = t->y; |
| 192 | transform_pos(gin, &coord[ncoord]); | 192 | transform_pos(gin, &coord[ncoord]); |
| 193 | ncoord++; | 193 | ncoord++; |
| 194 | } | 194 | } |
diff --git a/src/touch-caps.c b/src/touch-caps.c new file mode 100644 index 0000000..50306e0 --- /dev/null +++ b/src/touch-caps.c | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * grail - Gesture Recognition And Instantiation Library | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Canonical Ltd. | ||
| 6 | * Copyright (C) 2010 Henrik Rydberg <rydberg@bitmath.org> | ||
| 7 | * | ||
| 8 | * This program is free software: you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation, either version 3 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but | ||
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | * | ||
| 21 | ****************************************************************************/ | ||
| 22 | |||
| 23 | #include <grail-touch.h> | ||
| 24 | #include <math.h> | ||
| 25 | |||
| 26 | /* see mtdev-mapping.h */ | ||
| 27 | #define MTDEV_POSITION_X 5 | ||
| 28 | #define MTDEV_POSITION_Y 6 | ||
| 29 | #define MTDEV_ORIENTATION 4 | ||
| 30 | #define MTDEV_PRESSURE 10 | ||
| 31 | |||
| 32 | void touch_caps_init(struct touch_dev *dev) | ||
| 33 | { | ||
| 34 | const struct mtdev_caps *mt = &dev->mtdev.caps; | ||
| 35 | struct touch_caps *caps = &dev->caps; | ||
| 36 | caps->min_x = mt->abs[MTDEV_POSITION_X].minimum; | ||
| 37 | caps->max_x = mt->abs[MTDEV_POSITION_X].maximum; | ||
| 38 | caps->min_y = mt->abs[MTDEV_POSITION_Y].minimum; | ||
| 39 | caps->max_y = mt->abs[MTDEV_POSITION_Y].maximum; | ||
| 40 | caps->min_orient = mt->abs[MTDEV_ORIENTATION].minimum; | ||
| 41 | caps->max_orient = mt->abs[MTDEV_ORIENTATION].maximum; | ||
| 42 | caps->min_press = mt->abs[MTDEV_PRESSURE].minimum; | ||
| 43 | caps->max_press = mt->abs[MTDEV_PRESSURE].maximum; | ||
| 44 | if (caps->min_x == caps->max_x) { | ||
| 45 | caps->min_x = 0; | ||
| 46 | caps->max_x = 1024; | ||
| 47 | } | ||
| 48 | if (caps->min_y == caps->max_y) { | ||
| 49 | caps->min_y = 0; | ||
| 50 | caps->max_y = 768; | ||
| 51 | } | ||
| 52 | if (caps->min_orient == caps->max_orient) { | ||
| 53 | caps->min_orient = 0; | ||
| 54 | caps->max_orient = 1; | ||
| 55 | } | ||
| 56 | if (caps->min_press == caps->max_press) { | ||
| 57 | caps->min_press = 0; | ||
| 58 | caps->max_press = 256; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | float touch_angle(const struct touch_dev *dev, int orient) | ||
| 63 | { | ||
| 64 | const struct touch_caps *caps = &dev->caps; | ||
| 65 | double div = caps->max_orient - caps->min_orient; | ||
| 66 | return ((orient - caps->min_orient) / div - 0.5) * M_PI; | ||
| 67 | } | ||
| 68 | |||
| 69 | float touch_pressure(const struct touch_dev *dev, int press) | ||
| 70 | { | ||
| 71 | const struct touch_caps *caps = &dev->caps; | ||
| 72 | return (press - caps->min_press) / (caps->max_press - caps->min_press); | ||
| 73 | } | ||
diff --git a/src/touch-dev.c b/src/touch-dev.c index b88cb8d..287076a 100644 --- a/src/touch-dev.c +++ b/src/touch-dev.c | |||
| @@ -25,128 +25,97 @@ | |||
| 25 | #include <string.h> | 25 | #include <string.h> |
| 26 | #include <errno.h> | 26 | #include <errno.h> |
| 27 | 27 | ||
| 28 | /* from mtdev-mapping.h */ | 28 | #define SET_PROP(name, value) \ |
| 29 | #define MT_TOUCH_MAJOR 0 | 29 | if (t->name != value) { \ |
| 30 | #define MT_TOUCH_MINOR 1 | 30 | t->name = value; \ |
| 31 | #define MT_WIDTH_MAJOR 2 | 31 | frame->nmodify++; \ |
| 32 | #define MT_WIDTH_MINOR 3 | 32 | } |
| 33 | #define MT_ORIENTATION 4 | ||
| 34 | #define MT_POSITION_X 5 | ||
| 35 | #define MT_POSITION_Y 6 | ||
| 36 | #define MT_TRACKING_ID 9 | ||
| 37 | #define MT_PRESSURE 10 | ||
| 38 | |||
| 39 | struct touch_dev_impl { | ||
| 40 | int id[DIM_TOUCH]; | ||
| 41 | int slot; | ||
| 42 | int status; | ||
| 43 | grail_mask_t mask[DIM_TOUCH_BYTES]; | ||
| 44 | touch_prop_t prop[DIM_TOUCH_PROP]; | ||
| 45 | struct mtdev mtdev; | ||
| 46 | }; | ||
| 47 | |||
| 48 | static inline void set_info(struct touch_dev_caps *caps, int code, | ||
| 49 | const struct input_absinfo *info) | ||
| 50 | { | ||
| 51 | grail_mask_set(caps->mask, code); | ||
| 52 | caps->info[code] = *info; | ||
| 53 | } | ||
| 54 | |||
| 55 | static void set_caps(struct touch_dev_caps *caps, | ||
| 56 | const struct mtdev_caps *mtcaps) | ||
| 57 | { | ||
| 58 | if (mtcaps->has_abs[MT_POSITION_X]) | ||
| 59 | set_info(caps, TP_POS_X, &mtcaps->abs[MT_POSITION_X]); | ||
| 60 | if (mtcaps->has_abs[MT_POSITION_Y]) | ||
| 61 | set_info(caps, TP_POS_Y, &mtcaps->abs[MT_POSITION_Y]); | ||
| 62 | if (mtcaps->has_abs[MT_TOUCH_MAJOR]) | ||
| 63 | set_info(caps, TP_TOUCH_MAJOR, &mtcaps->abs[MT_TOUCH_MAJOR]); | ||
| 64 | if (mtcaps->has_abs[MT_TOUCH_MINOR]) | ||
| 65 | set_info(caps, TP_TOUCH_MINOR, &mtcaps->abs[MT_TOUCH_MINOR]); | ||
| 66 | if (mtcaps->has_abs[MT_WIDTH_MAJOR]) | ||
| 67 | set_info(caps, TP_WIDTH_MAJOR, &mtcaps->abs[MT_WIDTH_MAJOR]); | ||
| 68 | if (mtcaps->has_abs[MT_WIDTH_MINOR]) | ||
| 69 | set_info(caps, TP_WIDTH_MINOR, &mtcaps->abs[MT_WIDTH_MINOR]); | ||
| 70 | if (mtcaps->has_abs[MT_ORIENTATION]) | ||
| 71 | set_info(caps, TP_ORIENTATION, &mtcaps->abs[MT_ORIENTATION]); | ||
| 72 | if (mtcaps->has_abs[MT_PRESSURE]) | ||
| 73 | set_info(caps, TP_PRESSURE, &mtcaps->abs[MT_PRESSURE]); | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline void set_prop(struct touch_dev_impl *x, int code, int value) | ||
| 77 | { | ||
| 78 | grail_mask_set(x->mask, code); | ||
| 79 | x->prop[code] = value; | ||
| 80 | } | ||
| 81 | 33 | ||
| 82 | static void finish_touch(struct touch_dev *dev, | 34 | static void finish_touch(struct touch_dev *dev, struct touch_frame *frame) |
| 83 | struct touch_dev_impl *x) | ||
| 84 | { | 35 | { |
| 85 | if (x->status < 0 && dev->destroy) | 36 | struct touch *t = &frame->touch[dev->slot]; |
| 86 | dev->destroy(dev, x->slot); | 37 | if (dev->state > 0) { |
| 87 | if (x->status > 0 && dev->create) | 38 | t->active = 1; |
| 88 | dev->create(dev, x->slot, x->mask, x->prop); | 39 | grail_mask_set(frame->touches, dev->slot); |
| 89 | if (x->status == 0 && dev->modify && | 40 | frame->ncreate++; |
| 90 | grail_mask_count(x->mask, sizeof(x->mask))) | 41 | } |
| 91 | dev->modify(dev, x->slot, x->mask, x->prop); | 42 | if (dev->state < 0) { |
| 92 | x->status = 0; | 43 | t->active = 0; |
| 93 | memset(x->mask, 0, sizeof(x->mask)); | 44 | grail_mask_clear(frame->touches, dev->slot); |
| 45 | frame->ndestroy++; | ||
| 46 | } | ||
| 47 | dev->state = 0; | ||
| 94 | } | 48 | } |
| 95 | 49 | ||
| 96 | static void finish_packet(struct touch_dev *dev, | 50 | static void finish_packet(struct touch_dev *dev, |
| 97 | const struct input_event *syn) | 51 | const struct input_event *syn) |
| 98 | { | 52 | { |
| 99 | finish_touch(dev, dev->impl); | 53 | static const touch_time_t ms = 1000; |
| 54 | struct touch_frame *frame = &dev->frame; | ||
| 55 | int i, nslot = 0; | ||
| 56 | finish_touch(dev, frame); | ||
| 57 | grail_mask_foreach(i, frame->touches, DIM_TOUCH_BYTES) | ||
| 58 | frame->active[nslot++] = &frame->touch[i]; | ||
| 59 | frame->nactive = nslot; | ||
| 60 | frame->time = syn->time.tv_usec / ms + syn->time.tv_sec * ms; | ||
| 100 | if (dev->sync) | 61 | if (dev->sync) |
| 101 | dev->sync(dev, syn); | 62 | dev->sync(dev, syn); |
| 63 | frame->ncreate = 0; | ||
| 64 | frame->nmodify = 0; | ||
| 65 | frame->ndestroy = 0; | ||
| 102 | } | 66 | } |
| 103 | 67 | ||
| 104 | static int handle_abs_event(struct touch_dev *dev, | 68 | static int handle_abs_event(struct touch_dev *dev, |
| 105 | const struct input_event *ev) | 69 | const struct input_event *ev) |
| 106 | { | 70 | { |
| 107 | struct touch_dev_impl *x = dev->impl; | 71 | struct touch_frame *frame = &dev->frame; |
| 72 | struct touch *t = &frame->touch[dev->slot]; | ||
| 108 | switch (ev->code) { | 73 | switch (ev->code) { |
| 109 | case ABS_MT_SLOT: | 74 | case ABS_MT_SLOT: |
| 110 | if (ev->value >= 0 && ev->value < DIM_TOUCH) { | 75 | if (ev->value >= 0 && ev->value < DIM_TOUCH) { |
| 111 | if (x->slot != ev->value) | 76 | if (dev->slot != ev->value) |
| 112 | finish_touch(dev, x); | 77 | finish_touch(dev, frame); |
| 113 | x->slot = ev->value; | 78 | dev->slot = ev->value; |
| 79 | t = &frame->touch[dev->slot]; | ||
| 114 | } | 80 | } |
| 115 | return 1; | 81 | return 1; |
| 116 | case ABS_MT_POSITION_X: | 82 | case ABS_MT_POSITION_X: |
| 117 | set_prop(x, TP_POS_X, ev->value); | 83 | SET_PROP(x, ev->value); |
| 118 | return 1; | 84 | return 1; |
| 119 | case ABS_MT_POSITION_Y: | 85 | case ABS_MT_POSITION_Y: |
| 120 | set_prop(x, TP_POS_Y, ev->value); | 86 | SET_PROP(y, ev->value); |
| 121 | return 1; | 87 | return 1; |
| 122 | case ABS_MT_TOUCH_MAJOR: | 88 | case ABS_MT_TOUCH_MAJOR: |
| 123 | set_prop(x, TP_TOUCH_MAJOR, ev->value); | 89 | SET_PROP(touch_major, ev->value); |
| 124 | return 1; | 90 | return 1; |
| 125 | case ABS_MT_TOUCH_MINOR: | 91 | case ABS_MT_TOUCH_MINOR: |
| 126 | set_prop(x, TP_TOUCH_MINOR, ev->value); | 92 | SET_PROP(touch_minor, ev->value); |
| 127 | return 1; | 93 | return 1; |
| 128 | case ABS_MT_WIDTH_MAJOR: | 94 | case ABS_MT_WIDTH_MAJOR: |
| 129 | set_prop(x, TP_WIDTH_MAJOR, ev->value); | 95 | SET_PROP(width_major, ev->value); |
| 130 | return 1; | 96 | return 1; |
| 131 | case ABS_MT_WIDTH_MINOR: | 97 | case ABS_MT_WIDTH_MINOR: |
| 132 | set_prop(x, TP_WIDTH_MINOR, ev->value); | 98 | SET_PROP(width_minor, ev->value); |
| 133 | return 1; | 99 | return 1; |
| 134 | case ABS_MT_ORIENTATION: | 100 | case ABS_MT_ORIENTATION: |
| 135 | set_prop(x, TP_ORIENTATION, ev->value); | 101 | SET_PROP(orientation, ev->value); |
| 136 | return 1; | 102 | return 1; |
| 137 | case ABS_MT_PRESSURE: | 103 | case ABS_MT_PRESSURE: |
| 138 | set_prop(x, TP_PRESSURE, ev->value); | 104 | SET_PROP(pressure, ev->value); |
| 105 | return 1; | ||
| 106 | case ABS_MT_TOOL_TYPE: | ||
| 107 | SET_PROP(tool_type, ev->value); | ||
| 139 | return 1; | 108 | return 1; |
| 140 | case ABS_MT_TRACKING_ID: | 109 | case ABS_MT_TRACKING_ID: |
| 141 | if (x->id[x->slot] != ev->value) { | 110 | if (t->id != ev->value) { |
| 142 | if (x->id[x->slot] != MT_ID_NULL) { | 111 | if (t->id != MT_ID_NULL) { |
| 143 | x->status = -1; | 112 | dev->state = -1; |
| 144 | finish_touch(dev, x); | 113 | finish_touch(dev, frame); |
| 145 | } | 114 | } |
| 146 | if (ev->value != MT_ID_NULL) | 115 | if (ev->value != MT_ID_NULL) |
| 147 | x->status = 1; | 116 | dev->state = 1; |
| 117 | t->id = ev->value; | ||
| 148 | } | 118 | } |
| 149 | x->id[x->slot] = ev->value; | ||
| 150 | return 1; | 119 | return 1; |
| 151 | default: | 120 | default: |
| 152 | return 0; | 121 | return 0; |
| @@ -155,38 +124,35 @@ static int handle_abs_event(struct touch_dev *dev, | |||
| 155 | 124 | ||
| 156 | int touch_dev_open(struct touch_dev *dev, int fd) | 125 | int touch_dev_open(struct touch_dev *dev, int fd) |
| 157 | { | 126 | { |
| 158 | struct touch_dev_impl *x; | 127 | struct touch_frame *frame = &dev->frame; |
| 159 | int ret, i; | 128 | int ret, i; |
| 160 | memset(dev, 0, sizeof(*dev)); | 129 | memset(dev, 0, sizeof(*dev)); |
| 161 | x = calloc(1, sizeof(*x)); | 130 | for (i = 0; i < DIM_TOUCH; i++) { |
| 162 | if (!x) | 131 | struct touch *t = &frame->touch[i]; |
| 163 | return -ENOMEM; | 132 | t->slot = i; |
| 164 | ret = mtdev_open(&x->mtdev, fd); | 133 | t->id = MT_ID_NULL; |
| 165 | if (!ret && !x->mtdev.caps.has_mtdata) | 134 | } |
| 135 | ret = mtdev_open(&dev->mtdev, fd); | ||
| 136 | if (!ret && !dev->mtdev.caps.has_mtdata) | ||
| 166 | ret = -ENODEV; | 137 | ret = -ENODEV; |
| 167 | if (ret) | 138 | if (ret) |
| 168 | goto error; | 139 | goto error; |
| 169 | for (i = 0; i < DIM_TOUCH; i++) | 140 | touch_caps_init(dev); |
| 170 | x->id[i] = MT_ID_NULL; | ||
| 171 | set_caps(&dev->caps, &x->mtdev.caps); | ||
| 172 | dev->impl = x; | ||
| 173 | return 0; | 141 | return 0; |
| 174 | error: | 142 | error: |
| 175 | free(x); | ||
| 176 | return ret; | 143 | return ret; |
| 177 | } | 144 | } |
| 178 | 145 | ||
| 179 | int touch_dev_idle(struct touch_dev *dev, int fd, int ms) | 146 | int touch_dev_idle(struct touch_dev *dev, int fd, int ms) |
| 180 | { | 147 | { |
| 181 | return mtdev_idle(&dev->impl->mtdev, fd, ms); | 148 | return mtdev_idle(&dev->mtdev, fd, ms); |
| 182 | } | 149 | } |
| 183 | 150 | ||
| 184 | int touch_dev_pull(struct touch_dev *dev, int fd) | 151 | int touch_dev_pull(struct touch_dev *dev, int fd) |
| 185 | { | 152 | { |
| 186 | struct touch_dev_impl *x = dev->impl; | ||
| 187 | struct input_event ev; | 153 | struct input_event ev; |
| 188 | int ret, count = 0, consumed; | 154 | int ret, count = 0, consumed; |
| 189 | while ((ret = mtdev_get(&x->mtdev, fd, &ev, 1)) > 0) { | 155 | while ((ret = mtdev_get(&dev->mtdev, fd, &ev, 1)) > 0) { |
| 190 | consumed = 0; | 156 | consumed = 0; |
| 191 | if (ev.type == EV_SYN) { | 157 | if (ev.type == EV_SYN) { |
| 192 | if (ev.code == SYN_REPORT) | 158 | if (ev.code == SYN_REPORT) |
| @@ -204,8 +170,6 @@ int touch_dev_pull(struct touch_dev *dev, int fd) | |||
| 204 | 170 | ||
| 205 | void touch_dev_close(struct touch_dev *dev, int fd) | 171 | void touch_dev_close(struct touch_dev *dev, int fd) |
| 206 | { | 172 | { |
| 207 | struct touch_dev_impl *x = dev->impl; | 173 | mtdev_close(&dev->mtdev); |
| 208 | mtdev_close(&x->mtdev); | ||
| 209 | free(x); | ||
| 210 | memset(dev, 0, sizeof(*dev)); | 174 | memset(dev, 0, sizeof(*dev)); |
| 211 | } | 175 | } |
diff --git a/src/touch-engine.c b/src/touch-engine.c deleted file mode 100644 index d13785d..0000000 --- a/src/touch-engine.c +++ /dev/null | |||
| @@ -1,135 +0,0 @@ | |||
| 1 | /***************************************************************************** | ||
| 2 | * | ||
| 3 | * grail - Gesture Recognition And Instantiation Library | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Canonical Ltd. | ||
| 6 | * Copyright (C) 2010 Henrik Rydberg <rydberg@bitmath.org> | ||
| 7 | * | ||
| 8 | * This program is free software: you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation, either version 3 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but | ||
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | * | ||
| 21 | ****************************************************************************/ | ||
| 22 | |||
| 23 | #include <grail-touch.h> | ||
| 24 | #include <string.h> | ||
| 25 | |||
| 26 | static void tp_event(struct touch_dev *dev, const struct input_event *ev) | ||
| 27 | { | ||
| 28 | struct touch_engine *engine = dev->priv; | ||
| 29 | if (engine && engine->event) | ||
| 30 | engine->event(engine, ev); | ||
| 31 | } | ||
| 32 | |||
| 33 | static void update_frame(struct touch_frame *frame, | ||
| 34 | int slot, int active, | ||
| 35 | const grail_mask_t *mask, | ||
| 36 | const touch_prop_t *prop) | ||
| 37 | { | ||
| 38 | struct touch *t = &frame->touch[slot]; | ||
| 39 | int i; | ||
| 40 | t->active = active; | ||
| 41 | grail_mask_modify(frame->touches, slot, active); | ||
| 42 | if (mask && prop) | ||
| 43 | grail_mask_foreach(i, mask, DIM_TOUCH_PROP_BYTES) | ||
| 44 | t->prop[i] = prop[i]; | ||
| 45 | } | ||
| 46 | |||
| 47 | static void finalize_frame(struct touch_frame *frame, touch_time_t time) | ||
| 48 | { | ||
| 49 | int i, nslot = 0; | ||
| 50 | grail_mask_foreach(i, frame->touches, DIM_TOUCH_BYTES) | ||
| 51 | frame->active[nslot++] = &frame->touch[i]; | ||
| 52 | frame->nactive = nslot; | ||
| 53 | frame->time = time; | ||
| 54 | } | ||
| 55 | |||
| 56 | static void tp_create(struct touch_dev *dev, int slot, | ||
| 57 | const grail_mask_t *mask, const touch_prop_t *prop) | ||
| 58 | { | ||
| 59 | struct touch_engine *engine = dev->priv; | ||
| 60 | if (engine) { | ||
| 61 | engine->frame.ncreate++; | ||
| 62 | update_frame(&engine->frame, slot, 1, mask, prop); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | static void tp_modify(struct touch_dev *dev, int slot, | ||
| 67 | const grail_mask_t *mask, const touch_prop_t *prop) | ||
| 68 | { | ||
| 69 | struct touch_engine *engine = dev->priv; | ||
| 70 | if (engine) { | ||
| 71 | engine->frame.nmodify++; | ||
| 72 | update_frame(&engine->frame, slot, 1, mask, prop); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | static void tp_destroy(struct touch_dev *dev, int slot) | ||
| 77 | { | ||
| 78 | struct touch_engine *engine = dev->priv; | ||
| 79 | if (engine) { | ||
| 80 | engine->frame.ndestroy++; | ||
| 81 | update_frame(&engine->frame, slot, 0, 0, 0); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | static void tp_sync(struct touch_dev *dev, const struct input_event *syn) | ||
| 86 | { | ||
| 87 | static const touch_time_t ms = 1000; | ||
| 88 | struct touch_engine *engine = dev->priv; | ||
| 89 | touch_time_t time = syn->time.tv_usec / ms + syn->time.tv_sec * ms; | ||
| 90 | if (engine) { | ||
| 91 | finalize_frame(&engine->frame, time); | ||
| 92 | if (engine->sync) | ||
| 93 | engine->sync(engine, syn); | ||
| 94 | engine->frame.ncreate = 0; | ||
| 95 | engine->frame.nmodify = 0; | ||
| 96 | engine->frame.ndestroy = 0; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | void touch_engine_init(struct touch_engine *engine, | ||
| 101 | void (*event)(struct touch_engine *engine, | ||
| 102 | const struct input_event *ev), | ||
| 103 | void (*sync)(struct touch_engine *engine, | ||
| 104 | const struct input_event *ev), | ||
| 105 | void *priv) | ||
| 106 | { | ||
| 107 | int i; | ||
| 108 | memset(engine, 0, sizeof(*engine)); | ||
| 109 | for (i = 0; i < DIM_TOUCH; i++) | ||
| 110 | engine->frame.touch[i].slot = i; | ||
| 111 | engine->event = event; | ||
| 112 | engine->sync = sync; | ||
| 113 | engine->priv = priv; | ||
| 114 | } | ||
| 115 | |||
| 116 | void touch_engine_attach(struct touch_engine *engine, struct touch_dev *dev) | ||
| 117 | { | ||
| 118 | dev->priv = engine; | ||
| 119 | dev->event = tp_event; | ||
| 120 | dev->create = tp_create; | ||
| 121 | dev->modify = tp_modify; | ||
| 122 | dev->destroy = tp_destroy; | ||
| 123 | dev->sync = tp_sync; | ||
| 124 | } | ||
| 125 | |||
| 126 | void touch_engine_detach(struct touch_engine *engine, struct touch_dev *dev) | ||
| 127 | { | ||
| 128 | dev->sync = 0; | ||
| 129 | dev->destroy = 0; | ||
| 130 | dev->modify = 0; | ||
| 131 | dev->create = 0; | ||
| 132 | dev->event = 0; | ||
| 133 | dev->priv = 0; | ||
| 134 | } | ||
| 135 | |||
diff --git a/test/grail-touch.c b/test/grail-touch.c index bed31c3..c5ecae3 100644 --- a/test/grail-touch.c +++ b/test/grail-touch.c | |||
| @@ -28,22 +28,33 @@ | |||
| 28 | #include <unistd.h> | 28 | #include <unistd.h> |
| 29 | #include <fcntl.h> | 29 | #include <fcntl.h> |
| 30 | 30 | ||
| 31 | static void tp_event(struct touch_engine *engine, | 31 | static void tp_event(struct touch_dev *dev, |
| 32 | const struct input_event *ev) | 32 | const struct input_event *ev) |
| 33 | { | 33 | { |
| 34 | fprintf(stderr, "event %d 0x%x %d\n", ev->type, ev->code, ev->value); | 34 | fprintf(stderr, "event %d 0x%x %d\n", ev->type, ev->code, ev->value); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | static void tp_sync(struct touch_engine *engine, | 37 | static void tp_sync(struct touch_dev *dev, |
| 38 | const struct input_event *syn) | 38 | const struct input_event *syn) |
| 39 | { | 39 | { |
| 40 | struct touch_frame *frame = &engine->frame; | 40 | struct touch_frame *frame = &dev->frame; |
| 41 | int i, j; | 41 | int i, j; |
| 42 | for (i = 0; i < frame->nactive; i++) { | 42 | for (i = 0; i < frame->nactive; i++) { |
| 43 | struct touch *t = frame->active[i]; | 43 | struct touch *t = frame->active[i]; |
| 44 | fprintf(stderr, "touch %d %d\n", i, t->slot); | 44 | fprintf(stderr, "touch %d\n", i); |
| 45 | for (j = 0; j < DIM_TOUCH_PROP; j++) | 45 | fprintf(stderr, " slot: %d\n", t->slot); |
| 46 | fprintf(stderr, " %d\n", t->prop[j]); | 46 | fprintf(stderr, " id: %d\n", t->id); |
| 47 | fprintf(stderr, " tool_type: %d\n", t->tool_type); | ||
| 48 | fprintf(stderr, " x: %d\n", t->x); | ||
| 49 | fprintf(stderr, " y: %d\n", t->y); | ||
| 50 | fprintf(stderr, " touch_major: %d\n", t->touch_major); | ||
| 51 | fprintf(stderr, " touch_minor: %d\n", t->touch_minor); | ||
| 52 | fprintf(stderr, " width_major: %d\n", t->width_major); | ||
| 53 | fprintf(stderr, " width_minor: %d\n", t->width_minor); | ||
| 54 | fprintf(stderr, " angle: %f\n", | ||
| 55 | touch_angle(dev, t->orientation)); | ||
| 56 | fprintf(stderr, " pressure: %f\n", | ||
| 57 | touch_pressure(dev, t->pressure)); | ||
| 47 | } | 58 | } |
| 48 | fprintf(stderr, "sync %d %lld %d %d\n", | 59 | fprintf(stderr, "sync %d %lld %d %d\n", |
| 49 | frame->nactive, frame->time, frame->ncreate, frame->ndestroy); | 60 | frame->nactive, frame->time, frame->ncreate, frame->ndestroy); |
| @@ -51,12 +62,10 @@ static void tp_sync(struct touch_engine *engine, | |||
| 51 | 62 | ||
| 52 | static void loop_device(struct touch_dev *dev, int fd) | 63 | static void loop_device(struct touch_dev *dev, int fd) |
| 53 | { | 64 | { |
| 54 | struct touch_engine engine; | 65 | dev->event = tp_event; |
| 55 | touch_engine_init(&engine, tp_event, tp_sync, 0); | 66 | dev->sync = tp_sync; |
| 56 | touch_engine_attach(&engine, dev); | ||
| 57 | while (!touch_dev_idle(dev, fd, 5000)) | 67 | while (!touch_dev_idle(dev, fd, 5000)) |
| 58 | touch_dev_pull(dev, fd); | 68 | touch_dev_pull(dev, fd); |
| 59 | touch_engine_detach(&engine, dev); | ||
| 60 | } | 69 | } |
| 61 | 70 | ||
| 62 | int main(int argc, char *argv[]) | 71 | int main(int argc, char *argv[]) |
