/*****************************************************************************
*
* utouch-frame - Touch Frame 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 .
*
****************************************************************************/
#include "frame-impl.h"
#include
#include
#include
#include
#include
#define MAX(a, b) ((a) > (b) ? (a) : (b))
unsigned int utouch_frame_get_version(void)
{
return UTOUCH_FRAME_VERSION;
}
static void destroy_slots(struct utouch_contact **slots, int nslot)
{
int i;
if (slots) {
for (i = nslot - 1; i >= 0; i--)
free(slots[i]);
free(slots);
}
}
static void destroy_frame(struct utouch_frame *frame, int nslot)
{
if (frame) {
destroy_slots(frame->slots, nslot);
free(frame->active);
free(frame);
}
}
static void destroy_frames(struct utouch_frame **frames, int nframe, int nslot)
{
int i;
if (frames) {
for (i = nframe - 1; i >= 0; i--)
destroy_frame(frames[i], nslot);
free(frames);
}
}
static struct utouch_contact **create_slots(int nslot, int size)
{
struct utouch_contact **slots;
struct utouch_contact *s;
int i;
slots = calloc(nslot, sizeof(slots[0]));
if (!slots)
return 0;
for (i = 0; i < nslot; i++) {
s = calloc(1, size);
if (!s)
goto out;
slots[i] = s;
s->slot = i;
s->id = -1;
}
return slots;
out:
destroy_slots(slots, nslot);
return 0;
}
static struct utouch_frame *create_frame(int nslot,
int frame_size, int slot_size)
{
struct utouch_frame *frame;
int i;
frame = calloc(1, frame_size);
if (!frame)
return 0;
frame->active = calloc(nslot, sizeof(frame->active[0]));
frame->slots = create_slots(nslot, slot_size);
if (!frame->active || !frame->slots)
goto out;
return frame;
out:
destroy_frame(frame, nslot);
return 0;
}
static struct utouch_frame **create_frames(int nframe, int nslot,
int frame_size, int slot_size)
{
struct utouch_frame **frames;
struct utouch_frame *f;
int i;
frames = calloc(nframe, sizeof(frames[0]));
if (!frames)
return 0;
for (i = 0; i < nframe; i++) {
f = create_frame(nslot, frame_size, slot_size);
if (!f)
goto out;
frames[i] = f;
}
return frames;
out:
destroy_frames(frames, nframe, nslot);
return 0;
}
utouch_frame_handle utouch_frame_new_engine_raw(unsigned int nframe,
unsigned int nslot,
unsigned int rate,
unsigned int version,
unsigned int surface_size,
unsigned int frame_size,
unsigned int slot_size)
{
struct utouch_frame *f, *pf;
utouch_frame_handle fh;
int i, j;
fh = calloc(1, sizeof(struct utouch_frame_engine));
if (!fh)
return 0;
fh->num_frames = nframe;
fh->num_slots = nslot;
fh->hold_ms = 1000 / rate;
surface_size = MAX(surface_size, sizeof(struct utouch_surface));
frame_size = MAX(frame_size, sizeof(struct utouch_frame));
slot_size = MAX(slot_size, sizeof(struct utouch_contact));
fh->surface = calloc(1, surface_size);
fh->frames = create_frames(nframe, nslot, frame_size, slot_size);
fh->next = create_frame(nslot, frame_size, slot_size);
if (!fh->surface || !fh->frames || !fh->next)
goto out;
pf = fh->frames[nframe - 1];
for (i = 0; i < nframe; i++) {
f = fh->frames[i];
for (j = 0; j < nslot; j++)
f->slots[j]->prev = pf->slots[j];
f->prev = pf;
pf = f;
}
return fh;
out:
utouch_frame_delete_engine(fh);
return 0;
}
void utouch_frame_delete_engine(utouch_frame_handle fh)
{
destroy_frame(fh->next, fh->num_slots);
destroy_frames(fh->frames, fh->num_frames, fh->num_slots);
free(fh->surface);
free(fh);
}
struct utouch_surface *utouch_frame_get_surface(utouch_frame_handle fh)
{
return fh->surface;
}
struct utouch_contact *utouch_frame_get_current_slot(utouch_frame_handle fh)
{
return fh->next->slots[fh->slot];
}
int utouch_frame_set_current_slot(utouch_frame_handle fh, int slot)
{
if (slot < 0 || slot >= fh->num_slots)
return -EINVAL;
fh->slot = slot;
return 0;
}
int utouch_frame_set_current_id(utouch_frame_handle fh, int id)
{
struct utouch_contact *t;
int i;
for (i = 0; i < fh->num_slots; i++) {
t = fh->next->slots[i];
if (t->id == id)
return utouch_frame_set_current_slot(fh, i);
}
for (i = 0; i < fh->num_slots; i++) {
t = fh->next->slots[i];
if (t->id < 0) {
t->id = id;
return utouch_frame_set_current_slot(fh, i);
}
}
return -ENOMEM;
}
static void transform_slot(struct utouch_contact *slot,
const struct utouch_surface *s)
{
float fx = (s->mapped_max_x - s->mapped_min_x) / (s->max_x - s->min_x);
float fy = (s->mapped_max_y - s->mapped_min_y) / (s->max_y - s->min_y);
/* assume clipped view for asymmetrical scaling */
float f = MAX(fx, fy);
slot->x = fx * (slot->x - s->min_x) + s->mapped_min_x;
slot->y = fy * (slot->y - s->min_y) + s->mapped_min_y;
slot->touch_major *= f;
slot->touch_minor *= f;
slot->width_major *= f;
slot->width_minor *= f;
slot->orientation *= M_PI_2 / s->max_orient;
slot->pressure *= s->mapped_max_pressure / s->max_pressure;
slot->distance *= f;
}
static void copy_contact(utouch_frame_handle fh,
struct utouch_contact *a,
const struct utouch_contact *b)
{
struct utouch_surface *s = fh->surface;
a->active = b->id != -1;
a->id = b->id;
a->tool_type = b->tool_type;
a->x = b->x;
a->y = b->y;
a->touch_major = b->touch_major;
a->touch_minor = s->use_touch_minor && b->touch_minor > 0 ?
b->touch_minor : b->touch_major;
a->width_major = b->width_major;
a->width_minor = s->use_width_minor && b->width_minor > 0 ?
b->width_minor : b->width_major;
a->orientation = b->orientation;
a->pressure = b->pressure;
a->distance = b->distance;
transform_slot(a, s);
}
static int detect_addrem(const struct utouch_contact *a,
const struct utouch_contact *b)
{
return a->id != b->id || a->tool_type != b->tool_type;
}
static int detect_mod(const struct utouch_contact *a,
const struct utouch_contact *b)
{
return a->x != b->x || a->y != b->y ||
a->touch_major != b->touch_major ||
a->touch_minor != b->touch_minor ||
a->width_major != b->width_major ||
a->width_minor != b->width_minor ||
a->orientation != b->orientation ||
a->pressure != b->pressure ||
a->distance != b->distance;
}
static utouch_frame_time_t get_time_ms()
{
static const utouch_frame_time_t ms = 1000;
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_usec / ms + tv.tv_sec * ms;
}
const struct utouch_frame *utouch_frame_sync(utouch_frame_handle fh,
utouch_frame_time_t time)
{
struct utouch_frame *frame = fh->frames[fh->frame];
const struct utouch_frame *prev = frame->prev;
struct utouch_frame *next = fh->next;
int naddrem = 0, nmod = 0;
int i;
frame->num_active = 0;
for (i = 0; i < fh->num_slots; i++) {
struct utouch_contact *p = frame->slots[i];
const struct utouch_contact *q = next->slots[i];
copy_contact(fh, p, q);
if (p->active)
frame->active[frame->num_active++] = p;
naddrem += detect_addrem(p->prev, p);
nmod += detect_mod(p->prev, p);
}
if (naddrem + nmod == 0)
return 0;
frame->time = time ? time : get_time_ms();
if (naddrem == 0 && frame->time < prev->time + fh->hold_ms)
return 0;
if (frame->num_active != prev->num_active) {
next->mod_time = frame->time;
next->revision++;
}
if (naddrem) {
next->slot_mod_time = frame->time;
next->slot_revision++;
}
frame->revision = next->revision;
frame->slot_revision = next->slot_revision;
frame->mod_time = next->mod_time;
frame->slot_mod_time = next->slot_mod_time;
frame->sequence_id = next->sequence_id++;
fh->frame = (fh->frame + 1) % fh->num_frames;
return frame;
}