/*****************************************************************************
*
* grail - Gesture Recognition And Instantiation Library
*
* Copyright (C) 2010-2011 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 .
*
****************************************************************************/
#include "grail-impl.h"
#include "grail-inserter.h"
#include "grail-recognizer.h"
#include
#include
#include
#include
#include
#include
#include
#include
#define DIM_FRAMES 100
#define FRAME_RATE 100
unsigned int GRAIL_PUBLIC grail_get_version(void)
{
return GRAIL_VERSION;
}
int GRAIL_PUBLIC grail_open(struct grail *ge, int fd)
{
struct grail_impl *x;
struct stat fs;
int ret;
ret = fstat(fd, &fs);
if (ret)
return ret;
x = calloc(1, sizeof(*x));
if (!x)
return -ENOMEM;
if (!fs.st_rdev)
x->fptest = fdopen(fd, "r");
x->evemu = evemu_new(x->fptest ? "fptest" : 0);
if (!x->evemu) {
ret = -ENOMEM;
goto freemem;
}
if (x->fptest)
ret = evemu_read(x->evemu, x->fptest) <= 0;
else
ret = evemu_extract(x->evemu, fd);
if (ret)
goto freemem;
if (!utouch_frame_is_supported_mtdev(x->evemu)) {
ret = -ENODEV;
goto freemem;
}
if (!x->fptest) {
x->mtdev = mtdev_new_open(fd);
if (!x->mtdev) {
ret = -ENOMEM;
goto freemem;
}
}
x->fh = utouch_frame_new_engine(DIM_FRAMES, DIM_TOUCH, FRAME_RATE);
if (!x->fh) {
ret = -ENOMEM;
goto freedev;
}
ret = utouch_frame_init_mtdev(x->fh, x->evemu);
if (ret)
goto freeframe;
ge->impl = x;
ret = gin_init(ge);
if (ret)
goto freeframe;
ret = gru_init(ge);
if (ret)
goto freegin;
return 0;
freegin:
gin_destroy(ge);
freeframe:
utouch_frame_delete_engine(x->fh);
freedev:
if (x->mtdev)
mtdev_close_delete(x->mtdev);
freemem:
evemu_delete(x->evemu);
if (x->fptest)
fclose(x->fptest);
free(x);
ge->impl = 0;
return ret;
}
void GRAIL_PUBLIC grail_close(struct grail *ge, int fd)
{
struct grail_impl *x = ge->impl;
gru_destroy(ge);
gin_destroy(ge);
utouch_frame_delete_engine(x->fh);
if (x->mtdev)
mtdev_close_delete(x->mtdev);
evemu_delete(x->evemu);
if (x->fptest)
fclose(x->fptest);
free(x);
ge->impl = 0;
}
int GRAIL_PUBLIC grail_idle(struct grail *ge, int fd, int ms)
{
struct grail_impl *x = ge->impl;
if (x->fptest)
return 0;
if (x->mtdev)
return mtdev_idle(x->mtdev, fd, ms);
return 1;
}
void GRAIL_PUBLIC grail_get_units(const struct grail *ge,
struct grail_coord *min, struct grail_coord *max)
{
struct utouch_surface *s = utouch_frame_get_surface(ge->impl->fh);
min->x = s->min_x;
min->y = s->min_y;
max->x = s->max_x;
max->y = s->max_y;
}
const struct utouch_frame GRAIL_PUBLIC *
grail_get_contact_frame(const struct grail *ge)
{
return ge->impl->touch;
}
static void flush_events(struct grail *ge)
{
struct grail_impl *impl = ge->impl;
struct input_event iev;
grailbuf_clear(&impl->gbuf);
while (!evbuf_empty(&impl->evbuf)) {
evbuf_get(&impl->evbuf, &iev);
if (ge->event)
ge->event(ge, &iev);
}
}
static int skip_event(const struct input_event *ev, int count)
{
switch (ev->type) {
case EV_ABS:
return 1;
case EV_KEY:
return ev->code >= BTN_DIGI && ev->code < BTN_WHEEL;
case EV_SYN:
switch (ev->code) {
case SYN_MT_REPORT:
return 1;
case SYN_REPORT:
return count == 0;
default:
return 0;
}
default:
return 0;
}
}
static void flush_gestures(struct grail *ge)
{
struct grail_impl *impl = ge->impl;
struct input_event iev;
struct grail_event gev;
int count = 0;
while (!evbuf_empty(&impl->evbuf)) {
evbuf_get(&impl->evbuf, &iev);
if (skip_event(&iev, count))
continue;
if (ge->event)
ge->event(ge, &iev);
if (iev.type == EV_SYN && iev.code == SYN_REPORT)
count = 0;
else
count++;
}
while (!grailbuf_empty(&impl->gbuf)) {
grailbuf_get(&impl->gbuf, &gev);
if (ge->gesture)
ge->gesture(ge, &gev);
}
}
static int gesture_timeout(struct grail *ge, const struct utouch_frame *frame)
{
struct gesture_recognizer *gru = ge->gru;
struct gesture_inserter *gin = ge->gin;
return grail_mask_count(gin->used, sizeof(gin->used)) == 0 &&
frame->time - frame->mod_time > gru->move.fm[FM_X].hold_ms;
}
static void report_frame(struct grail *ge,
const struct utouch_frame *touch,
const struct input_event *syn)
{
struct grail_impl *impl = ge->impl;
struct grail_event gev;
impl->touch = touch;
if (touch->num_active && !touch->prev->num_active) {
impl->ongoing = 1;
impl->gesture = 0;
}
if (!impl->ongoing)
return;
gin_frame_begin(ge, touch);
gru_recognize(ge, touch);
gin_frame_end(ge, touch);
if (!grailbuf_empty(&impl->gbuf))
impl->gesture = 1;
if (touch->num_active == 0 || gesture_timeout(ge, touch))
impl->ongoing &= impl->gesture;
}
static void grail_pump_mtdev(struct grail *ge, const struct input_event *ev)
{
struct grail_impl *impl = ge->impl;
const struct utouch_frame *frame;
evbuf_put(&impl->evbuf, ev);
if (ev->type == EV_SYN || ev->type == EV_ABS || ev->type == EV_KEY) {
frame = utouch_frame_pump_mtdev(impl->fh, ev);
if (frame)
report_frame(ge, frame, ev);
}
if (ev->type == EV_SYN) {
if (!impl->ongoing)
flush_events(ge);
if (impl->gesture)
flush_gestures(ge);
}
}
static int grail_pull_fstest(struct grail *ge, int fd)
{
struct grail_impl *impl = ge->impl;
struct input_event ev;
struct timeval evtime;
int count = 0;
memset(&evtime, 0, sizeof(evtime));
while (evemu_read_event_realtime(impl->fptest, &ev, &evtime) > 0) {
grail_pump_mtdev(ge, &ev);
count++;
}
fclose(impl->fptest);
impl->fptest = 0;
return count > 0 ? count : -1;
}
int GRAIL_PUBLIC grail_pull(struct grail *ge, int fd)
{
struct grail_impl *impl = ge->impl;
struct input_event ev;
int ret, count = 0;
if (impl->fptest)
return grail_pull_fstest(ge, fd);
while ((ret = mtdev_get(impl->mtdev, fd, &ev, 1)) > 0) {
grail_pump_mtdev(ge, &ev);
count++;
}
return count > 0 ? count : ret;
}