blob: 68b434d580dc75e0c01feff5f1a0ccf1317ceaa7 [file] [log] [blame]
/*****************************************************************************
*
* mtdev - Multitouch Protocol Translation Library (MIT license)
*
* Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
* 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.
*
****************************************************************************/
#include "state.h"
#include "iobuf.h"
#include "evbuf.h"
#include "match.h"
static inline int istouch(const struct mtdev_slot *data,
const struct mtdev *dev)
{
if (!dev->has_abs[mtdev_abs2mt(ABS_MT_TOUCH_MAJOR)])
return data->pressure > 0;
return data->touch_major ||
!mtdev_has_mt_event(dev, ABS_MT_TOUCH_MAJOR);
}
static inline int isfilled(unsigned int mask)
{
return GETBIT(mask, mtdev_abs2mt(ABS_MT_POSITION_X)) &&
GETBIT(mask, mtdev_abs2mt(ABS_MT_POSITION_Y));
}
/* Response-augmented EWMA filter, courtesy of Vojtech Pavlik */
static int defuzz(int value, int old_val, int fuzz)
{
if (fuzz) {
if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
return old_val;
if (value > old_val - fuzz && value < old_val + fuzz)
return (old_val * 3 + value) / 4;
if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
return (old_val + value) / 2;
}
return value;
}
/*
* solve - solve contact matching problem
* @state: mtdev state
* @dev: device capabilities
* @sid: array of current tracking ids
* @sx: array of current position x
* @sy: array of current position y
* @sn: number of current contacts
* @nid: array of new or matched tracking ids, to be filled
* @nx: array of new position x
* @ny: array of new position y
* @nn: number of new contacts
* @touch: which of the new contacts to fill
*/
static void solve(struct mtdev_state *state, const struct mtdev *dev,
const int *sid, const int *sx, const int *sy, int sn,
int *nid, const int *nx, const int *ny, int nn,
bitmask_t touch)
{
int A[DIM2_FINGER], *row;
int n2s[DIM_FINGER];
int id, i, j;
/* setup distance matrix for contact matching */
for (j = 0; j < sn; j++) {
row = A + nn * j;
for (i = 0; i < nn; i++)
row[i] = dist2(nx[i] - sx[j], ny[i] - sy[j]);
}
mtdev_match(n2s, A, nn, sn);
/* update matched contacts and create new ones */
foreach_bit(i, touch) {
j = n2s[i];
id = j >= 0 ? sid[j] : MT_ID_NULL;
if (id == MT_ID_NULL)
id = state->lastid++ & MT_ID_MAX;
nid[i] = id;
}
}
/*
* assign_tracking_id - assign tracking ids to all contacts
* @state: mtdev state
* @dev: device capabilities
* @data: array of all present contacts, to be filled
* @prop: array of all set contacts properties
* @size: number of contacts in array
* @touch: which of the contacts are actual touches
*/
static void assign_tracking_id(struct mtdev_state *state,
const struct mtdev *dev,
struct mtdev_slot *data, bitmask_t *prop,
int size, bitmask_t touch)
{
int sid[DIM_FINGER], sx[DIM_FINGER], sy[DIM_FINGER], sn = 0;
int nid[DIM_FINGER], nx[DIM_FINGER], ny[DIM_FINGER], i;
foreach_bit(i, state->used) {
sid[sn] = state->data[i].tracking_id;
sx[sn] = state->data[i].position_x;
sy[sn] = state->data[i].position_y;
sn++;
}
for (i = 0; i < size; i++) {
nx[i] = data[i].position_x;
ny[i] = data[i].position_y;
}
solve(state, dev, sid, sx, sy, sn, nid, nx, ny, size, touch);
for (i = 0; i < size; i++) {
data[i].tracking_id = GETBIT(touch, i) ? nid[i] : MT_ID_NULL;
SETBIT(prop[i], mtdev_abs2mt(ABS_MT_TRACKING_ID));
}
}
/*
* process_typeA - consume MT events and update mtdev state
* @state: mtdev state
* @data: array of all present contacts, to be filled
* @prop: array of all set contacts properties, to be filled
*
* This function is called when a SYN_REPORT is seen, right before
* that event is pushed to the queue.
*
* Returns -1 if the packet is not MT related and should not affect
* the current mtdev state.
*/
static int process_typeA(struct mtdev_state *state,
struct mtdev_slot *data, bitmask_t *prop)
{
struct input_event ev;
int consumed, mtcode;
int mtcnt = 0, size = 0;
prop[size] = 0;
while (!evbuf_empty(&state->inbuf)) {
evbuf_get(&state->inbuf, &ev);
consumed = 0;
switch (ev.type) {
case EV_SYN:
switch (ev.code) {
case SYN_MT_REPORT:
if (size < DIM_FINGER && isfilled(prop[size]))
size++;
if (size < DIM_FINGER)
prop[size] = 0;
mtcnt++;
consumed = 1;
break;
}
break;
case EV_KEY:
switch (ev.code) {
case BTN_TOUCH:
mtcnt++;
break;
}
break;
case EV_ABS:
if (size < DIM_FINGER && mtdev_is_absmt(ev.code)) {
mtcode = mtdev_abs2mt(ev.code);
set_sval(&data[size], mtcode, ev.value);
SETBIT(prop[size], mtcode);
mtcnt++;
consumed = 1;
}
break;
}
if (!consumed)
evbuf_put(&state->outbuf, &ev);
}
return mtcnt ? size : -1;
}
/*
* process_typeB - propagate events without parsing
* @state: mtdev state
*
* This function is called when a SYN_REPORT is seen, right before
* that event is pushed to the queue.
*/
static void process_typeB(struct mtdev_state *state)
{
struct input_event ev;
while (!evbuf_empty(&state->inbuf)) {
evbuf_get(&state->inbuf, &ev);
evbuf_put(&state->outbuf, &ev);
}
}
/*
* filter_data - apply input filtering on new incoming data
* @state: mtdev state
* @dev: device capabilities
* @data: the incoming data to filter
* @prop: the properties to filter
* @slot: the slot the data refers to
*/
static void filter_data(const struct mtdev_state *state,
const struct mtdev *dev,
struct mtdev_slot *data, bitmask_t prop,
int slot)
{
int i;
foreach_bit(i, prop) {
int fuzz = mtdev_get_abs_fuzz(dev, mtdev_mt2abs(i));
int oldval = get_sval(&state->data[slot], i);
int value = get_sval(data, i);
set_sval(data, i, defuzz(value, oldval, fuzz));
}
}
/*
* push_slot_changes - propagate state changes
* @state: mtdev state
* @data: the incoming data to propagate
* @prop: the properties to propagate
* @slot: the slot the data refers to
* @syn: reference to the SYN_REPORT event
*/
static void push_slot_changes(struct mtdev_state *state,
const struct mtdev_slot *data, bitmask_t prop,
int slot, const struct input_event *syn)
{
struct input_event ev;
int i, count = 0;
foreach_bit(i, prop)
if (get_sval(&state->data[slot], i) != get_sval(data, i))
count++;
if (!count)
return;
ev.time = syn->time;
ev.type = EV_ABS;
ev.code = ABS_MT_SLOT;
ev.value = slot;
if (state->slot != ev.value) {
evbuf_put(&state->outbuf, &ev);
state->slot = ev.value;
}
foreach_bit(i, prop) {
ev.code = mtdev_mt2abs(i);
ev.value = get_sval(data, i);
if (get_sval(&state->data[slot], i) != ev.value) {
evbuf_put(&state->outbuf, &ev);
set_sval(&state->data[slot], i, ev.value);
}
}
}
/*
* apply_typeA_changes - parse and propagate state changes
* @state: mtdev state
* @dev: device capabilities
* @data: array of data to apply
* @prop: array of properties to apply
* @size: number of contacts in array
* @syn: reference to the SYN_REPORT event
*/
static void apply_typeA_changes(struct mtdev_state *state,
const struct mtdev *dev,
struct mtdev_slot *data, const bitmask_t *prop,
int size, const struct input_event *syn)
{
bitmask_t unused = ~state->used;
bitmask_t used = 0;
int i, slot, id;
for (i = 0; i < size; i++) {
id = data[i].tracking_id;
foreach_bit(slot, state->used) {
if (state->data[slot].tracking_id != id)
continue;
filter_data(state, dev, &data[i], prop[i], slot);
push_slot_changes(state, &data[i], prop[i], slot, syn);
SETBIT(used, slot);
id = MT_ID_NULL;
break;
}
if (id != MT_ID_NULL) {
slot = firstbit(unused);
push_slot_changes(state, &data[i], prop[i], slot, syn);
SETBIT(used, slot);
CLEARBIT(unused, slot);
}
}
/* clear unused slots and update slot usage */
foreach_bit(slot, state->used & ~used) {
struct mtdev_slot tdata = state->data[slot];
bitmask_t tprop = BITMASK(mtdev_abs2mt(ABS_MT_TRACKING_ID));
tdata.tracking_id = MT_ID_NULL;
push_slot_changes(state, &tdata, tprop, slot, syn);
}
state->used = used;
}
/*
* convert_A_to_B - propagate a type A packet as a type B packet
* @state: mtdev state
* @dev: device capabilities
* @syn: reference to the SYN_REPORT event
*/
static void convert_A_to_B(struct mtdev_state *state,
const struct mtdev *dev,
const struct input_event *syn)
{
struct mtdev_slot data[DIM_FINGER];
bitmask_t prop[DIM_FINGER];
int size = process_typeA(state, data, prop);
if (size < 0)
return;
if (!mtdev_has_mt_event(dev, ABS_MT_TRACKING_ID)) {
bitmask_t touch = 0;
int i;
for (i = 0; i < size; i++)
MODBIT(touch, i, istouch(&data[i], dev));
assign_tracking_id(state, dev, data, prop, size, touch);
}
apply_typeA_changes(state, dev, data, prop, size, syn);
}
struct mtdev *mtdev_new(void)
{
return calloc(1, sizeof(struct mtdev));
}
int mtdev_init(struct mtdev *dev)
{
int i;
memset(dev, 0, sizeof(struct mtdev));
dev->state = calloc(1, sizeof(struct mtdev_state));
if (!dev->state)
return -ENOMEM;
for (i = 0; i < DIM_FINGER; i++)
dev->state->data[i].tracking_id = MT_ID_NULL;
return 0;
}
int mtdev_open(struct mtdev *dev, int fd)
{
int ret;
ret = mtdev_init(dev);
if (ret)
goto error;
ret = mtdev_configure(dev, fd);
if (ret)
goto mtdev;
return 0;
mtdev:
mtdev_close(dev);
error:
return ret;
}
struct mtdev *mtdev_new_open(int fd)
{
struct mtdev *dev;
dev = mtdev_new();
if (!dev)
return NULL;
if (!mtdev_open(dev, fd))
return dev;
mtdev_delete(dev);
return NULL;
}
void mtdev_put_event(struct mtdev *dev, const struct input_event *ev)
{
struct mtdev_state *state = dev->state;
if (ev->type == EV_SYN && ev->code == SYN_REPORT) {
bitmask_t head = state->outbuf.head;
if (mtdev_has_mt_event(dev, ABS_MT_SLOT))
process_typeB(state);
else
convert_A_to_B(state, dev, ev);
if (state->outbuf.head != head)
evbuf_put(&state->outbuf, ev);
} else {
evbuf_put(&state->inbuf, ev);
}
}
void mtdev_close_delete(struct mtdev *dev)
{
mtdev_close(dev);
mtdev_delete(dev);
}
void mtdev_close(struct mtdev *dev)
{
free(dev->state);
memset(dev, 0, sizeof(struct mtdev));
}
void mtdev_delete(struct mtdev *dev)
{
free(dev);
}