summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/common/helpers.cpp9
-rw-r--r--tests/common/helpers.h7
-rw-r--r--tests/server/input.cpp219
3 files changed, 235 insertions, 0 deletions
diff --git a/tests/common/helpers.cpp b/tests/common/helpers.cpp
index 8f1f0fe..f07b3df 100644
--- a/tests/common/helpers.cpp
+++ b/tests/common/helpers.cpp
@@ -27,6 +27,8 @@
#endif
#include "helpers.h"
+#include <math.h>
+
#include <sstream>
#include <fstream>
@@ -289,3 +291,10 @@ bool SearchFileForString(const std::string &path, const std::string &substring)
return false;
}
+
+int double_cmp(double a, double b, int precision)
+{
+ int ai = (int)round(a * pow(10, precision));
+ int bi = (int)round(b * pow(10, precision));
+ return (ai > bi) ? 1 : (ai < bi) ? -1 : 0;
+}
diff --git a/tests/common/helpers.h b/tests/common/helpers.h
index 1cf3449..ac23ddb 100644
--- a/tests/common/helpers.h
+++ b/tests/common/helpers.h
@@ -175,5 +175,12 @@ bool NoEventPending(Display *dpy);
* false otherwise.
*/
bool SearchFileForString(const std::string &path, const std::string &substring);
+
+/**
+ * Compare two doubles for equality within the given precision.
+ *
+ * @return 1, 0, -1 if a is larger, equal, or less than b, respectively
+ */
+int double_cmp(double a, double b, int precision = 2);
#endif
diff --git a/tests/server/input.cpp b/tests/server/input.cpp
index f3c7c94..1065c9e 100644
--- a/tests/server/input.cpp
+++ b/tests/server/input.cpp
@@ -628,3 +628,222 @@ TEST_F(DeviceChangedTest, DeviceChangedEvent)
FAIL() << "Failed to find vscroll axis in DCE";
}
#endif
+
+enum MatrixType {
+ IDENTITY,
+ LEFT_HALF,
+ RIGHT_HALF,
+};
+
+class PointerAbsoluteTransformationMatrixTest : public XITServerInputTest,
+ public DeviceInterface,
+ public ::testing::WithParamInterface<std::tr1::tuple<enum ::MatrixType, int> > {
+public:
+ virtual void SetUp() {
+ SetDevice("tablets/Wacom-Intuos4-6x9.desc");
+ XITServerInputTest::SetUp();
+ }
+
+ virtual std::string EvdevOptions(enum MatrixType which) {
+ std::string matrix;
+ switch(which) {
+ case LEFT_HALF:
+ matrix = "0.5 0 0 0 1 0 0 0 1";
+ break;
+ case RIGHT_HALF:
+ matrix = "0.5 0 0.5 0 1 0 0 0 1";
+ break;
+ case IDENTITY:
+ matrix = "1 0 0 0 1 0 0 0 1";
+ break;
+ }
+
+ return "Option \"CorePointer\" \"on\"\n"
+ "Option \"GrabDevice\" \"on\"\n"
+ "Option \"TransformationMatrix\" \"" + matrix +"\"\n"
+ "Option \"Device\" \"" + dev->GetDeviceNode() + "\"";
+ }
+
+ virtual void SetUpConfigAndLog() {
+ std::tr1::tuple<enum MatrixType, int> t = GetParam();
+ enum MatrixType mtype = std::tr1::get<0>(t);
+ int nscreens = std::tr1::get<1>(t);
+
+ std::string opts = EvdevOptions(mtype);
+
+ switch(nscreens) {
+ case 1: SetUpSingleScreen(opts); break;
+ case 2: SetupDualHead(opts); break;
+ default:
+ FAIL();
+ }
+ }
+
+ virtual void SetUpSingleScreen(std::string &evdev_options) {
+ config.AddDefaultScreenWithDriver();
+ config.AddInputSection("evdev", "--device--", evdev_options);
+ config.AddInputSection("kbd", "kbd-device",
+ "Option \"CoreKeyboard\" \"on\"\n");
+ config.WriteConfig();
+ }
+
+ virtual void SetupDualHead(std::string &evdev_options) {
+ config.SetAutoAddDevices(true);
+ config.AppendRawConfig(std::string() +
+ "Section \"ServerLayout\"\n"
+ " Identifier \"X.org Configured\"\n"
+ " Screen 0 \"Screen0\"\n"
+ " Screen 1 \"Screen1\" RightOf \"Screen0\"\n"
+ " Option \"Xinerama\" \"off\"\n"
+ " Option \"AutoAddDevices\" \"off\"\n"
+ "EndSection\n"
+ "\n"
+ "Section \"Device\"\n"
+ " Identifier \"Card0\"\n"
+ " Driver \"dummy\"\n"
+ "EndSection\n"
+ "\n"
+ "Section \"Device\"\n"
+ " Identifier \"Card1\"\n"
+ " Driver \"dummy\"\n"
+ "EndSection\n"
+ "\n"
+ "Section \"Screen\"\n"
+ " Identifier \"Screen0\"\n"
+ " Device \"Card0\"\n"
+ "EndSection\n"
+ "\n"
+ "Section \"Screen\"\n"
+ " Identifier \"Screen1\"\n"
+ " Device \"Card1\"\n"
+ "EndSection");
+ config.AddInputSection("evdev", "--device--", evdev_options, false);
+ config.AddInputSection("kbd", "kbd-device",
+ "Option \"CoreKeyboard\" \"on\"\n",
+ false);
+ config.WriteConfig();
+ }
+};
+
+
+TEST_P(PointerAbsoluteTransformationMatrixTest, XI2ValuatorData)
+{
+ std::tr1::tuple<enum MatrixType, int> t = GetParam();
+ enum MatrixType mtype = std::tr1::get<0>(t);
+ int nscreens = std::tr1::get<1>(t);
+
+ std::string matrix;
+ switch(mtype) {
+ case IDENTITY: matrix = "identity marix"; break;
+ case LEFT_HALF: matrix = "left half"; break;
+ case RIGHT_HALF: matrix = "right half"; break;
+ }
+ std::stringstream screens;
+ screens << nscreens;
+
+ XORG_TESTCASE("Set transformation matrix on absolute tablet\n"
+ "XI2 valuator data must match input data\n"
+ "https://bugs.freedesktop.org/show_bug.cgi?id=63098\n");
+ SCOPED_TRACE("Matrix: " + matrix);
+ SCOPED_TRACE("nscreens: " + screens.str());
+
+
+ ::Display *dpy = Display();
+
+ /* Can't register for XIAllDevices, on a dual-head crossing into the new
+ stream will generate an XTest Pointer event */
+ int deviceid;
+ ASSERT_TRUE(FindInputDeviceByName(dpy, "--device--", &deviceid));
+
+ XIEventMask mask;
+ mask.deviceid = deviceid;
+ mask.mask_len = XIMaskLen(XI_Motion);
+ mask.mask = new unsigned char[mask.mask_len]();
+ XISetMask(mask.mask, XI_Motion);
+ XISetMask(mask.mask, XI_ButtonPress);
+ XISetMask(mask.mask, XI_ButtonRelease);
+
+ XISelectEvents(dpy, RootWindow(dpy, 0), &mask, 1);
+ if (nscreens > 1)
+ XISelectEvents(dpy, RootWindow(dpy, 1), &mask, 1);
+ XSync(dpy, False);
+ delete[] mask.mask;
+
+ int w = DisplayWidth(dpy, 0);
+
+ int minx, maxx;
+ dev->GetAbsData(ABS_X, &minx, &maxx);
+
+ dev->PlayOne(EV_ABS, ABS_X, 1000);
+ dev->PlayOne(EV_ABS, ABS_Y, 1000);
+ dev->PlayOne(EV_ABS, ABS_DISTANCE, 0);
+ dev->PlayOne(EV_KEY, BTN_TOOL_PEN, 1, true);
+
+ /* drop first event */
+ ASSERT_EVENT(XIDeviceEvent, m, dpy, GenericEvent, xi2_opcode, XI_Motion);
+
+ dev->PlayOne(EV_ABS, ABS_X, minx, true);
+
+ double expected_minx = minx,
+ expected_maxx = maxx;
+ switch (mtype) {
+ /* no matrix, we expect the device range */
+ case IDENTITY:
+ break;
+ /* left half we expect the device to go min-max/2 on a single-screen
+ setup. dual-screen is per-device range*/
+ case LEFT_HALF:
+ if (nscreens == 1)
+ expected_maxx = maxx/2;
+ break;
+ /* right half we expect the device to go from something smaller than
+ max/2 to max. the device has ~46 units per pixel, if we map to
+ 512 on the screen that's not maxx/2, it's something less than
+ that (see the scaling formula) */
+ case RIGHT_HALF:
+ if (nscreens == 1)
+ expected_minx = w/2.0 * maxx/(w-1);
+ else {
+ /* problem: scale to desktop == 1024
+ re-scale to device == doesn't give us minx because the
+ half-point of the display may not be the exact half-point of
+ the device if the device res is > screen res.
+
+ device range: [.............|.............]
+ ^ device range/2
+ pixels: ...[ ][ ][ ]|[ ][ ][ ]...
+ ^ 0/0 on screen2
+ so by scaling from desktop/2 (0/0 on S2) we re-scale into a
+ device range that isn't exactly 0 because that device
+ coordinate would resolve to pixel desktop/2-1.
+
+ so we expect minx to be _less_ than what would be one-pixel
+ on the device.
+ */
+ expected_minx = maxx/(w-1);
+ }
+ break;
+ }
+
+ ASSERT_EVENT(XIDeviceEvent, m1, dpy, GenericEvent, xi2_opcode, XI_Motion);
+ ASSERT_EQ(m1->deviceid, deviceid);
+ ASSERT_TRUE(XIMaskIsSet(m1->valuators.mask, 0));
+ if (mtype == RIGHT_HALF && nscreens > 1)
+ ASSERT_TRUE(double_cmp(m1->valuators.values[0], expected_minx) <= 0);
+ else
+ ASSERT_TRUE(double_cmp(m1->valuators.values[0], expected_minx) == 0);
+
+ /* y will be scaled in some weird way (depending on proportion of
+ screen:tablet), so we ignore it here */
+
+ dev->PlayOne(EV_ABS, ABS_X, maxx, true);
+
+ ASSERT_EVENT(XIDeviceEvent, m2, dpy, GenericEvent, xi2_opcode, XI_Motion);
+ ASSERT_EQ(m1->deviceid, deviceid);
+ ASSERT_TRUE(XIMaskIsSet(m2->valuators.mask, 0));
+ ASSERT_TRUE(double_cmp(m2->valuators.values[0], expected_maxx) == 0);
+}
+
+INSTANTIATE_TEST_CASE_P(, PointerAbsoluteTransformationMatrixTest,
+ ::testing::Combine(::testing::Values(IDENTITY, LEFT_HALF, RIGHT_HALF),
+ ::testing::Values(1, 2)));