summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2012-06-08 11:43:36 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2012-06-08 11:43:36 +1000
commit092d98c17999fbf696e4db8f1d85e79052c83be5 (patch)
tree76997ea488290ff707ec7fb2f0315455272b47e8
parent3476eb38063473a7a5fcd78e2095e284118de839 (diff)
parent90dcf889fcf82176c4a3aefa595efb53fc04e4d5 (diff)
Merge branch 'integration-tests' of git://people.freedesktop.org/~cndougla/xserver into integration-testing
-rw-r--r--configure.ac35
-rw-r--r--dix/devices.c1
-rw-r--r--dix/touch.c28
-rw-r--r--include/input.h1
-rw-r--r--m4/xorg-gtest.m4110
-rw-r--r--test/Makefile.am2
-rw-r--r--test/integration/.gitignore1
-rw-r--r--test/integration/Makefile-xorg-gtest.am64
-rw-r--r--test/integration/Makefile.am28
-rw-r--r--test/integration/recordings/ntrig_dell_xt2/device.prop32
-rw-r--r--test/integration/recordings/ntrig_dell_xt2/touch_1_begin.record11
-rw-r--r--test/integration/recordings/ntrig_dell_xt2/touch_1_end.record3
-rw-r--r--test/integration/xi2.cpp356
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));