From 3244deffa1aedbb6c1af8c2dc993f1d771373c6a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 15 Feb 2011 14:48:57 +0100 Subject: Optional XI2.1 support This patchs adds optional XI2.1 support to utouch-frame. The utouch/frame-xi2.h header contains helper functions equivalent to utouch/frame-mtdev.h. In addition, the device-to-screen mapping is handled via XEvents and optionally XIPropertyEvent. The latter allows free device placement via xinput set-prop, useful in multi- head setups, thanks to Chase Douglas. Touch frame events can be tested using the utouch-frame-test-xi2 tool. Use --with-xi to enable. Signed-off-by: Henrik Rydberg --- .gitignore | 1 + configure.ac | 9 ++ include/utouch/frame-xi2.h | 49 +++++++ src/Makefile.am | 8 ++ src/frame-impl.h | 2 + src/frame-xi2.c | 302 ++++++++++++++++++++++++++++++++++++++++ src/frame.c | 1 + tools/Makefile.am | 12 ++ tools/utouch-frame-test-xi2.c | 238 +++++++++++++++++++++++++++++++ tools/utouch-frame-test-xi2.txt | 22 +++ 10 files changed, 644 insertions(+) create mode 100644 include/utouch/frame-xi2.h create mode 100644 src/frame-xi2.c create mode 100644 tools/utouch-frame-test-xi2.c create mode 100644 tools/utouch-frame-test-xi2.txt diff --git a/.gitignore b/.gitignore index cf601fa..75c2197 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,4 @@ core .bzr patches tools/utouch-frame-test-mtdev +tools/utouch-frame-test-xi2 diff --git a/configure.ac b/configure.ac index 980a044..12b275e 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,15 @@ AC_PROG_INSTALL PKG_CHECK_MODULES([MTDEV], [mtdev >= 1.1]) PKG_CHECK_MODULES([EVEMU], [utouch-evemu >= 1.0]) +AC_ARG_WITH([xi], AS_HELP_STRING([--with-xi], [Build with XI2.1 support])) +AM_CONDITIONAL([HAVE_XI], [test "x$with_xi" != "x"]) + +AS_IF([test "x$with_xi" = "xyes"], [ + PKG_CHECK_MODULES([X11], [x11]) + PKG_CHECK_MODULES(XINPUT, x11 xext [xi >= 1.2] [inputproto >= 1.5]) + PKG_CHECK_MODULES(XI2_1, [xi >= 1.4.99.1] [inputproto >= 2.0.99.1]) +]) + AC_CHECK_PROG([ASCIIDOC], [a2x], [a2x]) AM_CONDITIONAL([HAVE_DOCTOOLS], [test "x$ASCIIDOC" != "x"]) AS_IF([test "x$ASCIIDOC" = "x"], diff --git a/include/utouch/frame-xi2.h b/include/utouch/frame-xi2.h new file mode 100644 index 0000000..5849c40 --- /dev/null +++ b/include/utouch/frame-xi2.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * + * utouch-frame - Touch Frame 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 . + * + ****************************************************************************/ + +#ifndef _UTOUCH_FRAME_XI2_H +#define _UTOUCH_FRAME_XI2_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MTDEV_NO_LEGACY_API + +#include +#include + +int utouch_frame_is_supported_xi2(Display *dpy, const XIDeviceInfo *dev); + +int utouch_frame_init_xi2(utouch_frame_handle fh, + Display *dpy, const XIDeviceInfo *dev); + +int utouch_frame_configure_xi2(utouch_frame_handle fh, + const XConfigureEvent *ev); + +const struct utouch_frame * +utouch_frame_pump_xi2(utouch_frame_handle fh, const XIDeviceEvent *ev); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index a6f98f9..c3fd3b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,3 +19,11 @@ libutouch_frameincludedir = $(includedir)/utouch libutouch_frameinclude_HEADERS = \ $(top_srcdir)/include/utouch/frame.h \ $(top_srcdir)/include/utouch/frame-mtdev.h + +if HAVE_XI + +libutouch_frame_la_LDFLAGS += $(XINPUT_LIBS) $(X11_LIBS) +libutouch_frame_la_SOURCES += frame-xi2.c +libutouch_frameinclude_HEADERS += $(top_srcdir)/include/utouch/frame-xi2.h + +endif diff --git a/src/frame-impl.h b/src/frame-impl.h index 38affe7..b072de9 100644 --- a/src/frame-impl.h +++ b/src/frame-impl.h @@ -33,6 +33,8 @@ struct utouch_frame_engine { struct utouch_surface *surface; struct utouch_frame **frames; struct utouch_frame *next; + int *evmap; + float map[9]; }; #endif diff --git a/src/frame-xi2.c b/src/frame-xi2.c new file mode 100644 index 0000000..a78d9bf --- /dev/null +++ b/src/frame-xi2.c @@ -0,0 +1,302 @@ +/***************************************************************************** + * + * 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 +#include +#include +#include "frame-impl.h" +#include +#include +#include +#include +#include +#include + +static Atom transform_atom; + +#define set_field(fh, name, number, field) \ + if (!strcmp(name, AXIS_LABEL_PROP_##field) && \ + (fh->evmap[number] = field)) + +int utouch_frame_is_supported_xi2(Display *dpy, const XIDeviceInfo *dev) +{ + + int i; + + for (i = 0; i < dev->num_classes; i++) + if (dev->classes[i]->type == XITouchClass) + return 1; + + return 0; +} + +static void init_properties(utouch_frame_handle fh, + Display *dpy, const XIDeviceInfo *dev) +{ + struct utouch_surface *s = fh->surface; + XITouchClassInfo *v; + int i; + + for (i = 0; i < dev->num_classes; i++) { + v = (void *)dev->classes[i]; + if (v->type != XITouchClass) + continue; + switch(v->mode) { + case XIDependentTouch: + s->needs_pointer = 1; + break; + case XIDirectTouch: + s->is_direct = 1; + break; + default: + fprintf(stderr, "unknown mode\n"); + break; + } + } + + if (transform_atom == None) + transform_atom = XInternAtom(dpy, XI_PROP_TRANSFORM, True); + + fh->map[0] = fh->map[4] = fh->map[8] = 1; +} + +static void update_transform(utouch_frame_handle fh, Display *dpy, int devid) +{ + unsigned long nitem, nbyte; + unsigned char *data; + int format; + Atom type; + + if (transform_atom == None) + return; + + XIGetProperty(dpy, devid, transform_atom, 0, 9, False, + AnyPropertyType, &type, &format, &nitem, &nbyte, &data); + + if (format == 32 && nitem == 9 && nbyte == 0) + memcpy(fh->map, data, sizeof(fh->map)); + else + fprintf(stderr, "Invalid transform matrix\n"); + + XFree(data); +} + +static void update_screen(utouch_frame_handle fh, Display *dpy) +{ + struct utouch_surface *s = fh->surface; + XWindowAttributes attrs; + float x1, y1, x2, y2; + + XGetWindowAttributes(dpy, XDefaultRootWindow(dpy), &attrs); + + x1 = 0; + y1 = 0; + x2 = attrs.width; + y2 = attrs.height; + + s->mapped_min_x = fh->map[0] * x1 + fh->map[1] * y1 + fh->map[2]; + s->mapped_min_y = fh->map[3] * x1 + fh->map[4] * y1 + fh->map[5]; + s->mapped_max_x = fh->map[0] * x2 + fh->map[1] * y2 + fh->map[2]; + s->mapped_max_y = fh->map[3] * x2 + fh->map[4] * y2 + fh->map[5]; + s->mapped_max_pressure = 1; + + fprintf(stderr, "frame map: %f %f %f %f %f\n", + s->mapped_min_x, s->mapped_min_y, + s->mapped_max_x, s->mapped_max_y, s->mapped_max_pressure); +} + +static void init_valuators(utouch_frame_handle fh, + Display *dpy, const XIDeviceInfo *dev) +{ + struct utouch_surface *s = fh->surface; + XITouchValuatorClassInfo *v; + char *name; + int i; + + s->phys_width = 100; + s->phys_height = 65; + s->phys_pressure = 10; + s->max_x = 1024; + s->max_y = 768; + s->max_pressure = 256; + s->max_orient = 1; + + for (i = 0; i < dev->num_classes; i++) { + v = (void *)dev->classes[i]; + if (v->type != XITouchValuatorClass) + continue; + name = XGetAtomName(dpy, v->label); + set_field(fh, name, v->number, ABS_MT_POSITION_X) { + s->min_x = v->min; + s->max_x = v->max; + if (v->resolution > 0) + s->phys_width = (v->max - v->min) / + v->resolution; + } + set_field(fh, name, v->number, ABS_MT_POSITION_Y) { + s->min_y = v->min; + s->max_y = v->max; + if (v->resolution > 0) + s->phys_height = (v->max - v->min) / + v->resolution; + } + set_field(fh, name, v->number, ABS_MT_TOUCH_MAJOR) { + s->use_touch_major = 1; + } + set_field(fh, name, v->number, ABS_MT_TOUCH_MINOR) { + s->use_touch_minor = 1; + } + set_field(fh, name, v->number, ABS_MT_WIDTH_MAJOR) { + s->use_width_major = 1; + } + set_field(fh, name, v->number, ABS_MT_WIDTH_MINOR) { + s->use_width_minor = 1; + } + set_field(fh, name, v->number, ABS_MT_ORIENTATION) { + s->use_orientation = 1; + s->max_orient = v->max > 0 ? v->max : 1; + } + set_field(fh, name, v->number, ABS_MT_PRESSURE) { + s->use_pressure = 1; + s->max_pressure = v->max > 0 ? v->max : 256; + if (v->resolution > 0) + s->phys_pressure = v->max / v->resolution; + } +#if 0 + set_field(fh, name, v->number, ABS_MT_DISTANCE) { + s->use_distance = 1; + } +#endif + XFree(name); + } +} + +int utouch_frame_init_xi2(utouch_frame_handle fh, + Display *dpy, const XIDeviceInfo *dev) +{ + int event; + int err; + + free(fh->evmap); + fh->evmap =calloc(dev->num_classes, sizeof(int)); + if (!fh->evmap) + return -ENOMEM; + + if (!utouch_frame_is_supported_xi2(dpy, dev)) + return -ENODEV; + + init_properties(fh, dpy, dev); + init_valuators(fh, dpy, dev); + update_transform(fh, dpy, dev->deviceid); + update_screen(fh, dpy); + + return 0; +} + +static int handle_event(utouch_frame_handle fh, + struct utouch_contact *slot, + int ix, float value) +{ + switch (fh->evmap[ix]) { + case ABS_MT_POSITION_X: + slot->x = value; + return 1; + case ABS_MT_POSITION_Y: + slot->y = value; + return 1; + case ABS_MT_TOUCH_MAJOR: + slot->touch_major = value; + return 1; + case ABS_MT_TOUCH_MINOR: + slot->touch_minor = value; + return 1; + case ABS_MT_WIDTH_MAJOR: + slot->width_major = value; + return 1; + case ABS_MT_WIDTH_MINOR: + slot->width_minor = value; + return 1; + case ABS_MT_ORIENTATION: + slot->orientation = value; + return 1; + case ABS_MT_PRESSURE: + slot->pressure = value; + return 1; +#ifdef ABS_MT_DISTANCE + case ABS_MT_DISTANCE: + slot->distance = value; + return 1; +#endif + case ABS_MT_TOOL_TYPE: + slot->tool_type = value; + return 1; + default: + fprintf(stderr, "did not get that one: %d\n", ix); + return 0; + } +} + +int utouch_frame_configure_xi2(utouch_frame_handle fh, + const XConfigureEvent *ev) +{ + if (ev->display) + update_screen(fh, ev->display); + return 1; +} + +const struct utouch_frame * +utouch_frame_pump_xi2(utouch_frame_handle fh, const XIDeviceEvent *ev) +{ + const XIPropertyEvent *prop_ev; + const double *value; + const unsigned char *mask; + struct utouch_contact *slot; + int touchid, nbyte, nbit, num, i; + + switch (ev->evtype) { + case XI_TouchBegin: + case XI_TouchMotion: + case XI_TouchEnd: + value = ev->valuators.values; + mask = ev->valuators.mask; + touchid = ev->detail; + nbyte = ev->valuators.mask_len; + nbit = nbyte << 3; + + utouch_frame_set_current_id(fh, touchid); + slot = utouch_frame_get_current_slot(fh); + num = 0; + for (i = 0; i < nbit; i++) + if (XIMaskIsSet(mask, i)) + handle_event(fh, slot, i, value[num++]); + if (ev->evtype == XI_TouchEnd) + utouch_frame_get_current_slot(fh)->id = -1; + return utouch_frame_sync(fh, ev->time); + case XI_PropertyEvent: + prop_ev = (XIPropertyEvent *)ev; + if (prop_ev->property == transform_atom && ev->display) { + update_transform(fh, ev->display, ev->deviceid); + update_screen(fh, ev->display); + } + break; + } + + return 0; +} diff --git a/src/frame.c b/src/frame.c index 061b170..32bd2cd 100644 --- a/src/frame.c +++ b/src/frame.c @@ -181,6 +181,7 @@ out: void utouch_frame_delete_engine(utouch_frame_handle fh) { + free(fh->evmap); destroy_frame(fh->next, fh->num_slots); destroy_frames(fh->frames, fh->num_frames, fh->num_slots); free(fh->surface); diff --git a/tools/Makefile.am b/tools/Makefile.am index 91e99ce..3b612cc 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -6,6 +6,18 @@ utouch_frame_test_mtdev_SOURCES = utouch-frame-test-mtdev.c utouch_frame_test_mtdev_LDFLAGS = -L$(top_builddir)/src/.libs/ \ -lutouch-frame -lutouch-evemu -lmtdev -lm +if HAVE_XI + +AM_CFLAGS = $(XINPUT_CFLAGS) + +bin_PROGRAMS += utouch-frame-test-xi2 + +utouch_frame_test_xi2_SOURCES = utouch-frame-test-xi2.c +utouch_frame_test_xi2_LDFLAGS = $(XINPUT_LIBS) $(X11_LIBS) \ + -L$(top_builddir)/src/.libs/ -lutouch-frame + +endif + if HAVE_DOCTOOLS dist_man_MANS = utouch-frame-test-mtdev.1 diff --git a/tools/utouch-frame-test-xi2.c b/tools/utouch-frame-test-xi2.c new file mode 100644 index 0000000..df68c1a --- /dev/null +++ b/tools/utouch-frame-test-xi2.c @@ -0,0 +1,238 @@ +/***************************************************************************** + * + * utouch-frame - Touch Frame 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 . + * + * Authors: + * Henrik Rydberg + * + ****************************************************************************/ + +#define MTDEV_NO_LEGACY_API + +#include +#include +#include +#include +#include +#include + +static int opcode; + +struct frame_test { + Display *display; + Window root, win; + XIDeviceInfo *info, *dev; + utouch_frame_handle fh; +}; + +static int init_xi2(struct frame_test *test, int id) +{ + Display *dpy; + int ndevices, i; + int event, error; + + dpy = XOpenDisplay(NULL); + if (!dpy) + return -1; + + if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) + return -1; + + test->display = dpy; + test->info = XIQueryDevice(dpy, XIAllDevices, &ndevices); + test->dev = 0; + for (i = 0; i < ndevices; i++) + if (test->info[i].deviceid == id) + test->dev = &test->info[i]; + if (!test->dev) + return -1; + + test->root = DefaultRootWindow(dpy); + test->win = XCreateSimpleWindow(dpy, test->root, + 0, 0, 400, 400, 0, + BlackPixel(dpy, 0), + WhitePixel(dpy, 0)); + + XMapWindow(test->display, test->win); + XFlush(test->display); + + return 0; +} + +static int init_frame(struct frame_test *test) +{ + test->fh = utouch_frame_new_engine(100, 32, 100); + if (!test->fh) + return -1; + return utouch_frame_init_xi2(test->fh, test->display, test->dev); +} + +static void destroy_all(struct frame_test *test) +{ + utouch_frame_delete_engine(test->fh); + XIFreeDeviceInfo(test->info); + XDestroyWindow(test->display, test->win); + XCloseDisplay(test->display); + memset(test, 0, sizeof(*test)); +} + +static void report_device_caps(struct frame_test *test) +{ + struct utouch_surface *s = utouch_frame_get_surface(test->fh); + fprintf(stderr, "device props:\n"); + if (s->needs_pointer) + fprintf(stderr, "\tpointer\n"); + if (s->is_direct) + fprintf(stderr, "\tdirect\n"); + if (s->is_buttonpad) + fprintf(stderr, "\tbuttonpad\n"); + if (s->is_semi_mt) + fprintf(stderr, "\tsemi_mt\n"); + fprintf(stderr, "device mt events:\n"); + if (s->use_touch_major) + fprintf(stderr, "\ttouch_major\n"); + if (s->use_touch_minor) + fprintf(stderr, "\ttouch_minor\n"); + if (s->use_width_major) + fprintf(stderr, "\twidth_major\n"); + if (s->use_width_minor) + fprintf(stderr, "\twidth_minor\n"); + if (s->use_orientation) + fprintf(stderr, "\torientation\n"); + if (s->use_pressure) + fprintf(stderr, "\tpressure\n"); + if (s->use_distance) + fprintf(stderr, "\tdistance\n"); +} + +static void report_frame(const struct utouch_frame *frame, + const XIDeviceEvent *ev) +{ + int i; + + for (i = 0; i < frame->num_active; i++) { + const struct utouch_contact *t = frame->active[i]; + + fprintf(stderr, "touch %d\n", i); + fprintf(stderr, " slot: %d\n", t->slot); + fprintf(stderr, " id: %d\n", t->id); + fprintf(stderr, " tool_type: %d\n", t->tool_type); + fprintf(stderr, " x: %f\n", t->x); + fprintf(stderr, " y: %f\n", t->y); + fprintf(stderr, " touch_major: %f\n", t->touch_major); + fprintf(stderr, " touch_minor: %f\n", t->touch_minor); + fprintf(stderr, " width_major: %f\n", t->width_major); + fprintf(stderr, " width_minor: %f\n", t->width_minor); + fprintf(stderr, " angle: %f\n", t->orientation); + fprintf(stderr, " pressure: %f\n", t->pressure); + fprintf(stderr, " distance: %f\n", t->distance); + } + + fprintf(stderr, "sync %d %ld %d %d %d %f %f\n", + frame->num_active, + frame->time, + frame->sequence_id, + frame->revision, + frame->slot_revision, + ev->root_x, + ev->root_y); +} + +static void handle_event(struct frame_test *test, XEvent *ev) +{ + XGenericEventCookie *gev = &ev->xcookie; + const struct utouch_frame *frame; + XConfigureEvent *cev; + XIDeviceEvent *dev; + + switch(ev->type) { + case ConfigureNotify: + cev = (XConfigureEvent *)ev; + if (cev->window == XDefaultRootWindow(cev->display)) + utouch_frame_configure_xi2(test->fh, cev); + break; + case GenericEvent: + if (!XGetEventData(test->display, gev)) + break; + dev = gev->data; + /* null for some requests, quite odd */ + dev->display = ev->xany.display; + if (gev->type == GenericEvent && gev->extension == opcode) { + frame = utouch_frame_pump_xi2(test->fh, dev); + if (frame) + report_frame(frame, dev); + } + XFreeEventData(test->display, gev); + break; + } +} + +static void loop_device(struct frame_test *test) +{ + XIEventMask mask; + + mask.deviceid = XIAllDevices; + mask.mask_len = XIMaskLen(XI_TouchMotion); + mask.mask = calloc(mask.mask_len, sizeof(char)); + + XISetMask(mask.mask, XI_TouchBegin); + XISetMask(mask.mask, XI_TouchMotion); + XISetMask(mask.mask, XI_TouchEnd); + XISetMask(mask.mask, XI_PropertyEvent); + XISelectEvents(test->display, test->win, &mask, 1); + + XSelectInput(test->display, test->root, StructureNotifyMask); + + while (1) { + XEvent ev; + XNextEvent(test->display, &ev); + handle_event(test, &ev); + } +} + +int main(int argc, char *argv[]) +{ + struct frame_test test; + int id; + + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return -1; + } + id = atoi(argv[1]); + + if (init_xi2(&test, id)) { + fprintf(stderr, "error: could not describe device\n"); + return -1; + } + if (!utouch_frame_is_supported_xi2(test.display, test.dev)) { + fprintf(stderr, "error: unsupported device\n"); + return -1; + } + if (init_frame(&test)) { + fprintf(stderr, "error: could not init frame\n"); + return -1; + } + + report_device_caps(&test); + + loop_device(&test); + + destroy_all(&test); + return 0; +} diff --git a/tools/utouch-frame-test-xi2.txt b/tools/utouch-frame-test-xi2.txt new file mode 100644 index 0000000..9f79167 --- /dev/null +++ b/tools/utouch-frame-test-xi2.txt @@ -0,0 +1,22 @@ +UTOUCH-FRAME-TEST-XI2(1) +========================== + +NAME +---- + + utouch-frame-test-xi2 - report frame events from an xinput device + +SYNOPSIS +-------- + + utouch-frame-test-xi2 xinput-device-number + +DESCRIPTION +----------- + +A test tool used to analyse the capabilities and act as a test driver for the +uTouch touch frame library. + +AUTHOR +------ +utouch-frame-test-xi2 was written by Henrik Rydberg -- cgit v1.2.3