diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2012-06-08 11:43:36 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2012-06-08 11:43:36 +1000 |
commit | 092d98c17999fbf696e4db8f1d85e79052c83be5 (patch) | |
tree | 76997ea488290ff707ec7fb2f0315455272b47e8 | |
parent | 3476eb38063473a7a5fcd78e2095e284118de839 (diff) | |
parent | 90dcf889fcf82176c4a3aefa595efb53fc04e4d5 (diff) |
Merge branch 'integration-tests' of git://people.freedesktop.org/~cndougla/xserver into integration-testing
-rw-r--r-- | configure.ac | 35 | ||||
-rw-r--r-- | dix/devices.c | 1 | ||||
-rw-r--r-- | dix/touch.c | 28 | ||||
-rw-r--r-- | include/input.h | 1 | ||||
-rw-r--r-- | m4/xorg-gtest.m4 | 110 | ||||
-rw-r--r-- | test/Makefile.am | 2 | ||||
-rw-r--r-- | test/integration/.gitignore | 1 | ||||
-rw-r--r-- | test/integration/Makefile-xorg-gtest.am | 64 | ||||
-rw-r--r-- | test/integration/Makefile.am | 28 | ||||
-rw-r--r-- | test/integration/recordings/ntrig_dell_xt2/device.prop | 32 | ||||
-rw-r--r-- | test/integration/recordings/ntrig_dell_xt2/touch_1_begin.record | 11 | ||||
-rw-r--r-- | test/integration/recordings/ntrig_dell_xt2/touch_1_end.record | 3 | ||||
-rw-r--r-- | test/integration/xi2.cpp | 356 |
13 files changed, 670 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac index 9ae77fbae..57e5c3338 100644 --- a/configure.ac +++ b/configure.ac @@ -35,7 +35,7 @@ AM_MAINTAINER_MODE # Require xorg-macros minimum of 1.14 for XORG_COMPILER_BRAND in XORG_DEFAULT_OPTIONS m4_ifndef([XORG_MACROS_VERSION], [m4_fatal([must install xorg-macros 1.14 or later before running autoconf/autogen])]) -XORG_MACROS_VERSION(1.14) +XORG_MACROS_VERSION(1.17) XORG_DEFAULT_OPTIONS XORG_WITH_DOXYGEN(1.6.1) XORG_CHECK_SGML_DOCTOOLS(1.8) @@ -45,6 +45,7 @@ XORG_WITH_XMLTO(0.0.20) XORG_WITH_FOP XORG_WITH_XSLTPROC XORG_ENABLE_UNIT_TESTS +XORG_ENABLE_INTEGRATION_TESTS XORG_LD_WRAP([optional]) m4_ifndef([XORG_FONT_MACROS_VERSION], [m4_fatal([must install fontutil 1.1 or later before running autoconf/autogen])]) @@ -2143,6 +2144,37 @@ AM_CONDITIONAL(XEPHYR, [test "x$KDRIVE" = xyes && test "x$XEPHYR" = xyes]) AM_CONDITIONAL(BUILD_KDRIVEFBDEVLIB, [test "x$KDRIVE" = xyes && test "x$KDRIVEFBDEVLIB" = xyes]) AM_CONDITIONAL(XFAKESERVER, [test "x$KDRIVE" = xyes && test "x$XFAKE" = xyes]) +dnl --------------------------------------------------------------------------- +dnl Tests section. +dnl --------------------------------------------------------------------------- + +if [test "x$XORG" = xyes && test "x$enable_integration_tests" != xno]; then + AC_PROG_CXX + CHECK_XORG_GTEST + AM_CONDITIONAL(ENABLE_XORG_GTEST_TESTS, [test "x$have_xorg_gtest" = xyes]) + if [test "x$have_xorg_gtest" = xyes]; then + AC_MSG_NOTICE([xorg-gtest is available, tests will be built]) + elif [test "x$enable_integration_tests" = xyes]; then + AC_MSG_ERROR([xorg-gtest is not available]) + else + AC_MSG_NOTICE([xorg-gtest is not available, tests will not be built]) + fi +fi + +PKG_CHECK_MODULES(TEST_X11, x11, have_test_x11=yes, have_test_x11=no) +PKG_CHECK_MODULES(TEST_XINPUT, [xi >= 1.6], have_test_xinput=yes, have_test_xinput=no) + +if [test "x$have_test_x11" != xyes]; then + AC_MSG_NOTICE([libX11 not available, skipping input tests]) +elif [test "x$have_test_xinput" != xyes]; then + AC_MSG_NOTICE([libXi 1.6 not available, skipping input tests]) +elif [test "x$have_xorg_gtest_evemu" != xyes]; then + AC_MSG_NOTICE([uTouch-Evemu not available, skipping input tests]) +else + enable_input_tests=yes +fi +AM_CONDITIONAL(ENABLE_XORG_GTEST_INPUT_TESTS, [test "x$enable_input_tests" = xyes]) + dnl and the rest of these are generic, so they're in config.h dnl dnl though, thanks to the passing of some significant amount of time, the @@ -2275,6 +2307,7 @@ hw/kdrive/linux/Makefile hw/kdrive/src/Makefile test/Makefile test/xi2/Makefile +test/integration/Makefile xserver.ent xorg-server.pc ]) diff --git a/dix/devices.c b/dix/devices.c index 0c62a012d..dc5f51cf2 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -437,6 +437,7 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent) if (*prev != dev) return FALSE; + TouchEndPhysicallyActiveTouches(dev); ReleaseButtonsAndKeys(dev); SyncRemoveDeviceIdleTime(dev->idle_counter); dev->idle_counter = NULL; diff --git a/dix/touch.c b/dix/touch.c index 401cb981a..c9f72a923 100644 --- a/dix/touch.c +++ b/dix/touch.c @@ -1028,3 +1028,31 @@ TouchAcceptReject(ClientPtr client, DeviceIntPtr dev, int mode, return TouchListenerAcceptReject(dev, ti, i, mode); } + +/** + * End physically active touches for a device. + */ +void +TouchEndPhysicallyActiveTouches(DeviceIntPtr dev) +{ + InternalEvent *eventlist = InitEventList(GetMaximumEventsNum()); + int i; + + OsBlockSignals(); + mieqProcessInputEvents(); + for (i = 0; i < dev->last.num_touches; i++) { + DDXTouchPointInfoPtr ddxti = dev->last.touches + i; + + if (ddxti->active) { + int j; + int nevents = GetTouchEvents(eventlist, dev, ddxti->ddx_id, + XI_TouchEnd, 0, NULL); + + for (j = 0; j < nevents; j++) + mieqProcessDeviceEvent(dev, eventlist + j, NULL); + } + } + OsReleaseSignals(); + + FreeEventList(eventlist, GetMaximumEventsNum()); +} diff --git a/include/input.h b/include/input.h index bcf98a63e..c702929f3 100644 --- a/include/input.h +++ b/include/input.h @@ -579,6 +579,7 @@ extern int TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener, int mode); extern int TouchAcceptReject(ClientPtr client, DeviceIntPtr dev, int mode, uint32_t touchid, Window grab_window, XID *error); +extern void TouchEndPhysicallyActiveTouches(DeviceIntPtr dev); /* misc event helpers */ extern Mask GetEventMask(DeviceIntPtr dev, xEvent *ev, InputClientsPtr clients); diff --git a/m4/xorg-gtest.m4 b/m4/xorg-gtest.m4 new file mode 100644 index 000000000..062842ca8 --- /dev/null +++ b/m4/xorg-gtest.m4 @@ -0,0 +1,110 @@ +# serial 2 + +# Copyright (C) 2012 Canonical, Ltd. +# +# 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. + +# Checks whether the gtest source is available on the system. Allows for +# adjusting the include and source path. Sets have_gtest=yes if the source is +# present. Sets GTEST_CPPFLAGS and GTEST_SOURCE to the preprocessor flags and +# source location respectively. +AC_DEFUN([_CHECK_GTEST], +[ + AC_ARG_WITH([gtest-source-path], + [AS_HELP_STRING([--with-gtest-source-path], + [location of the Google test sources, defaults to /usr/src/gtest])], + [GTEST_SOURCE="$withval"; GTEST_CPPFLAGS="-I$withval/include"], + [GTEST_SOURCE="/usr/src/gtest"]) + + AC_ARG_WITH([gtest-include-path], + [AS_HELP_STRING([--with-gtest-include-path], + [location of the Google test headers])], + [GTEST_CPPFLAGS="-I$withval"]) + + GTEST_CPPFLAGS="$GTEST_CPPFLAGS -I$GTEST_SOURCE" + + AC_CHECK_FILES([$GTEST_SOURCE/src/gtest-all.cc] + [$GTEST_SOURCE/src/gtest_main.cc], + [have_gtest=yes], + [have_gtest=no]) + + AS_IF([test "x$have_gtest_source" = xyes], + [AC_SUBST(GTEST_CPPFLAGS)] + [AC_SUBST(GTEST_SOURCE)]) +]) # _CHECK_GTEST + +# CHECK_XORG_GTEST([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Checks whether the xorg-gtest source is available on the system. Allows for +# adjusting the include and source path. Sets have_xorg_gtest=yes if the source +# is present. Sets XORG_GTEST_CPPFLAGS and XORG_GTEST_SOURCE to the preprocessor +# flags and source location respectively. Sets XORG_GTEST_LIBS to all the +# libraries needed to link against a built xorg-gtest library. +# +# Both default actions are no-ops. +AC_DEFUN([CHECK_XORG_GTEST], +[ + AC_REQUIRE([_CHECK_GTEST]) + + PKG_CHECK_EXISTS([xorg-gtest], + [have_xorg_gtest=yes], + [have_xorg_gtest=no]) + + XORG_GTEST_SOURCE=`$PKG_CONFIG --variable=sourcedir --print-errors xorg-gtest` + XORG_GTEST_CPPFLAGS=`$PKG_CONFIG --variable=CPPflags --print-errors xorg-gtest` + XORG_GTEST_CPPFLAGS="$GTEST_CPPFLAGS $XORG_GTEST_CPPFLAGS" + XORG_GTEST_CPPFLAGS="$XORG_GTEST_CPPFLAGS -I$XORG_GTEST_SOURCE" + + PKG_CHECK_MODULES(X11, [x11], [have_x11=yes], [have_x11=no]) + + # Check if we should include support for utouch-evemu + AC_ARG_WITH([evemu], + [AS_HELP_STRING([--with-evemu], + [support Linux input device recording playback + (default: enabled if available)])], + [], + [with_evemu=check]) + + AS_IF([test "x$with_evemu" = xyes], + [PKG_CHECK_MODULES(EVEMU, [utouch-evemu], [have_xorg_gtest_evemu=yes])], + [test "x$with_evemu" = xcheck], + [PKG_CHECK_MODULES(EVEMU, + [utouch-evemu], + [have_xorg_gtest_evemu=yes], + [have_xorg_gtest_evemu=no])]) + AS_IF([test "x$have_xorg_gtest_evemu" = xyes], + [XORG_GTEST_CPPFLAGS="$XORG_GTEST_CPPFLAGS -DHAVE_EVEMU"]) + + AS_IF([test "x$have_gtest" != xyes -o "x$have_x11" != xyes], + [have_xorg_gtest=no]) + + AS_IF([test "x$have_xorg_gtest" = xyes], + [AC_SUBST(XORG_GTEST_SOURCE)] + [AC_SUBST(XORG_GTEST_CPPFLAGS)] + + # Get BASE_CXXFLAGS and STRICT_CXXFLAGS + [XORG_MACROS_VERSION(1.17)] + [AC_LANG_PUSH([C++])] + [XORG_STRICT_OPTION] + [AC_LANG_POP] + [$1], + [$2]) + +]) # CHECK_XORG_GTEST diff --git a/test/Makefile.am b/test/Makefile.am index b2a53aa64..f58faed24 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -4,7 +4,7 @@ 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 -SUBDIRS += xi2 +SUBDIRS += xi2 integration noinst_PROGRAMS += xkb input xtest misc fixes xfree86 hashtabletest endif check_LTLIBRARIES = libxservertest.la diff --git a/test/integration/.gitignore b/test/integration/.gitignore new file mode 100644 index 000000000..ab86ebf47 --- /dev/null +++ b/test/integration/.gitignore @@ -0,0 +1 @@ +gtest-tests diff --git a/test/integration/Makefile-xorg-gtest.am b/test/integration/Makefile-xorg-gtest.am new file mode 100644 index 000000000..20983eba5 --- /dev/null +++ b/test/integration/Makefile-xorg-gtest.am @@ -0,0 +1,64 @@ +# Copyright (C) 2012 Canonical, Ltd. +# +# 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. +# + +XORG_GTEST_BUILD_LIBS = \ + libgtest.a \ + libgtest_main.a \ + libxorg-gtest.a \ + libxorg-gtest_main.a + +# Here and below we compile without warnings (-w) because the projects using +# xorg-gtest will not want to see warnings or fail to build due to warnings in +# gtest or xorg-gtest. +nodist_libgtest_a_SOURCES = $(GTEST_SOURCE)/src/gtest-all.cc +libgtest_a_CPPFLAGS = $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) -w +libgtest_a_CXXFLAGS = $(GTEST_CXXFLAGS) $(AM_CXXFLAGS) + +nodist_libgtest_main_a_SOURCES = $(GTEST_SOURCE)/src/gtest_main.cc +libgtest_main_a_CPPFLAGS = $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) -w +libgtest_main_a_CXXFLAGS = $(GTEST_CXXFLAGS) $(AM_CXXFLAGS) + +nodist_libxorg_gtest_a_SOURCES = $(XORG_GTEST_SOURCE)/src/xorg-gtest-all.cpp +libxorg_gtest_a_CPPFLAGS = \ + $(XORG_GTEST_CPPFLAGS) \ + $(GTEST_CPPFLAGS) \ + $(AM_CPPFLAGS) \ + -w +libxorg_gtest_a_CXXFLAGS = \ + $(XORG_GTEST_CXXFLAGS) \ + $(GTEST_CXXFLAGS) \ + $(AM_CPPFLAGS) + +nodist_libxorg_gtest_main_a_SOURCES = \ + $(XORG_GTEST_SOURCE)/src/xorg-gtest_main.cpp +libxorg_gtest_main_a_CPPFLAGS = \ + $(XORG_GTEST_CPPFLAGS) \ + $(GTEST_CPPFLAGS) \ + $(AM_CPPFLAGS) \ + -w +libxorg_gtest_main_a_CXXFLAGS = \ + $(XORG_GTEST_CXXFLAGS) \ + $(GTEST_CXXFLAGS) \ + $(AM_CXXFLAGS) + +XORG_GTEST_LIBS = libxorg-gtest.a libgtest.a -lpthread $(X11_LIBS) +XORG_GTEST_MAIN_LIBS = libxorg-gtest_main.a diff --git a/test/integration/Makefile.am b/test/integration/Makefile.am new file mode 100644 index 000000000..ff9789ec9 --- /dev/null +++ b/test/integration/Makefile.am @@ -0,0 +1,28 @@ +TESTS = + +if ENABLE_XORG_GTEST_TESTS +include $(top_srcdir)/test/integration/Makefile-xorg-gtest.am +noinst_LIBRARIES = $(XORG_GTEST_BUILD_LIBS) + +if ENABLE_XORG_GTEST_INPUT_TESTS +TESTS += gtest-tests +endif +endif + +noinst_PROGRAMS = $(TESTS) + +AM_CPPFLAGS = \ + -DDEFAULT_XORG_SERVER=\"$(abs_top_builddir)/hw/xfree86/Xorg\" \ + -DTEST_ROOT_DIR=\"$(abs_top_srcdir)/test/integration/\" \ + $(GTEST_CPPFLAGS) \ + $(XORG_GTEST_CPPFLAGS) + +AM_CXXFLAGS = $(GTEST_CXXFLAGS) $(XORG_GTEST_CXXFLAGS) + +gtest_tests_SOURCES = xi2.cpp +gtest_tests_LDADD = \ + $(TEST_XINPUT_LIBS) \ + $(TEST_X11_LIBS) \ + $(XORG_GTEST_LIBS) \ + $(EVEMU_LIBS) \ + $(XORG_GTEST_MAIN_LIBS) diff --git a/test/integration/recordings/ntrig_dell_xt2/device.prop b/test/integration/recordings/ntrig_dell_xt2/device.prop new file mode 100644 index 000000000..2738c04b6 --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/device.prop @@ -0,0 +1,32 @@ +N: N-Trig MultiTouch (Virtual Test Device) +I: 0003 1b96 0001 0110 +P: 00 00 00 00 00 00 00 00 +B: 00 0b 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 04 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 01 00 00 00 00 00 00 00 00 +B: 02 00 00 00 00 00 00 00 00 +B: 03 03 00 00 00 00 01 73 00 +B: 04 00 00 00 00 00 00 00 00 +B: 05 00 00 00 00 00 00 00 00 +B: 11 00 00 00 00 00 00 00 00 +B: 12 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00 +B: 15 00 00 00 00 00 00 00 00 +A: 00 0 9600 75 0 +A: 01 0 7200 78 0 +A: 28 0 255 0 0 +A: 30 0 9600 200 0 +A: 31 0 7200 150 0 +A: 34 0 1 0 0 +A: 35 0 9600 75 0 +A: 36 0 7200 78 0 diff --git a/test/integration/recordings/ntrig_dell_xt2/touch_1_begin.record b/test/integration/recordings/ntrig_dell_xt2/touch_1_begin.record new file mode 100644 index 000000000..28a849b89 --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/touch_1_begin.record @@ -0,0 +1,11 @@ +E: 1327542640.244087 0003 0000 2745 +E: 1327542640.244089 0003 0001 1639 +E: 1327542640.244090 0003 0035 2745 +E: 1327542640.244091 0003 0036 1639 +E: 1327542640.244092 0003 0034 0 +E: 1327542640.244093 0003 0030 468 +E: 1327542640.244094 0003 0031 306 +E: 1327542640.244095 0000 0002 0 +E: 1327542640.244251 0001 014d 1 +E: 1327542640.244251 0001 014a 1 +E: 1327542640.244253 0000 0000 0 diff --git a/test/integration/recordings/ntrig_dell_xt2/touch_1_end.record b/test/integration/recordings/ntrig_dell_xt2/touch_1_end.record new file mode 100644 index 000000000..cd6a9d94b --- /dev/null +++ b/test/integration/recordings/ntrig_dell_xt2/touch_1_end.record @@ -0,0 +1,3 @@ +E: 1327542642.244253 0001 014d 0 +E: 1327542642.244253 0001 014a 0 +E: 1327542642.244253 0000 0000 0 diff --git a/test/integration/xi2.cpp b/test/integration/xi2.cpp new file mode 100644 index 000000000..1cbbe0592 --- /dev/null +++ b/test/integration/xi2.cpp @@ -0,0 +1,356 @@ +#include <stdexcept> + +#include <xorg/gtest/xorg-gtest.h> + +#include <X11/extensions/XInput.h> +#include <X11/extensions/XInput2.h> + +namespace { + +/** + * Wait for an event on the X connection. + * + * @param [in] display The X display connection + * @param [in] timeout The timeout in milliseconds + * + * @return Whether an event is available + */ +bool WaitForEvent(::Display *display, time_t timeout = 1000) +{ + fd_set fds; + FD_ZERO(&fds); + + int display_fd = ConnectionNumber(display); + + XSync(display, False); + + if (XPending(display)) + return true; + else { + FD_SET(display_fd, &fds); + + struct timeval timeval = { + static_cast<time_t>(timeout / 1000), + static_cast<time_t>(timeout % 1000), + }; + + int ret; + if (timeout) + ret = select(display_fd + 1, &fds, NULL, NULL, &timeval); + else + ret = select(display_fd + 1, &fds, NULL, NULL, NULL); + + if (ret < 0) + throw std::runtime_error("Failed to select on X fd"); + + if (ret == 0) + return false; + + return XPending(display); + } +} + +/** + * Wait for an event of a specific type on the X connection. + * + * All events preceding the matching event are discarded. If no event was found + * before the timeout expires, all events in the queue will have been discarded. + * + * @param [in] display The X display connection + * @param [in] type The X core protocol event type + * @param [in] extension The X extension opcode of a generic event, or -1 for + * any generic event + * @param [in] evtype The X extension event type of a generic event, or -1 + * for any event of the given extension + * @param [in] timeout The timeout in milliseconds + * + * @return Whether an event is available + */ +bool WaitForEventOfType(::Display *display, int type, int extension, int evtype, + time_t timeout = 1000) +{ + while (WaitForEvent(display)) { + XEvent event; + if (!XPeekEvent(display, &event)) + throw std::runtime_error("Failed to peek X event"); + + if (event.type != type) { + if (XNextEvent(display, &event) != Success) + throw std::runtime_error("Failed to remove X event"); + continue; + } + + if (event.type != GenericEvent || extension == -1) + return true; + + XGenericEvent *generic_event = reinterpret_cast<XGenericEvent*>(&event); + + if (generic_event->extension != extension) { + if (XNextEvent(display, &event) != Success) + throw std::runtime_error("Failed to remove X event"); + continue; + } + + if (evtype == -1 || generic_event->evtype == evtype) + return true; + + if (XNextEvent(display, &event) != Success) + throw std::runtime_error("Failed to remove X event"); + } +} + +/** + * Wait for a specific device to be added to the server. + * + * @param [in] display The X display connection + * @param [in] name The name of the device to wait for + * @param [in] timeout The timeout in milliseconds + * + * @return Whether the device was added + */ +bool WaitForDevice(::Display *display, const std::string &name, + time_t timeout = 1000) +{ + int opcode; + int event_start; + int error_start; + + if (!XQueryExtension(display, "XInputExtension", &opcode, &event_start, + &error_start)) + throw std::runtime_error("Failed to query XInput extension"); + + while (WaitForEventOfType(display, GenericEvent, opcode, + XI_HierarchyChanged)) { + XEvent event; + if (XNextEvent(display, &event) != Success) + throw std::runtime_error("Failed to get X event"); + + XGenericEventCookie *xcookie = + reinterpret_cast<XGenericEventCookie*>(&event.xcookie); + if (!XGetEventData(display, xcookie)) + throw std::runtime_error("Failed to get X event data"); + + XIHierarchyEvent *hierarchy_event = + reinterpret_cast<XIHierarchyEvent*>(xcookie->data); + + if (!(hierarchy_event->flags & XIDeviceEnabled)) { + XFreeEventData(display, xcookie); + continue; + } + + bool device_found = false; + for (int i = 0; i < hierarchy_event->num_info; i++) { + if (!(hierarchy_event->info[i].flags & XIDeviceEnabled)) + continue; + + int num_devices; + XIDeviceInfo *device_info = + XIQueryDevice(display, hierarchy_event->info[i].deviceid, + &num_devices); + if (num_devices != 1 || !device_info) + throw std::runtime_error("Failed to query device"); + + if (name.compare(device_info[0].name) == 0) { + device_found = true; + break; + } + } + + XFreeEventData(display, xcookie); + + if (device_found) + return true; + } + + return false; +} + +} + +/** + * A test fixture for testing the XInput 2.x extension. + * + * @tparam The XInput 2.x minor version + */ +class XInput2Test : public xorg::testing::Test, + public ::testing::WithParamInterface<int> { +protected: + virtual void SetUp() + { + ASSERT_NO_FATAL_FAILURE(xorg::testing::Test::SetUp()); + + int event_start; + int error_start; + + ASSERT_TRUE(XQueryExtension(Display(), "XInputExtension", &xi2_opcode_, + &event_start, &error_start)); + + int major = 2; + int minor = GetParam(); + + ASSERT_EQ(Success, XIQueryVersion(Display(), &major, &minor)); + } + + int xi2_opcode_; +}; + +/** + * XIQueryPointer for XInput 2.1 and earlier should report the first button + * pressed if a touch is physically active. For XInput 2.2 and later clients, + * the first button should not be reported. + */ +TEST_P(XInput2Test, XIQueryPointerTouchscreen) +{ + XIEventMask mask; + mask.deviceid = XIAllDevices; + mask.mask_len = XIMaskLen(XI_HierarchyChanged); + mask.mask = reinterpret_cast<unsigned char*>(calloc(mask.mask_len, 1)); + XISetMask(mask.mask, XI_HierarchyChanged); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + mask.deviceid = XIAllMasterDevices; + XIClearMask(mask.mask, XI_HierarchyChanged); + XISetMask(mask.mask, XI_ButtonPress); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + free(mask.mask); + + XFlush(Display()); + + std::auto_ptr<xorg::testing::evemu::Device> device; + try { + device = std::auto_ptr<xorg::testing::evemu::Device>( + new xorg::testing::evemu::Device( + TEST_ROOT_DIR "recordings/ntrig_dell_xt2/device.prop")); + } catch (std::runtime_error &error) { + std::cerr << "Failed to create evemu device, skipping test.\n"; + return; + } + + ASSERT_TRUE(WaitForDevice(Display(), + "N-Trig MultiTouch (Virtual Test Device)")); + + device->Play( + TEST_ROOT_DIR "recordings/ntrig_dell_xt2/touch_1_begin.record"); + + ASSERT_TRUE(WaitForEventOfType(Display(), GenericEvent, xi2_opcode_, + XI_ButtonPress)); + + XEvent event; + ASSERT_EQ(Success, XNextEvent(Display(), &event)); + + XGenericEventCookie *xcookie = &event.xcookie; + ASSERT_TRUE(XGetEventData(Display(), xcookie)); + + XIDeviceEvent *device_event = + reinterpret_cast<XIDeviceEvent*>(xcookie->data); + + Window root; + Window child; + double root_x; + double root_y; + double win_x; + double win_y; + XIButtonState buttons; + XIModifierState modifiers; + XIGroupState group; + ASSERT_TRUE(XIQueryPointer(Display(), device_event->deviceid, + DefaultRootWindow(Display()), &root, &child, + &root_x, &root_y, &win_x, &win_y, &buttons, + &modifiers, &group)); + + /* Test if button 1 is pressed */ + ASSERT_GE(buttons.mask_len, XIMaskLen(2)); + if (GetParam() < 2) + EXPECT_TRUE(XIMaskIsSet(buttons.mask, 1)); + else + EXPECT_FALSE(XIMaskIsSet(buttons.mask, 1)); + + XFreeEventData(Display(), xcookie); +} + +/** + * When a device is disabled, any physically active touches should end. + */ +TEST_P(XInput2Test, DisableDeviceEndTouches) +{ + /* This is an XInput 2.2 and later test only */ + if (GetParam() < 2) + return; + + XIEventMask mask; + mask.deviceid = XIAllDevices; + mask.mask_len = XIMaskLen(XI_TouchEnd); + mask.mask = reinterpret_cast<unsigned char*>(calloc(mask.mask_len, 1)); + XISetMask(mask.mask, XI_HierarchyChanged); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + mask.deviceid = XIAllMasterDevices; + XIClearMask(mask.mask, XI_HierarchyChanged); + XISetMask(mask.mask, XI_TouchBegin); + XISetMask(mask.mask, XI_TouchUpdate); + XISetMask(mask.mask, XI_TouchEnd); + + ASSERT_EQ(Success, + XISelectEvents(Display(), DefaultRootWindow(Display()), &mask, + 1)); + + free(mask.mask); + + XFlush(Display()); + + std::auto_ptr<xorg::testing::evemu::Device> device; + try { + device = std::auto_ptr<xorg::testing::evemu::Device>( + new xorg::testing::evemu::Device( + TEST_ROOT_DIR "recordings/ntrig_dell_xt2/device.prop")); + } catch (std::runtime_error &error) { + std::cerr << "Failed to create evemu device, skipping test.\n"; + return; + } + + ASSERT_TRUE(WaitForDevice(Display(), + "N-Trig MultiTouch (Virtual Test Device)")); + + device->Play( + TEST_ROOT_DIR "recordings/ntrig_dell_xt2/touch_1_begin.record"); + + ASSERT_TRUE(WaitForEventOfType(Display(), GenericEvent, xi2_opcode_, + XI_TouchBegin)); + + XEvent event; + ASSERT_EQ(Success, XNextEvent(Display(), &event)); + + XGenericEventCookie *xcookie = &event.xcookie; + ASSERT_TRUE(XGetEventData(Display(), xcookie)); + + XIDeviceEvent *device_event = + reinterpret_cast<XIDeviceEvent*>(xcookie->data); + + XDevice *xdevice = XOpenDevice(Display(), device_event->sourceid); + XFreeEventData(Display(), xcookie); + ASSERT_TRUE(xdevice != NULL); + + XDeviceEnableControl enable_control; + enable_control.enable = false; + XDeviceControl *control = + reinterpret_cast<XDeviceControl*>(&enable_control); + ASSERT_TRUE(XChangeDeviceControl(Display(), xdevice, DEVICE_ENABLE, + control)); + XCloseDevice(Display(), xdevice); + XFlush(Display()); + + ASSERT_TRUE(WaitForEventOfType(Display(), GenericEvent, xi2_opcode_, + XI_TouchEnd)); +} + +INSTANTIATE_TEST_CASE_P(, XInput2Test, ::testing::Range(0, 3)); |