/*****************************************************************************
*
* 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
#include "evbuf.h"
#include "mtdev-plumbing.h"
#define DIM_TOUCH 32
struct touch_dev_impl {
int id[DIM_TOUCH];
int slot;
int status;
touch_prop_mask_t mask[1];
touch_prop_t prop[DIM_TOUCH_PROP];
struct evbuf evbuf;
struct mtdev mtdev;
};
static int touch_get_fetched(struct touch_dev_impl *x,
struct input_event* ev, int ev_max)
{
struct input_event kev;
int count = 0;
while (count < ev_max) {
while (mtdev_empty(&x->mtdev)) {
if (evbuf_empty(&x->evbuf))
return count;
evbuf_get(&x->evbuf, &kev);
mtdev_put_event(&x->mtdev, &kev);
}
mtdev_get_event(&x->mtdev, &ev[count++]);
}
return count;
}
static inline int has_any_prop(const touch_prop_mask_t *mask)
{
return mask[0] != 0;
}
static inline void set_prop(struct touch_dev_impl *x, int code, int value)
{
x->mask[code >> 5] |= 1U << (code & 31);
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 && has_any_prop(x->mask) && dev->modify)
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;
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_fetch(struct touch_dev *dev, int fd)
{
struct touch_dev_impl *x = dev->impl;
struct input_event ev;
int ret, count = 0;
while ((ret = mtdev_fetch_event(&x->mtdev, fd, &ev)) > 0) {
evbuf_put(&x->evbuf, &ev);
count++;
}
return count > 0 ? count : ret;
}
int touch_dev_process(struct touch_dev *dev)
{
struct touch_dev_impl *x = dev->impl;
struct input_event ev;
int ret, count = 0, consumed;
while ((ret = touch_get_fetched(x, &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));
}