/***************************************************************************** * * mtview - Multitouch Viewer (GPLv3 license) * * 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 #if HAVE_XI #include #endif #include #include #include #include #define DEF_FRAC 0.15 #define DEF_WIDTH 0.05 #define DIM_TOUCH 32 static int opcode; struct windata { Display *dsp; Window win; GC gc; int screen; float off_x, off_y; unsigned long white, black; unsigned long color[DIM_TOUCH]; int id[DIM_TOUCH]; }; static inline float max(float a, float b) { return b > a ? b : a; } static unsigned long new_color(struct windata *w) { return lrand48() & 0xffffff; } static void clear_screen(utouch_frame_handle fh, struct windata *w) { const struct utouch_surface *s = utouch_frame_get_surface(fh); int width = s->mapped_max_x - s->mapped_min_x; int height = s->mapped_max_y - s->mapped_min_y; XSetForeground(w->dsp, w->gc, w->black); XFillRectangle(w->dsp, w->win, w->gc, 0, 0, width, height); } static void output_touch(utouch_frame_handle fh, struct windata *w, const struct utouch_contact *t) { const struct utouch_surface *s = utouch_frame_get_surface(fh); float dx = s->mapped_max_x - s->mapped_min_x; float dy = s->mapped_max_y - s->mapped_min_y; float x = t->x - w->off_x, y = t->y - w->off_y; float major = 0, minor = 0, angle = 0; if (s->use_pressure) { major = DEF_FRAC * t->pressure * dy; minor = DEF_FRAC * t->pressure * dx; angle = 0; } if (s->use_touch_major) { major = t->touch_major; minor = t->touch_minor; angle = t->orientation; } if (major == 0 && minor == 0) { major = DEF_WIDTH * dy; minor = DEF_WIDTH * dx; } float ac = fabs(cos(angle)); float as = fabs(sin(angle)); float mx = max(minor * ac, major * as); float my = max(major * ac, minor * as); if (w->id[t->slot] != t->id) { w->id[t->slot] = t->id; w->color[t->slot] = new_color(w); } XSetForeground(w->dsp, w->gc, w->color[t->slot]); XFillArc(w->dsp, w->win, w->gc, x - mx / 2, y - my / 2, mx, my, 0, 360 * 64); XFlush(w->dsp); } static void report_frame(utouch_frame_handle fh, const struct utouch_frame *frame, struct windata *w) { int i; for (i = 0; i < frame->num_active; i++) output_touch(fh, w, frame->active[i]); } static int init_window(struct windata *w) { int event, err; int i; memset(w, 0, sizeof(w)); for (i = 0; i < DIM_TOUCH; i++) w->id[i] = -1; w->dsp = XOpenDisplay(NULL); if (!w->dsp) return -1; if (!XQueryExtension(w->dsp, "XInputExtension", &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, 200, 200, 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 windata *w) { XDestroyWindow(w->dsp, w->win); XCloseDisplay(w->dsp); } static void set_screen_size_mtdev(utouch_frame_handle fh, struct windata *w, XEvent *xev) { struct utouch_surface *s = utouch_frame_get_surface(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; } fprintf(stderr, "map: %f %f %f %f %f %f\n", w->off_x, w->off_y, s->mapped_min_x, s->mapped_min_y, s->mapped_max_x, s->mapped_max_y); } static void run_window_mtdev(utouch_frame_handle fh, struct mtdev *dev, int fd) { const struct utouch_frame *frame; struct input_event iev; struct windata w; XEvent xev; if (init_window(&w)) return; clear_screen(fh, &w); set_screen_size_mtdev(fh, &w, 0); XSelectInput(w.dsp, w.win, StructureNotifyMask); while (1) { while (!mtdev_idle(dev, fd, 100)) { while (mtdev_get(dev, fd, &iev, 1) > 0) { frame = utouch_frame_pump_mtdev(fh, &iev); if (frame) report_frame(fh, frame, &w); } } while (XPending(w.dsp)) { XNextEvent(w.dsp, &xev); set_screen_size_mtdev(fh, &w, &xev); } } term_window(&w); } static int run_mtdev(const char *name) { struct evemu_device *evemu; struct mtdev *mtdev; utouch_frame_handle fh; int fd; fd = open(name, O_RDONLY | O_NONBLOCK); if (fd < 0) { fprintf(stderr, "error: could not open device\n"); return -1; } if (ioctl(fd, EVIOCGRAB, 1)) { fprintf(stderr, "error: could not grab the device\n"); return -1; } evemu = evemu_new(0); if (!evemu || evemu_extract(evemu, fd)) { fprintf(stderr, "error: could not describe device\n"); return -1; } if (!utouch_frame_is_supported_mtdev(evemu)) { fprintf(stderr, "error: unsupported device\n"); return -1; } mtdev = mtdev_new_open(fd); if (!mtdev) { fprintf(stderr, "error: could not open mtdev\n"); return -1; } fh = utouch_frame_new_engine(100, 32, 100); if (!fh || utouch_frame_init_mtdev(fh, evemu)) { fprintf(stderr, "error: could not init frame\n"); return -1; } run_window_mtdev(fh, mtdev, fd); utouch_frame_delete_engine(fh); mtdev_close_delete(mtdev); evemu_delete(evemu); ioctl(fd, EVIOCGRAB, 0); close(fd); return 0; } #if HAVE_XI static void handle_event_xi2(struct windata *w, utouch_frame_handle fh, XEvent *ev) { XConfigureEvent *cev = (XConfigureEvent *)ev; XGenericEventCookie *gev = &ev->xcookie; const struct utouch_frame *frame; switch(ev->type) { case ConfigureNotify: if (cev->window == XDefaultRootWindow(cev->display)) { utouch_frame_configure_xi2(fh, cev); } else { w->off_x = cev->x; w->off_y = cev->y; } break; case GenericEvent: if (!XGetEventData(w->dsp, gev)) break; if (gev->type == GenericEvent && gev->extension == opcode) { frame = utouch_frame_pump_xi2(fh, gev->data); if (frame) report_frame(fh, frame, w); } XFreeEventData(w->dsp, gev); break; } } static void run_window_xi2(struct windata *w, utouch_frame_handle fh, XIDeviceInfo *dev) { const struct utouch_frame *frame; XIEventMask mask; fprintf(stderr, "xi2 running\n"); XSelectInput(w->dsp, w->win, StructureNotifyMask); XSelectInput(w->dsp, XDefaultRootWindow(w->dsp), StructureNotifyMask); mask.deviceid = dev->deviceid; mask.mask_len = XIMaskLen(XI_LASTEVENT); mask.mask = calloc(mask.mask_len, sizeof(char)); XISetMask(mask.mask, XI_PropertyEvent); XISetMask(mask.mask, XI_TouchBegin); XISetMask(mask.mask, XI_TouchUpdate); XISetMask(mask.mask, XI_TouchEnd); XISelectEvents(w->dsp, w->win, &mask, 1); while (1) { XEvent ev; XNextEvent(w->dsp, &ev); handle_event_xi2(w, fh, &ev); } } static int run_xi2(int id) { struct windata w; XIDeviceInfo *info, *dev; utouch_frame_handle fh; int ndevice; int i; if (init_window(&w)) { fprintf(stderr, "error: could not init window\n"); return -1; } info = XIQueryDevice(w.dsp, XIAllDevices, &ndevice); dev = 0; for (i = 0; i < ndevice; i++) if (info[i].deviceid == id) dev = &info[i]; if (!dev) return -1; if (!utouch_frame_is_supported_xi2(w.dsp, dev)) { fprintf(stderr, "error: unsupported device\n"); return -1; } fh = utouch_frame_new_engine(100, 32, 100); if (!fh || utouch_frame_init_xi2(fh, w.dsp, dev)) { fprintf(stderr, "error: could not init frame\n"); return -1; } run_window_xi2(&w, fh, dev); utouch_frame_delete_engine(fh); XIFreeDeviceInfo(info); term_window(&w); return 0; } #else static int run_xi2(int id) { fprintf(stderr, "XI2.1 not supported\n"); return 0; } #endif int main(int argc, char *argv[]) { int id, ret; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return -1; } id = atoi(argv[1]); if (id) ret = run_xi2(id); else ret = run_mtdev(argv[1]); return ret; }