From 7db9d8d76ff61c4802ac15559dcea6fa54ff567a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Fri, 6 Aug 2010 21:11:17 +0200 Subject: initial bzr repo at rev 27 --- src/Makefile.am | 36 +++++++ src/evbuf.h | 64 +++++++++++ src/gebuf.h | 70 ++++++++++++ src/gestures-combo.c | 71 +++++++++++++ src/gestures-pan.c | 68 ++++++++++++ src/gestures-pinch.c | 68 ++++++++++++ src/gestures-rotate.c | 66 ++++++++++++ src/gestures-tapping.c | 101 ++++++++++++++++++ src/grail-api.c | 115 ++++++++++++++++++++ src/grail-bits.c | 94 +++++++++++++++++ src/grail-gestures.c | 188 +++++++++++++++++++++++++++++++++ src/grail-gestures.h | 77 ++++++++++++++ src/grail-inserter.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++ src/grail-inserter.h | 71 +++++++++++++ src/grail-recognizer.c | 58 ++++++++++ src/grail-recognizer.h | 44 ++++++++ src/touch-dev.c | 218 ++++++++++++++++++++++++++++++++++++++ src/touch-engine.c | 137 ++++++++++++++++++++++++ 18 files changed, 1828 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/evbuf.h create mode 100644 src/gebuf.h create mode 100644 src/gestures-combo.c create mode 100644 src/gestures-pan.c create mode 100644 src/gestures-pinch.c create mode 100644 src/gestures-rotate.c create mode 100644 src/gestures-tapping.c create mode 100644 src/grail-api.c create mode 100644 src/grail-bits.c create mode 100644 src/grail-gestures.c create mode 100644 src/grail-gestures.h create mode 100644 src/grail-inserter.c create mode 100644 src/grail-inserter.h create mode 100644 src/grail-recognizer.c create mode 100644 src/grail-recognizer.h create mode 100644 src/touch-dev.c create mode 100644 src/touch-engine.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..7dc3ac2 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,36 @@ +lib_LTLIBRARIES = libgrail.la + +libgrail_la_LDFLAGS = \ + -version-info @LIB_VERSION@ \ + -lm \ + $(MTDEV_LIBS) + +libgrail_la_SOURCES = \ + evbuf.h \ + gebuf.h \ + touch-dev.c \ + touch-engine.c \ + grail-bits.c \ + grail-inserter.c \ + grail-inserter.h \ + grail-gestures.c \ + grail-gestures.h \ + gestures-pan.c \ + gestures-pinch.c \ + gestures-rotate.c \ + gestures-combo.c \ + gestures-tapping.c \ + grail-recognizer.c \ + grail-recognizer.h \ + grail-api.c + +AM_CFLAGS = $(CWARNFLAGS) + +INCLUDES = -I$(top_srcdir)/include/ + +libgrailincludedir = $(includedir) +libgrailinclude_HEADERS = \ + $(top_srcdir)/include/grail-bits.h \ + $(top_srcdir)/include/grail-types.h \ + $(top_srcdir)/include/grail-touch.h \ + $(top_srcdir)/include/grail.h diff --git a/src/evbuf.h b/src/evbuf.h new file mode 100644 index 0000000..dc5ad1e --- /dev/null +++ b/src/evbuf.h @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * mtdev - Multitouch Protocol Translation Library (MIT license) + * + * Copyright (C) 2010 Henrik Rydberg + * Copyright (C) 2010 Canonical Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************/ + +#ifndef GRAIL_EVBUF_H +#define GRAIL_EVBUF_H + +#define DIM_EVENTS 512 + +struct evbuf { + int head; + int tail; + struct input_event buffer[DIM_EVENTS]; +}; + +static inline void evbuf_clear(struct evbuf *evbuf) +{ + evbuf->head = evbuf->tail = 0; +} + +static inline int evbuf_empty(const struct evbuf *evbuf) +{ + return evbuf->head == evbuf->tail; +} + +static inline void evbuf_put(struct evbuf *evbuf, + const struct input_event *ev) +{ + evbuf->buffer[evbuf->head++] = *ev; + evbuf->head &= DIM_EVENTS - 1; +} + +static inline void evbuf_get(struct evbuf *evbuf, + struct input_event *ev) +{ + *ev = evbuf->buffer[evbuf->tail++]; + evbuf->tail &= DIM_EVENTS - 1; +} + +#endif diff --git a/src/gebuf.h b/src/gebuf.h new file mode 100644 index 0000000..3f71ab5 --- /dev/null +++ b/src/gebuf.h @@ -0,0 +1,70 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#ifndef _GRAIL_GESTURE_BUFFER_H +#define _GRAIL_GESTURE_BUFFER_H + +#include + +#define DIM_GESTURE_EVENTS 512 + +struct gesture_event { + int ntouch; + int nprop; + touch_time_t time; + struct grail_coord pos; + grail_prop_t prop[DIM_GRAIL_PROP]; +}; + +struct gebuf { + int head; + int tail; + struct gesture_event buffer[DIM_GESTURE_EVENTS]; +}; + +static inline void gebuf_clear(struct gebuf *gebuf) +{ + gebuf->head = gebuf->tail = 0; +} + +static inline int gebuf_empty(const struct gebuf *gebuf) +{ + return gebuf->head == gebuf->tail; +} + +static inline void gebuf_put(struct gebuf *gebuf, + const struct gesture_event *ev) +{ + gebuf->buffer[gebuf->head++] = *ev; + gebuf->head &= DIM_GESTURE_EVENTS - 1; +} + +static inline void gebuf_get(struct gebuf *gebuf, + struct gesture_event *ev) +{ + *ev = gebuf->buffer[gebuf->tail++]; + gebuf->tail &= DIM_GESTURE_EVENTS - 1; +} + +#endif diff --git a/src/gestures-combo.c b/src/gestures-combo.c new file mode 100644 index 0000000..7b8dbd5 --- /dev/null +++ b/src/gestures-combo.c @@ -0,0 +1,71 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include +#include + +static const int getype[DIM_TOUCH + 1] = { + 0, + 0, + GRAIL_TYPE_COMBO2, + GRAIL_TYPE_COMBO3, + GRAIL_TYPE_COMBO4, + GRAIL_TYPE_COMBO5, +}; + +int gru_combo(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct combo_model *state = &gru->combo; + struct move_model *move = &gru->move; + grail_prop_t prop[DIM_GRAIL_PROP]; + if (!move->multi) { + if (state->active) { + gru_end(ge, state->gid, move); + state->active = 0; + } + return 0; + } + if (!move->x.tickle && !move->y.tickle && + !move->r.tickle && !move->a.tickle) + return 0; + if (!state->active) { + int type = getype[move->ntouch]; + if (!type) + return 0; + state->gid = gin_gid_begin(ge, type, PRIO_GESTURE, frame); + state->active = 1; + } + if (!move->x.active && !move->y.active && + !move->r.active && !move->a.active) + return 0; + prop[0] = move->x.active ? move->x.delta : 0; + prop[1] = move->y.active ? move->y.delta : 0; + prop[2] = move->r.active ? move->r.delta : 0; + prop[3] = move->a.active ? move->a.delta : 0; + gru_event(ge, state->gid, move, prop, 4); + return 1; +} diff --git a/src/gestures-pan.c b/src/gestures-pan.c new file mode 100644 index 0000000..3094007 --- /dev/null +++ b/src/gestures-pan.c @@ -0,0 +1,68 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include +#include + +static const int getype[DIM_TOUCH + 1] = { + 0, + 0, + GRAIL_TYPE_PAN, + GRAIL_TYPE_SWIPE, + GRAIL_TYPE_BRUSH, + GRAIL_TYPE_HAND, +}; + +int gru_pan(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct combo_model *state = &gru->pan; + struct move_model *move = &gru->move; + grail_prop_t prop[DIM_GRAIL_PROP]; + if (!move->multi) { + if (state->active) { + gru_end(ge, state->gid, move); + state->active = 0; + } + return 0; + } + if (!move->x.tickle && !move->y.tickle) + return 0; + if (!state->active) { + int type = getype[move->ntouch]; + if (!type) + return 0; + state->gid = gin_gid_begin(ge, type, PRIO_GESTURE, frame); + state->active = 1; + } + if (!move->x.active && !move->y.active) + return 0; + prop[0] = move->x.active ? move->x.delta : 0; + prop[1] = move->y.active ? move->y.delta : 0; + gru_event(ge, state->gid, move, prop, 2); + return 1; +} + diff --git a/src/gestures-pinch.c b/src/gestures-pinch.c new file mode 100644 index 0000000..0721e02 --- /dev/null +++ b/src/gestures-pinch.c @@ -0,0 +1,68 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include +#include + +static const int getype[DIM_TOUCH + 1] = { + 0, + 0, + GRAIL_TYPE_PINCH, + GRAIL_TYPE_SCALE, + GRAIL_TYPE_PICK, + GRAIL_TYPE_GRAB, +}; + +int gru_pinch(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct combo_model *state = &gru->pinch; + struct move_model *move = &gru->move; + grail_prop_t prop[DIM_GRAIL_PROP]; + if (!move->multi) { + if (state->active) { + gru_end(ge, state->gid, move); + state->active = 0; + } + return 0; + } + if (!move->a.tickle) + return 0; + if (!move->r.tickle) + return 0; + if (!state->active) { + int type = getype[move->ntouch]; + if (!type) + return 0; + state->gid = gin_gid_begin(ge, type, PRIO_GESTURE, frame); + state->active = 1; + } + if (!move->r.active) + return 0; + prop[0] = move->r.delta; + gru_event(ge, state->gid, move, prop, 1); + return 1; +} diff --git a/src/gestures-rotate.c b/src/gestures-rotate.c new file mode 100644 index 0000000..6c496d2 --- /dev/null +++ b/src/gestures-rotate.c @@ -0,0 +1,66 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include +#include + +static const int getype[DIM_TOUCH + 1] = { + 0, + 0, + GRAIL_TYPE_ROTATE, + GRAIL_TYPE_TURN, + GRAIL_TYPE_WHIRL, + GRAIL_TYPE_REVOLVE, +}; + +int gru_rotate(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct combo_model *state = &gru->rotate; + struct move_model *move = &gru->move; + grail_prop_t prop[DIM_GRAIL_PROP]; + if (!move->multi) { + if (state->active) { + gru_end(ge, state->gid, move); + state->active = 0; + } + return 0; + } + if (!move->a.tickle) + return 0; + if (!state->active) { + int type = getype[move->ntouch]; + if (!type) + return 0; + state->gid = gin_gid_begin(ge, type, PRIO_GESTURE, frame); + state->active = 1; + } + if (!move->a.active) + return 0; + prop[0] = move->a.delta; + gru_event(ge, state->gid, move, prop, 1); + return 1; +} diff --git a/src/gestures-tapping.c b/src/gestures-tapping.c new file mode 100644 index 0000000..cc0e685 --- /dev/null +++ b/src/gestures-tapping.c @@ -0,0 +1,101 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include + +static const int TAP_BEGIN = 0; +static const int TAP_END = 1; +static const int TAP_CANCEL = 2; + +static const int TAP_MIN_GAP_MS = 25; +static const int TAP_MAX_GAP_MS = 300; + +int gru_tapping(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct tapping_model *state = &gru->tapping; + struct move_model *move = &gru->move; + grail_prop_t prop[DIM_GRAIL_PROP]; + if (move->ntouch) { + if (state->status == TAP_CANCEL) + return 0; + state->status = TAP_BEGIN; + if (!state->ntouch) + state->start = move->time; + if (move->ntouch > state->ntouch) { + state->ntouch = move->ntouch; + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + state->x = move->x.val; + state->y = move->y.val; + } else if (state->ntouch == 1) { + if (fabs(move->x.val - state->x) > move->y.bar || + fabs(move->y.val - state->y) > move->y.bar) + state->status = TAP_CANCEL; + } + if (!state->active) { + int type = GRAIL_TYPE_TAP1 + state->ntouch - 1; + state->gid = gin_gid_begin(ge, type, PRIO_TAP, frame); + state->active = 1; + } + } else if (state->status == TAP_BEGIN && state->ntouch) { + state->status = TAP_END; + } else { + state->status = TAP_BEGIN; + state->ntouch = 0; + return 0; + } + if (state->ntouch < 1 || state->ntouch > 5) + state->status = TAP_CANCEL; + if (move->time - state->start > TAP_MAX_GAP_MS) + state->status = TAP_CANCEL; + if (state->status == TAP_END && + move->time - state->start < TAP_MIN_GAP_MS) + state->status = TAP_CANCEL; + if (move->x.active || move->y.active) + state->status = TAP_CANCEL; + if (state->status == TAP_CANCEL) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + state->ntouch = 0; + return 0; + } + if (state->status != TAP_END || + move->time - state->start < TAP_MIN_GAP_MS) + return 0; + prop[0] = move->time - state->start; + gin_gid_event(ge, state->gid, + move->x.val, move->y.val, state->ntouch, + prop, 1, GRAIL_STATUS_END); + state->status = TAP_BEGIN; + state->ntouch = 0; + state->active = 0; + return 1; +} diff --git a/src/grail-api.c b/src/grail-api.c new file mode 100644 index 0000000..9ceb1e7 --- /dev/null +++ b/src/grail-api.c @@ -0,0 +1,115 @@ +#include "grail-inserter.h" +#include "grail-recognizer.h" +#include +#include +#include +#include +#include +#include +#include +#include "evbuf.h" + +struct grail_impl { + struct touch_dev dev; + struct touch_engine engine; + struct evbuf evbuf; +}; + +static void tp_event(struct touch_engine *engine, + const struct input_event *ev) +{ + struct grail *ge = engine->priv; + struct grail_impl *x = ge->impl; + evbuf_put(&x->evbuf, ev); +} + +static void tp_sync(struct touch_engine *engine, + const struct input_event *syn) +{ + struct input_event ev; + struct grail *ge = engine->priv; + struct grail_impl *x = ge->impl; + struct touch_frame *frame = &engine->frame; + grail_mask_t filtered[DIM_EV_TYPE_BYTES]; + int nevent = 0; + gin_frame_begin(ge, frame); + gru_recognize(ge, frame); + gin_frame_end(ge, filtered, sizeof(filtered), frame); + if (!ge->event) { + evbuf_clear(&x->evbuf); + return; + } + while (!evbuf_empty(&x->evbuf)) { + evbuf_get(&x->evbuf, &ev); + if (grail_mask_get(filtered, ev.type)) + continue; + ge->event(ge, &ev); + nevent++; + } + if (nevent) + ge->event(ge, syn); +} + +int grail_open(struct grail *ge, int fd) +{ + struct grail_impl *x; + int ret; + x = calloc(1, sizeof(*x)); + if (!x) + return -ENOMEM; + ge->impl = x; + ret = gin_init(ge); + if (ret) + goto freemem; + ret = touch_dev_open(&x->dev, fd); + if (ret) + goto freegin; + ret = gru_init(ge); + if (ret) + goto freedev; + touch_engine_init(&x->engine, tp_event, tp_sync, ge); + touch_engine_attach(&x->engine, &x->dev); + return 0; + freedev: + touch_dev_close(&x->dev, fd); + freegin: + gin_destroy(ge); + freemem: + free(x); + ge->impl = 0; + return ret; +} + +void grail_close(struct grail *ge, int fd) +{ + struct grail_impl *x = ge->impl; + void *status; + touch_engine_detach(&x->engine, &x->dev); + gru_destroy(ge); + touch_dev_close(&x->dev, fd); + gin_destroy(ge); + free(ge->impl); + ge->impl = 0; +} + +int grail_idle(struct grail *ge, int fd, int ms) +{ + struct grail_impl *x = ge->impl; + return touch_dev_idle(&x->dev, fd, ms); +} + +int grail_pull(struct grail *ge, int fd) +{ + struct grail_impl *x = ge->impl; + return touch_dev_pull(&x->dev, fd); +} + +void grail_get_units(struct grail_coord *min, struct grail_coord *max, + const struct grail *ge) +{ + const struct touch_dev_caps *caps = &ge->impl->dev.caps; + min->x = caps->info[TP_POS_X].minimum; + min->y = caps->info[TP_POS_Y].minimum; + max->x = caps->info[TP_POS_X].maximum; + max->y = caps->info[TP_POS_Y].maximum; +} 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 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-bits.h" + +static const int grail_bits_in_byte[256] = +{ + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, +}; + +static const int grail_first_set_bit[256] = +{ + -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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, +}; + +void grail_mask_set_mask(grail_mask_t *a, const grail_mask_t *b, int bytes) +{ + while (bytes--) + *a++ |= *b++; +} + +void grail_mask_clear_mask(grail_mask_t *a, const grail_mask_t *b, int bytes) +{ + while (bytes--) + *a++ &= ~*b++; +} + +int grail_mask_count(const grail_mask_t *mask, int bytes) +{ + int count = 0; + while (bytes--) + count += grail_bits_in_byte[*mask++]; + return count; +} + +int grail_mask_get_first(const grail_mask_t *mask, int bytes) +{ + int k; + for (k = 0; k < bytes; k++) + if (mask[k]) + return (k << 3) | grail_first_set_bit[mask[k]]; + return -1; +} + +int grail_mask_get_next(int i, const grail_mask_t *mask, int bytes) +{ + int k = ++i >> 3; + if (k < bytes) { + i = grail_first_set_bit[mask[k] & (~0 << (i & 7))]; + if (i >= 0) + return (k << 3) | i; + while (++k < bytes) { + i = grail_first_set_bit[mask[k]]; + if (i >= 0) + return (k << 3) | i; + } + } + return -1; +} diff --git a/src/grail-gestures.c b/src/grail-gestures.c new file mode 100644 index 0000000..e1bf622 --- /dev/null +++ b/src/grail-gestures.c @@ -0,0 +1,188 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include + +static const float SN_MOVE = 200; +static const float SN_ZOOM = 200; +static const float SN_ROTATE = 200; +static const float BAR_MOVE = 4; +static const float BAR_ZOOM = 12; +static const float BAR_ROTATE = 12; + +static void compute_position(float *x, float *y, + const struct touch_frame *frame) +{ + int i, n = frame->nactive; + *x = 0; + *y = 0; + if (n < 1) + return; + for (i = 0; i < n; i++) { + const struct touch *t = frame->active[i]; + *x += t->prop[TP_POS_X]; + *y += t->prop[TP_POS_Y]; + } + *x /= n; + *y /= n; +} + +static float compute_radius(float x, float y, + const struct touch_frame *frame) +{ + int i, n = frame->nactive; + float r = 0, r2 = 0; + if (n < 2) + return r; + for (i = 0; i < n; i++) { + const struct touch *t = frame->active[i]; + float dx = t->prop[TP_POS_X] - x; + float dy = t->prop[TP_POS_Y] - y; + r2 += dx * dx + dy * dy; + } + r2 /= n; + r = sqrt(r2); + return r; +} + +static float compute_rotation(float x, float y, float r, + const struct touch_frame *prev, + const struct touch_frame *frame) +{ + int i, n = frame->nactive; + float da = 0, da2 = 0; + if (n < 2) + return da; + for (i = 0; i < n; i++) { + const struct touch *t = frame->active[i]; + const struct touch *ot = &prev->touch[t->slot]; + float dx = t->prop[TP_POS_X] - x; + float dy = t->prop[TP_POS_Y] - y; + float mx = t->prop[TP_POS_X] - ot->prop[TP_POS_X]; + float my = t->prop[TP_POS_Y] - ot->prop[TP_POS_Y]; + da2 += dx * my - dy * mx; + } + da2 /= n; + da = da2 / r; + return da; +} + +static float move_filter(const struct filter_model *m, float val) +{ + if (val > m->val - m->fuzz / 2 && val < m->val + m->fuzz / 2) + return m->val; + if (val > m->val - m->fuzz && val < m->val + m->fuzz) + return (m->val * 3 + val) / 4; + if (val > m->val - m->fuzz * 2 && val < m->val + m->fuzz * 2) + return (m->val + val) / 2; + return val; +} + +static int move_reset(struct filter_model *m, float val) +{ + m->delta = 0; + m->tickle = 0; + m->active = 0; + m->val = val; + m->orig = val; +} + +static int move_update(struct filter_model *m, float val) +{ + m->delta = val - m->val; + m->tickle = fabs(m->delta) > 0; + m->val = val; + if (!m->active && fabs(val - m->orig) > m->bar) + m->active = 1; + return m->active; +} + +void gru_init_motion(struct grail *ge) +{ + struct gesture_recognizer *gru = ge->gru; + struct move_model *state = &gru->move; + struct grail_coord min, max; + grail_get_units(&min, &max, ge); + float x = max.x - min.x; + float y = max.y - min.y; + float r = sqrt(x * x + y * y); + state->x.fuzz = x / SN_MOVE; + state->y.fuzz = y / SN_MOVE; + state->r.fuzz = r / SN_ZOOM; + state->a.fuzz = r / SN_ROTATE; + state->x.bar = BAR_MOVE * state->x.fuzz; + state->y.bar = BAR_MOVE * state->y.fuzz; + state->r.bar = BAR_ZOOM * state->r.fuzz; + state->a.bar = BAR_ROTATE * state->a.fuzz; + state->multi = 0; +} + +void gru_motion(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_recognizer *gru = ge->gru; + struct move_model *state = &gru->move; + float x, y, r, a; + compute_position(&x, &y, frame); + if (frame->nactive < 2 || frame->ncreate || frame->ndestroy) { + r = compute_radius(x, y, frame); + a = 0; + move_reset(&state->x, x); + move_reset(&state->y, y); + move_reset(&state->r, r); + move_reset(&state->a, a); + state->multi = 0; + } else { + x = move_filter(&state->x, x); + y = move_filter(&state->y, y); + r = compute_radius(x, y, frame); + r = move_filter(&state->r, r); + a = state->a.val + compute_rotation(x, y, r, &gru->frame, frame); + //a = move_filter(&state->a, a); + move_update(&state->x, x); + move_update(&state->y, y); + move_update(&state->r, r); + move_update(&state->a, a); + state->multi = 1; + } + state->ntouch = frame->nactive; + state->time = frame->time; +} + +void gru_event(struct grail *ge, int gid, + const struct move_model *move, + const grail_prop_t *prop, int nprop) +{ + gin_gid_event(ge, gid, + move->x.val, move->y.val, move->ntouch, + prop, nprop, + GRAIL_STATUS_UPDATE); +} + +void gru_end(struct grail *ge, int gid, const struct move_model *move) +{ + gin_gid_end(ge, gid, move->x.val, move->y.val, move->ntouch); +} + diff --git a/src/grail-gestures.h b/src/grail-gestures.h new file mode 100644 index 0000000..663d357 --- /dev/null +++ b/src/grail-gestures.h @@ -0,0 +1,77 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#ifndef _GRAIL_GESTURES_H +#define _GRAIL_GESTURES_H + +#include "grail-inserter.h" + +#define PRIO_POINTER 1 +#define PRIO_GESTURE 2 +#define PRIO_TAP 3 + +struct filter_model { + float delta, val, orig, fuzz, bar; + int tickle, active; +}; + +struct move_model { + struct filter_model x, y, r, a; + int multi, ntouch; + float dx, dy, dr, da; + grail_time_t time; +}; + +void gru_init_motion(struct grail *ge); +void gru_motion(struct grail *ge, + const struct touch_frame *frame); +void gru_event(struct grail *ge, int gid, + const struct move_model *move, + const grail_prop_t *prop, int nprop); + +struct combo_model { + int active, gid; +}; + +int gru_pan(struct grail *ge, + const struct touch_frame *frame); +int gru_pinch(struct grail *ge, + const struct touch_frame *frame); +int gru_rotate(struct grail *ge, + const struct touch_frame *frame); +int gru_combo(struct grail *ge, + const struct touch_frame *frame); + +struct tapping_model { + grail_time_t start; + float x, y; + int status, ntouch; + int active, gid; +}; + +int gru_tapping(struct grail *ge, + const struct touch_frame *frame); + +#endif + diff --git a/src/grail-inserter.c b/src/grail-inserter.c new file mode 100644 index 0000000..724052c --- /dev/null +++ b/src/grail-inserter.c @@ -0,0 +1,282 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-inserter.h" +#include +#include +#include + +static const int MAX_GESTURE_ID = 0xfff; + +struct gesture_inserter { + struct slot_state state[DIM_INSTANCE]; + grail_mask_t unused[DIM_INSTANCE_BYTES]; + grail_mask_t fresh[DIM_INSTANCE_BYTES]; + grail_mask_t used[DIM_INSTANCE_BYTES]; + grail_time_t time; + int gestureid; +}; + +static int find_gslot(const struct gesture_inserter *gin, int gid) +{ + int i; + grail_mask_foreach(i, gin->used, sizeof(gin->used)) + if (gin->state[i].id == gid) + return i; + return -1; +} + +static void send_event(struct grail *ge, struct slot_state *s, + const struct gesture_event *ev) +{ + struct grail_event gev; + int i; + if (!ge->gesture) + return; + gev.type = s->type; + gev.id = s->id; + gev.status = s->status; + gev.ntouch = ev->ntouch; + gev.nprop = ev->nprop; + gev.time = ev->time; + gev.pos = ev->pos; + memcpy(gev.prop, ev->prop, ev->nprop * sizeof(grail_prop_t)); + for (i = 0; i < s->nclient; i++) { + gev.client_id = s->client_id[i]; + ge->gesture(ge, &gev); + } +} + +// todo: spanning tree for multi-user case +static void setup_new_gestures(struct grail *ge, + const struct touch_frame *frame) +{ + struct gesture_inserter *gin = ge->gin; + grail_mask_t types[DIM_GRAIL_TYPE_BYTES]; + grail_mask_t span[DIM_TOUCH_BYTES]; + struct grail_client_info info[DIM_CLIENT]; + int i, j, nclient = 0; + int nfresh = grail_mask_count(gin->fresh, sizeof(gin->fresh)); + if (!nfresh) + return; + + memset(types, 0, sizeof(types)); + memset(span, 0, sizeof(span)); + + grail_mask_foreach(i, gin->fresh, sizeof(gin->fresh)) { + struct slot_state *s = &gin->state[i]; + grail_mask_set(types, s->type); + grail_mask_set_mask(span, s->span, sizeof(span)); + } + + if (ge->get_clients) { + struct grail_coord coord[DIM_CLIENT]; + int ncoord = 0; + grail_mask_foreach(i, span, sizeof(span)) { + const struct touch *t = &frame->touch[i]; + coord[ncoord].x = t->prop[TP_POS_X]; + coord[ncoord].y = t->prop[TP_POS_Y]; + ncoord++; + } + nclient = ge->get_clients(ge, info, DIM_CLIENT, + coord, ncoord, types, sizeof(types)); + } + + grail_mask_foreach(i, gin->fresh, sizeof(gin->fresh)) { + struct slot_state *s = &gin->state[i]; + s->nclient = 0; + for (j = 0; j < nclient; j++) { + if (!grail_mask_get(info[j].mask, s->type)) + continue; + s->client_id[s->nclient++] = info[j].id; + } + } + + memset(gin->fresh, 0, sizeof(gin->fresh)); +} + +int gin_init(struct grail *ge) +{ + struct gesture_inserter *gin; + int i; + gin = calloc(1, sizeof(*gin)); + if (!gin) + return -ENOMEM; + for (i = 0; i < DIM_INSTANCE; i++) + grail_mask_set(gin->unused, i); + ge->gin = gin; + return 0; +} + +void gin_destroy(struct grail *ge) +{ + free(ge->gin); + ge->gin = NULL; +} + +void gin_frame_begin(struct grail *ge, const struct touch_frame *frame) +{ + struct gesture_inserter *gin = ge->gin; + gin->time = frame->time; +} + +void gin_frame_end(struct grail *ge, + grail_mask_t *filtered, int max_filtered, + const struct touch_frame *frame) +{ + struct gesture_inserter *gin = ge->gin; + grail_mask_t span[DIM_INSTANCE_BYTES]; + int i, hold = 0, discard = 0; + + memset(filtered, 0, max_filtered); + memset(span, 0, sizeof(span)); + + setup_new_gestures(ge, frame); + + grail_mask_foreach(i, gin->used, sizeof(gin->used)) { + struct slot_state *s = &gin->state[i]; + if (!s->nclient) + continue; + if (s->priority > hold) + hold = s->priority; + if (s->status == GRAIL_STATUS_BEGIN) + continue; + if (s->priority > discard) + discard = s->priority; + } + + grail_mask_foreach(i, gin->used, sizeof(gin->used)) { + struct slot_state *s = &gin->state[i]; + if (!s->nclient || s->priority < discard) + gin_gid_discard(ge, s->id); + } + + grail_mask_foreach(i, gin->used, sizeof(gin->used)) { + struct slot_state *s = &gin->state[i]; + struct gesture_event ev; + grail_mask_set_mask(span, s->span, sizeof(span)); + if (s->priority < hold) + continue; + while (!gebuf_empty(&s->buf)) { + gebuf_get(&s->buf, &ev); + send_event(ge, s, &ev); + } + } + + grail_mask_foreach(i, gin->used, sizeof(gin->used)) { + struct slot_state *s = &gin->state[i]; + if (s->status == GRAIL_STATUS_END) + gin_gid_discard(ge, s->id); + } + + if (grail_mask_count(span, sizeof(span))) + grail_mask_set(filtered, EV_ABS); +} + +int gin_gid_begin_select(struct grail *ge, int type, int priority, + const grail_mask_t *span, int nspan) +{ + struct gesture_inserter *gin = ge->gin; + struct slot_state *s; + int i = grail_mask_get_first(gin->unused, sizeof(gin->unused)); + if (i < 0) + return -1; + s = &gin->state[i]; + s->type = type; + s->priority = priority; + s->id = gin->gestureid++ & MAX_GESTURE_ID; + s->status = GRAIL_STATUS_BEGIN; + s->nclient = 0; + memcpy(s->span, span, nspan); + gebuf_clear(&s->buf); + grail_mask_clear(gin->unused, i); + grail_mask_set(gin->fresh, i); + grail_mask_set(gin->used, i); + return s->id; +} + +int gin_gid_begin(struct grail *ge, int type, int priority, + const struct touch_frame *frame) +{ + return gin_gid_begin_select(ge, type, priority, + frame->touches, sizeof(frame->touches)); +} + +void gin_gid_discard(struct grail *ge, int gid) +{ + struct gesture_inserter *gin = ge->gin; + struct slot_state *s; + int i = find_gslot(gin, gid); + if (i < 0) + return; + s = &gin->state[i]; + gebuf_clear(&s->buf); + s->status = GRAIL_STATUS_END; + grail_mask_clear(gin->used, i); + grail_mask_set(gin->unused, i); +} + +void gin_gid_event(struct grail *ge, int gid, + float x, float y, int ntouch, + const grail_prop_t *prop, int nprop, + int status) +{ + struct gesture_inserter *gin = ge->gin; + struct gesture_event ev; + struct slot_state *s; + int i = find_gslot(gin, gid); + if (i < 0) + return; + s = &gin->state[i]; + ev.ntouch = ntouch; + ev.nprop = nprop; + ev.time = gin->time; + ev.pos.x = x; + ev.pos.y = y; + memcpy(ev.prop, prop, nprop * sizeof(grail_prop_t)); + gebuf_put(&s->buf, &ev); + if (s->status == GRAIL_STATUS_BEGIN) + s->status = status; +} + +void gin_gid_end(struct grail *ge, int gid, + float x, float y, int ntouch) +{ + struct gesture_inserter *gin = ge->gin; + struct gesture_event ev; + struct slot_state *s; + int i = find_gslot(gin, gid); + if (i < 0) + return; + s = &gin->state[i]; + if (s->status != GRAIL_STATUS_BEGIN) { + ev.ntouch = ntouch; + ev.nprop = 0; + ev.time = gin->time; + ev.pos.x = x; + ev.pos.y = y; + gebuf_put(&s->buf, &ev); + } + s->status = GRAIL_STATUS_END; +} diff --git a/src/grail-inserter.h b/src/grail-inserter.h new file mode 100644 index 0000000..3ffacec --- /dev/null +++ b/src/grail-inserter.h @@ -0,0 +1,71 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#ifndef _GRAIL_INSERTER_H +#define _GRAIL_INSERTER_H + +#include +#include +#include "gebuf.h" + +#define DIM_EV_TYPE EV_CNT +#define DIM_EV_TYPE_BYTES ((DIM_EV_TYPE + 7) >> 3) + +#define DIM_INSTANCE 32 +#define DIM_INSTANCE_BYTES ((DIM_INSTANCE + 7) >> 3) + +#define DIM_CLIENT 32 + +struct slot_state { + int type; + int priority; + int id; + int status; + int nclient; + struct grail_client_id client_id[DIM_CLIENT]; + grail_mask_t span[DIM_TOUCH_BYTES]; + struct gebuf buf; +}; + +int gin_init(struct grail *ge); +void gin_destroy(struct grail *ge); + +void gin_frame_begin(struct grail *ge, const struct touch_frame *frame); +void gin_frame_end(struct grail *ge, grail_mask_t *filtered, int max_filtered, + const struct touch_frame *frame); + +int gin_gid_begin_select(struct grail *ge, int type, int priority, + const grail_mask_t *span, int nspan); +int gin_gid_begin(struct grail *ge, int type, int priority, + const struct touch_frame *frame); +void gin_gid_discard(struct grail *ge, int gid); + +void gin_gid_event(struct grail *ge, int gid, + float x, float y, int ntouch, + const grail_prop_t *prop, int nprop, + int status); +void gin_gid_end(struct grail *ge, int gid, + float x, float y, int ntouch); + +#endif diff --git a/src/grail-recognizer.c b/src/grail-recognizer.c new file mode 100644 index 0000000..587a12a --- /dev/null +++ b/src/grail-recognizer.c @@ -0,0 +1,58 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include "grail-recognizer.h" +#include +#include +#include + +int gru_init(struct grail *ge) +{ + struct gesture_recognizer *gru; + gru = calloc(1, sizeof(struct gesture_recognizer)); + if (!gru) + return -ENOMEM; + ge->gru = gru; + gru_init_motion(ge); + return 0; +} + +void gru_destroy(struct grail *ge) +{ + free(ge->gru); + ge->gru = NULL; +} + +void gru_recognize(struct grail *ge, const struct touch_frame *frame) +{ + if (!ge->gin || !ge->gru) + return; + gru_motion(ge, frame); + gru_pan(ge, frame); + gru_pinch(ge, frame); + gru_rotate(ge, frame); + gru_combo(ge, frame); + gru_tapping(ge, frame); + ge->gru->frame = *frame; +} diff --git a/src/grail-recognizer.h b/src/grail-recognizer.h new file mode 100644 index 0000000..8a5ac80 --- /dev/null +++ b/src/grail-recognizer.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#ifndef _GRAIL_RECOGNIZER_H +#define _GRAIL_RECOGNIZER_H + +#include "grail-gestures.h" + +struct gesture_recognizer { + struct touch_frame frame; + struct move_model move; + struct combo_model pan; + struct combo_model pinch; + struct combo_model rotate; + struct combo_model combo; + struct tapping_model tapping; +}; + +int gru_init(struct grail *ge); +void gru_recognize(struct grail *ge, const struct touch_frame *frame); +void gru_destroy(struct grail *ge); + +#endif diff --git a/src/touch-dev.c b/src/touch-dev.c new file mode 100644 index 0000000..4cc8d55 --- /dev/null +++ b/src/touch-dev.c @@ -0,0 +1,218 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include +#include +#include +#include + +/* from mtdev-mapping.h */ +#define MT_TOUCH_MAJOR 0 +#define MT_TOUCH_MINOR 1 +#define MT_WIDTH_MAJOR 2 +#define MT_WIDTH_MINOR 3 +#define MT_ORIENTATION 4 +#define MT_POSITION_X 5 +#define MT_POSITION_Y 6 +#define MT_TRACKING_ID 9 +#define MT_PRESSURE 10 + +int mtdev_fetch_event(struct mtdev *dev, int fd, struct input_event *ev); +void mtdev_put_event(struct mtdev *dev, const struct input_event *ev); +int mtdev_empty(struct mtdev *dev); +void mtdev_get_event(struct mtdev *dev, struct input_event* ev); + +struct touch_dev_impl { + int id[DIM_TOUCH]; + int slot; + int status; + grail_mask_t mask[DIM_TOUCH_BYTES]; + touch_prop_t prop[DIM_TOUCH_PROP]; + struct mtdev mtdev; +}; + +static inline void set_info(struct touch_dev_caps *caps, int code, + const struct input_absinfo *info) +{ + grail_mask_set(caps->mask, code); + caps->info[code] = *info; +} + +static void set_caps(struct touch_dev_caps *caps, + const struct mtdev_caps *mtcaps) +{ + if (mtcaps->has_abs[MT_POSITION_X]) + set_info(caps, TP_POS_X, &mtcaps->abs[MT_POSITION_X]); + if (mtcaps->has_abs[MT_POSITION_Y]) + set_info(caps, TP_POS_Y, &mtcaps->abs[MT_POSITION_Y]); + if (mtcaps->has_abs[MT_TOUCH_MAJOR]) + set_info(caps, TP_TOUCH_MAJOR, &mtcaps->abs[MT_TOUCH_MAJOR]); + if (mtcaps->has_abs[MT_TOUCH_MINOR]) + set_info(caps, TP_TOUCH_MINOR, &mtcaps->abs[MT_TOUCH_MINOR]); + if (mtcaps->has_abs[MT_WIDTH_MAJOR]) + set_info(caps, TP_WIDTH_MAJOR, &mtcaps->abs[MT_WIDTH_MAJOR]); + if (mtcaps->has_abs[MT_WIDTH_MINOR]) + set_info(caps, TP_WIDTH_MINOR, &mtcaps->abs[MT_WIDTH_MINOR]); + if (mtcaps->has_abs[MT_ORIENTATION]) + set_info(caps, TP_ORIENTATION, &mtcaps->abs[MT_ORIENTATION]); + if (mtcaps->has_abs[MT_PRESSURE]) + set_info(caps, TP_PRESSURE, &mtcaps->abs[MT_PRESSURE]); +} + +static inline void set_prop(struct touch_dev_impl *x, int code, int value) +{ + grail_mask_set(x->mask, code); + x->prop[code] = value; +} + +static void finish_touch(struct touch_dev *dev, + struct touch_dev_impl *x) +{ + if (x->status < 0 && dev->destroy) + dev->destroy(dev, x->slot); + if (x->status > 0 && dev->create) + dev->create(dev, x->slot, x->mask, x->prop); + if (x->status == 0 && dev->modify && + grail_mask_count(x->mask, sizeof(x->mask))) + dev->modify(dev, x->slot, x->mask, x->prop); + x->status = 0; + memset(x->mask, 0, sizeof(x->mask)); +} + +static void finish_packet(struct touch_dev *dev, + const struct input_event *syn) +{ + finish_touch(dev, dev->impl); + if (dev->sync) + dev->sync(dev, syn); +} + +static int handle_abs_event(struct touch_dev *dev, + const struct input_event *ev) +{ + struct touch_dev_impl *x = dev->impl; + switch (ev->code) { + case ABS_MT_SLOT: + if (ev->value >= 0 && ev->value < DIM_TOUCH) { + if (x->slot != ev->value) + finish_touch(dev, x); + x->slot = ev->value; + } + return 1; + case ABS_MT_POSITION_X: + set_prop(x, TP_POS_X, ev->value); + return 1; + case ABS_MT_POSITION_Y: + set_prop(x, TP_POS_Y, ev->value); + return 1; + case ABS_MT_TOUCH_MAJOR: + set_prop(x, TP_TOUCH_MAJOR, ev->value); + return 1; + case ABS_MT_TOUCH_MINOR: + set_prop(x, TP_TOUCH_MINOR, ev->value); + return 1; + case ABS_MT_WIDTH_MAJOR: + set_prop(x, TP_WIDTH_MAJOR, ev->value); + return 1; + case ABS_MT_WIDTH_MINOR: + set_prop(x, TP_WIDTH_MINOR, ev->value); + return 1; + case ABS_MT_ORIENTATION: + set_prop(x, TP_ORIENTATION, ev->value); + return 1; + case ABS_MT_PRESSURE: + set_prop(x, TP_PRESSURE, ev->value); + return 1; + case ABS_MT_TRACKING_ID: + if (x->id[x->slot] != ev->value) { + if (x->id[x->slot] != MT_ID_NULL) { + x->status = -1; + finish_touch(dev, x); + } + if (ev->value != MT_ID_NULL) + x->status = 1; + } + x->id[x->slot] = ev->value; + return 1; + default: + return 0; + } +} + +int touch_dev_open(struct touch_dev *dev, int fd) +{ + struct touch_dev_impl *x; + int ret, i; + memset(dev, 0, sizeof(*dev)); + x = calloc(1, sizeof(*x)); + if (!x) + return -ENOMEM; + ret = mtdev_open(&x->mtdev, fd); + if (!ret && !x->mtdev.caps.has_mtdata) + ret = -ENODEV; + if (ret) + goto error; + for (i = 0; i < DIM_TOUCH; i++) + x->id[i] = MT_ID_NULL; + set_caps(&dev->caps, &x->mtdev.caps); + dev->impl = x; + return 0; + error: + free(x); + return ret; +} + +int touch_dev_idle(struct touch_dev *dev, int fd, int ms) +{ + return mtdev_idle(&dev->impl->mtdev, fd, ms); +} + +int touch_dev_pull(struct touch_dev *dev, int fd) +{ + struct touch_dev_impl *x = dev->impl; + struct input_event ev; + int ret, count = 0, consumed; + while ((ret = mtdev_get(&x->mtdev, fd, &ev, 1)) > 0) { + consumed = 0; + if (ev.type == EV_SYN) { + if (ev.code == SYN_REPORT) + finish_packet(dev, &ev); + consumed++; + } else if (ev.type == EV_ABS) { + consumed += handle_abs_event(dev, &ev); + } + if (!consumed && dev->event) + dev->event(dev, &ev); + count++; + } + return count > 0 ? count : ret; +} + +void touch_dev_close(struct touch_dev *dev, int fd) +{ + struct touch_dev_impl *x = dev->impl; + mtdev_close(&x->mtdev); + free(x); + memset(dev, 0, sizeof(*dev)); +} diff --git a/src/touch-engine.c b/src/touch-engine.c new file mode 100644 index 0000000..3e6870e --- /dev/null +++ b/src/touch-engine.c @@ -0,0 +1,137 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#include +#include + +static void tp_event(struct touch_dev *dev, const struct input_event *ev) +{ + struct touch_engine *engine = dev->priv; + if (engine && engine->event) + engine->event(engine, ev); +} + +static void update_frame(struct touch_frame *frame, + int slot, int active, + const grail_mask_t *mask, + const touch_prop_t *prop) +{ + struct touch *t = &frame->touch[slot]; + int i; + t->active = active; + grail_mask_modify(frame->touches, slot, active); + if (mask && prop) + grail_mask_foreach(i, mask, DIM_TOUCH_PROP_BYTES) + t->prop[i] = prop[i]; +} + +static void finalize_frame(struct touch_frame *frame, touch_time_t time) +{ + int i, nslot = 0; + grail_mask_foreach(i, frame->touches, DIM_TOUCH_BYTES) + frame->active[nslot++] = &frame->touch[i]; + frame->nactive = nslot; + frame->time = time; +} + +static void tp_create(struct touch_dev *dev, int slot, + const grail_mask_t *mask, const touch_prop_t *prop) +{ + struct touch_engine *engine = dev->priv; + if (engine) { + engine->frame.ncreate++; + update_frame(&engine->frame, slot, 1, mask, prop); + } +} + +static void tp_modify(struct touch_dev *dev, int slot, + const grail_mask_t *mask, const touch_prop_t *prop) +{ + struct touch_engine *engine = dev->priv; + if (engine) { + engine->frame.nmodify++; + update_frame(&engine->frame, slot, 1, mask, prop); + } +} + +static void tp_destroy(struct touch_dev *dev, int slot) +{ + struct touch_engine *engine = dev->priv; + if (engine) { + engine->frame.ndestroy++; + update_frame(&engine->frame, slot, 0, 0, 0); + } +} + +static void tp_sync(struct touch_dev *dev, const struct input_event *syn) +{ + static const touch_time_t ms = 1000; + struct touch_engine *engine = dev->priv; + touch_time_t time = syn->time.tv_usec / ms + syn->time.tv_sec * ms; + if (engine) { + finalize_frame(&engine->frame, time); + if (engine->sync) + engine->sync(engine, syn); + engine->frame.ncreate = 0; + engine->frame.nmodify = 0; + engine->frame.ndestroy = 0; + } +} + +void touch_engine_init(struct touch_engine *engine, + void (*event)(struct touch_engine *engine, + const struct input_event *ev), + void (*sync)(struct touch_engine *engine, + const struct input_event *ev), + void *priv) +{ + int i; + memset(engine, 0, sizeof(*engine)); + for (i = 0; i < DIM_TOUCH; i++) + engine->frame.touch[i].slot = i; + engine->event = event; + engine->sync = sync; + engine->priv = priv; +} + +void touch_engine_attach(struct touch_engine *engine, struct touch_dev *dev) +{ + dev->priv = engine; + dev->event = tp_event; + dev->create = tp_create; + dev->modify = tp_modify; + dev->destroy = tp_destroy; + dev->sync = tp_sync; +} + +void touch_engine_detach(struct touch_engine *engine, struct touch_dev *dev) +{ + dev->sync = 0; + dev->destroy = 0; + dev->modify = 0; + dev->create = 0; + dev->event = 0; + dev->priv = 0; +} + -- cgit v1.2.3