From 758bc57ba5a89f765d83f0b169aa09e79a89bf89 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 14 Dec 2011 14:48:56 +1000 Subject: dix: add helper functions to create DDX touch recs DDX touch points are the ones that keep records of the driver-submitted touchpoints. They're unaffected by the grab state and terminate on a TouchEnd submitted by the driver. The client ID assigned is server-global. Since drivers usually submit in the SIGIO handler, we cannot allocate in the these functions. Co-authored-by: Daniel Stone Signed-off-by: Peter Hutterer Reviewed-by: Chase Douglas --- dix/touch.c | 116 +++++++++++++++++++++++++++++++++++++++++++ include/input.h | 5 ++ test/Makefile.am | 3 +- test/touch.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 test/touch.c diff --git a/dix/touch.c b/dix/touch.c index 9fa2f3c8d..f9d16172b 100644 --- a/dix/touch.c +++ b/dix/touch.c @@ -31,6 +31,122 @@ #include "inputstr.h" #include "scrnintstr.h" +#include "eventstr.h" +#include "exevents.h" + +/** + * Some documentation about touch points: + * The driver submits touch events with it's own (unique) touch point ID. + * The driver may re-use those IDs, the DDX doesn't care. It just passes on + * the data to the DIX. In the server, the driver's ID is referred to as the + * DDX id anyway. + * + * On a TouchBegin, we create a DDXTouchPointInfo that contains the DDX id + * and the client ID that this touchpoint will have. The client ID is the + * one visible on the protocol. + * + * TouchUpdate and TouchEnd will only be processed if there is an active + * touchpoint with the same DDX id. + * + * The DDXTouchPointInfo struct is stored dev->last.touches. When the event + * being processed, it becomes a TouchPointInfo in dev->touch-touches which + * contains amongst other things the sprite trace and delivery information. + */ + +/** + * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the + * associated DDXTouchPointInfoRec. + * + * @param dev The device to create the touch point for + * @param ddx_id Touch id assigned by the driver/ddx + * @param create Create the touchpoint if it cannot be found + */ +DDXTouchPointInfoPtr +TouchFindByDDXID(DeviceIntPtr dev, uint32_t ddx_id, Bool create) +{ + DDXTouchPointInfoPtr ti; + int i; + + if (!dev->touch) + return NULL; + + for (i = 0; i < dev->last.num_touches; i++) + { + ti = &dev->last.touches[i]; + if (ti->active && ti->ddx_id == ddx_id) + return ti; + } + + return create ? TouchBeginDDXTouch(dev, ddx_id) : NULL; +} + +/** + * Given a unique DDX ID for a touchpoint, create a touchpoint record and + * return it. + * + * If no other touch points are active, mark new touchpoint for pointer + * emulation. + * + * Returns NULL on failure (i.e. if another touch with that ID is already active, + * allocation failure). + */ +DDXTouchPointInfoPtr +TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id) +{ + static int next_client_id = 1; + int i; + TouchClassPtr t = dev->touch; + DDXTouchPointInfoPtr ti = NULL; + Bool emulate_pointer = (t->mode == XIDirectTouch); + + if (!t) + return NULL; + + /* Look for another active touchpoint with the same DDX ID. DDX + * touchpoints must be unique. */ + if (TouchFindByDDXID(dev, ddx_id, FALSE)) + return NULL; + + for (i = 0; i < dev->last.num_touches; i++) + { + /* Only emulate pointer events on the first touch */ + if (dev->last.touches[i].active) + emulate_pointer = FALSE; + else if (!ti) /* ti is now first non-active touch rec */ + ti = &dev->last.touches[i]; + + if (!emulate_pointer && ti) + break; + } + + if (ti) + { + int client_id; + ti->active = TRUE; + ti->ddx_id = ddx_id; + client_id = next_client_id; + next_client_id++; + if (next_client_id == 0) + next_client_id = 1; + ti->client_id = client_id; + ti->emulate_pointer = emulate_pointer; + return ti; + } + + /* If we get here, then we've run out of touches, drop the event */ + return NULL; +} + +void +TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti) +{ + TouchClassPtr t = dev->touch; + + if (!t) + return; + + ti->active = FALSE; +} void TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch) diff --git a/include/input.h b/include/input.h index 0d31edf75..e79a3ee84 100644 --- a/include/input.h +++ b/include/input.h @@ -587,6 +587,11 @@ enum TouchListenerType { }; extern void TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch); +extern DDXTouchPointInfoPtr TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id); +extern void TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti); +extern DDXTouchPointInfoPtr TouchFindByDDXID(DeviceIntPtr dev, + uint32_t ddx_id, + Bool create); extern Bool TouchInitTouchPoint(TouchClassPtr touch, ValuatorClassPtr v, int index); extern void TouchFreeTouchPoint(DeviceIntPtr dev, int index); diff --git a/test/Makefile.am b/test/Makefile.am index 48393d39a..ba8932c5d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ if ENABLE_UNIT_TESTS SUBDIRS= . -noinst_PROGRAMS = list string +noinst_PROGRAMS = list string touch if XORG # Tests that require at least some DDX functions in order to fully link # For now, requires xf86 ddx, could be adjusted to use another @@ -35,6 +35,7 @@ list_LDADD=$(TEST_LDADD) misc_LDADD=$(TEST_LDADD) fixes_LDADD=$(TEST_LDADD) xfree86_LDADD=$(TEST_LDADD) +touch_LDADD=$(TEST_LDADD) libxservertest_la_LIBADD = $(XSERVER_LIBS) if XORG diff --git a/test/touch.c b/test/touch.c new file mode 100644 index 000000000..5b8e567cc --- /dev/null +++ b/test/touch.c @@ -0,0 +1,149 @@ +/** + * Copyright © 2011 Red Hat, Inc. + * + * 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. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include "inputstr.h" +#include "assert.h" + +static void touch_find_ddxid(void) +{ + DeviceIntRec dev; + DDXTouchPointInfoPtr ti; + ValuatorClassRec val; + TouchClassRec touch; + int size = 5; + int i; + + memset(&dev, 0, sizeof(dev)); + dev.id = 2; + dev.valuator = &val; + val.numAxes = 5; + dev.touch = &touch; + dev.last.num_touches = size; + dev.last.touches = calloc(dev.last.num_touches, sizeof(*dev.last.touches)); + inputInfo.devices = &dev; + assert(dev.last.touches); + + + dev.last.touches[0].active = TRUE; + dev.last.touches[0].ddx_id = 10; + dev.last.touches[0].client_id = 20; + + + /* existing */ + ti = TouchFindByDDXID(&dev, 10, FALSE); + assert(ti == &dev.last.touches[0]); + + /* non-existing */ + ti = TouchFindByDDXID(&dev, 20, FALSE); + assert(ti == NULL); + + /* Non-active */ + dev.last.touches[0].active = FALSE; + ti = TouchFindByDDXID(&dev, 10, FALSE); + assert(ti == NULL); + + /* create on number 2*/ + dev.last.touches[0].active = TRUE; + + ti = TouchFindByDDXID(&dev, 20, TRUE); + assert(ti == &dev.last.touches[1]); + assert(ti->active); + assert(ti->ddx_id == 20); + + /* set all to active */ + for (i = 0; i < size; i++) + dev.last.touches[i].active = TRUE; + + /* Try to create more, fail */ + ti = TouchFindByDDXID(&dev, 30, TRUE); + assert(ti == NULL); + ti = TouchFindByDDXID(&dev, 30, TRUE); + assert(ti == NULL); + /* make sure we haven't resized, we're in the signal handler */ + assert(dev.last.num_touches == size); + + /* stop one touchpoint, try to create, succeed */ + dev.last.touches[2].active = FALSE; + ti = TouchFindByDDXID(&dev, 30, TRUE); + assert(ti == &dev.last.touches[2]); + /* but still grow anyway */ + ProcessWorkQueue(); + ti = TouchFindByDDXID(&dev, 40, TRUE); + assert(ti == &dev.last.touches[size]); +} + +static void touch_begin_ddxtouch(void) +{ + DeviceIntRec dev; + DDXTouchPointInfoPtr ti; + ValuatorClassRec val; + TouchClassRec touch; + int ddx_id = 123; + unsigned int last_client_id = 0; + int size = 5; + + memset(&dev, 0, sizeof(dev)); + dev.id = 2; + dev.valuator = &val; + val.numAxes = 5; + touch.mode = XIDirectTouch; + dev.touch = &touch; + dev.last.num_touches = size; + dev.last.touches = calloc(dev.last.num_touches, sizeof(*dev.last.touches)); + inputInfo.devices = &dev; + assert(dev.last.touches); + + ti = TouchBeginDDXTouch(&dev, ddx_id); + assert(ti); + assert(ti->ddx_id == ddx_id); + /* client_id == ddx_id can happen in real life, but not in this test */ + assert(ti->client_id != ddx_id); + assert(ti->active); + assert(ti->client_id > last_client_id); + assert(ti->emulate_pointer); + last_client_id = ti->client_id; + + ddx_id += 10; + ti = TouchBeginDDXTouch(&dev, ddx_id); + assert(ti); + assert(ti->ddx_id == ddx_id); + /* client_id == ddx_id can happen in real life, but not in this test */ + assert(ti->client_id != ddx_id); + assert(ti->active); + assert(ti->client_id > last_client_id); + assert(!ti->emulate_pointer); + last_client_id = ti->client_id; +} + +int main(int argc, char** argv) +{ + touch_find_ddxid(); + touch_begin_ddxtouch(); + + return 0; +} -- cgit v1.2.3