/*****************************************************************************
*
* 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 "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct appdata {
int opcode;
struct grail_coord min, max;
int fd;
struct evemu_device *evemu;
struct mtdev *mtdev;
utouch_frame_handle fh;
struct grail *ge;
struct grail_coord pos[4];
struct grail_coord touch[32];
struct grail_coord pivot;
XPoint pts[5];
int ntouch;
Display *dsp;
Window win;
GC gc;
int screen;
float off_x, off_y;
unsigned long white, black;
};
static void clear_screen(struct appdata *w)
{
const struct utouch_surface *s = utouch_frame_get_surface(w->fh);
int width = s->mapped_max_x - s->mapped_min_x;
int height = s->mapped_max_y - s->mapped_min_y;
int i;
XSetForeground(w->dsp, w->gc, w->white);
XFillRectangle(w->dsp, w->win, w->gc, 0, 0, width, height);
for (i = 0; i < 5; i++) {
w->pts[i].x = w->pos[i % 4].x - w->off_x;
w->pts[i].y = w->pos[i % 4].y - w->off_y;
}
XSetForeground(w->dsp, w->gc, w->black);
XDrawLines(w->dsp, w->win, w->gc, w->pts, 5, CoordModeOrigin);
}
static void draw_object(struct appdata *w)
{
static const double frac = 0.01;
const struct utouch_surface *s = utouch_frame_get_surface(w->fh);
double d = frac * (s->mapped_max_x - s->mapped_min_x);
int i;
XDrawLines(w->dsp, w->win, w->gc, w->pts, 5, CoordModeOrigin);
XFillArc(w->dsp, w->win, w->gc,
w->pivot.x - d / 2, w->pivot.y - d / 2,
d, d, 0, 360 * 64);
d *= 4;
for (i = 0; i < w->ntouch; i++)
XFillArc(w->dsp, w->win, w->gc,
w->touch[i].x - d / 2, w->touch[i].y - d / 2,
d, d, 0, 360 * 64);
}
static void report_frame(struct appdata *w,
const struct utouch_frame *touch)
{
const struct grail_control *ctl = grail_get_control(w->ge);
const struct grail_frame *frame;
const struct grail_element *slot;
struct grail_coord tmp;
int i;
if (!touch)
return;
frame = grail_pump_frame(w->ge, touch);
if (!frame || !frame->num_ongoing || !frame->prev->num_ongoing)
return;
slot = frame->ongoing[frame->num_ongoing - 1];
if (slot->transform[0] == 0 && slot->transform[1] == 0)
return;
for (i = 0; i < 4; i++) {
grail_element_transform(slot, &tmp, &w->pos[i]);
w->pos[i] = tmp;
}
XSetForeground(w->dsp, w->gc, w->white);
draw_object(w);
w->ntouch = touch->num_active;
for (i = 0; i < w->ntouch; i++) {
w->touch[i].x = touch->active[i]->x - w->off_x;
w->touch[i].y = touch->active[i]->y - w->off_y;
}
w->pivot.x = slot->pivot.x - w->off_x;
w->pivot.y = slot->pivot.y - w->off_y;
for (i = 0; i < 5; i++) {
w->pts[i].x = w->pos[i % 4].x - w->off_x;
w->pts[i].y = w->pos[i % 4].y - w->off_y;
}
XSetForeground(w->dsp, w->gc, w->black);
draw_object(w);
XFlush(w->dsp);
}
static int init_window(struct appdata *w)
{
int event, err;
w->pos[0].x = 100;
w->pos[0].y = 100;
w->pos[1].x = 200;
w->pos[1].y = 100;
w->pos[2].x = 200;
w->pos[2].y = 200;
w->pos[3].x = 100;
w->pos[3].y = 200;
w->dsp = XOpenDisplay(NULL);
if (!w->dsp)
return -1;
if (!XQueryExtension(w->dsp, "XInputExtension",
&w->opcode, &event, &err))
return -1;
w->screen = DefaultScreen(w->dsp);
w->white = WhitePixel(w->dsp, w->screen);
w->black = BlackPixel(w->dsp, w->screen);
w->win = XCreateSimpleWindow(w->dsp, XDefaultRootWindow(w->dsp),
0, 0, 600, 600, 0, w->black, w->white);
w->gc = DefaultGC(w->dsp, w->screen);
XMapWindow(w->dsp, w->win);
XFlush(w->dsp);
return 0;
}
static void term_window(struct appdata *w)
{
XDestroyWindow(w->dsp, w->win);
XCloseDisplay(w->dsp);
}
static void set_screen_size_mtdev(struct appdata *w, XEvent *xev)
{
struct utouch_surface *s = utouch_frame_get_surface(w->fh);
XConfigureEvent *cev = (XConfigureEvent *)xev;
s->mapped_min_x = 0;
s->mapped_min_y = 0;
s->mapped_max_x = DisplayWidth(w->dsp, w->screen);
s->mapped_max_y = DisplayHeight(w->dsp, w->screen);
s->mapped_max_pressure = 1;
if (cev) {
w->off_x = cev->x;
w->off_y = cev->y;
}
}
static void loop_device_mtdev(struct appdata *w)
{
const struct utouch_frame *tf;
struct input_event iev;
XEvent xev;
clear_screen(w);
set_screen_size_mtdev(w, 0);
XSelectInput(w->dsp, w->win, StructureNotifyMask);
while (1) {
while (!mtdev_idle(w->mtdev, w->fd, 100)) {
while (mtdev_get(w->mtdev, w->fd, &iev, 1) > 0) {
tf = utouch_frame_pump_mtdev(w->fh, &iev);
report_frame(w, tf);
}
}
while (XPending(w->dsp)) {
XNextEvent(w->dsp, &xev);
set_screen_size_mtdev(w, &xev);
}
}
}
static int run_mtdev(struct appdata *w, const char *path)
{
w->fd = open(path, O_RDONLY | O_NONBLOCK);
if (w->fd < 0) {
fprintf(stderr, "error: could not open device\n");
return -1;
}
if (ioctl(w->fd, EVIOCGRAB, 1)) {
fprintf(stderr, "error: could not grab the device\n");
return -1;
}
w->evemu = evemu_new(NULL);
if (!w->evemu)
return -1;
if (evemu_extract(w->evemu, w->fd))
return -1;
w->mtdev = mtdev_new_open(w->fd);
if (!w->mtdev)
return -1;
w->fh = utouch_frame_new_engine(500, 32, 100);
if (!w->fh)
return -1;
if (utouch_frame_init_mtdev(w->fh, w->evemu))
return -1;
w->ge = grail_new(w->fh, 100, 0);
if (!w->ge)
return -1;
grail_get_control(w->ge)->drop_x_ms = 1e8;
grail_get_control(w->ge)->drop_y_ms = 1e8;
grail_get_control(w->ge)->drop_scale_ms = 1e8;
grail_get_control(w->ge)->drop_angle_ms = 1e8;
loop_device_mtdev(w);
grail_delete(w->ge);
utouch_frame_delete_engine(w->fh);
mtdev_close_delete(w->mtdev);
evemu_delete(w->evemu);
ioctl(w->fd, EVIOCGRAB, 0);
close(w->fd);
}
#if HAVE_XI
static void loop_device_xi2(struct appdata *w, int id)
{
const struct utouch_frame *tf;
XIEventMask mask;
mask.deviceid = id;
mask.mask_len = XIMaskLen(XI_LASTEVENT);
mask.mask = calloc(mask.mask_len, sizeof(char));
XISetMask(mask.mask, XI_TouchBegin);
XISetMask(mask.mask, XI_TouchUpdate);
XISetMask(mask.mask, XI_TouchEnd);
XISetMask(mask.mask, XI_PropertyEvent);
XISelectEvents(w->dsp, w->win, &mask, 1);
XSelectInput(w->dsp, DefaultRootWindow(w->dsp), StructureNotifyMask);
while (1) {
XEvent ev;
XIDeviceEvent *event = (void *)&ev;
XGenericEventCookie *cookie = &ev.xcookie;
XNextEvent(w->dsp, &ev);
if (XGetEventData(w->dsp, cookie) &&
cookie->type == GenericEvent &&
cookie->extension == w->opcode) {
tf = utouch_frame_pump_xi2(w->fh, cookie->data);
report_frame(w, tf);
}
XFreeEventData(w->dsp, cookie);
}
}
static int run_xi2(struct appdata *w, int id)
{
XIDeviceInfo *info, *dev;
int ndevices, i;
info = XIQueryDevice(w->dsp, XIAllDevices, &ndevices);
dev = 0;
for (i = 0; i < ndevices; i++)
if (info[i].deviceid == id)
dev = &info[i];
if (!dev)
return -1;
w->fh = utouch_frame_new_engine(500, 32, 100);
if (!w->fh)
return -1;
if (utouch_frame_init_xi2(w->fh, w->dsp, dev))
return -1;
w->ge = grail_new(w->fh, 100, 0);
if (!w->ge)
return -1;
loop_device_xi2(w, id);
grail_delete(w->ge);
utouch_frame_delete_engine(w->fh);
XIFreeDeviceInfo(info);
return 0;
}
#else
static int run_xi2(struct appdata *w, int id)
{
fprintf(stderr, "X not supported in this build (use --with-xi)\n");
return 0;
}
#endif
int main(int argc, char **argv)
{
struct appdata w;
int id, ret;
if (argc < 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return -1;
}
memset(&w, 0, sizeof(w));
if (init_window(&w))
return -1;
id = atoi(argv[1]);
if (id)
ret = run_xi2(&w, id);
else
ret = run_mtdev(&w, argv[1]);
term_window(&w);
return ret;
}