summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2012-10-23 11:56:42 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2012-10-23 11:56:42 +1000
commite2c8baf6e9124dab6d15258f1dd6d69b627ddde4 (patch)
tree668644acf4c2c51ebcf9dea8e9c0533039c690d3
parent197ca79abd3324e65e2b9e06de7ae7f451282238 (diff)
parent7361f8b29b89bb80f384054bc5cce1dea35f376b (diff)
Merge branch 'wait-for-device-fix'
-rw-r--r--src/xserver.cpp109
-rw-r--r--test/xserver-test.cpp120
2 files changed, 225 insertions, 4 deletions
diff --git a/src/xserver.cpp b/src/xserver.cpp
index 1ba4e08..9803e1f 100644
--- a/src/xserver.cpp
+++ b/src/xserver.cpp
@@ -164,18 +164,117 @@ bool xorg::testing::XServer::WaitForEventOfType(::Display *display, int type, in
return false;
}
+static XIEventMask* set_hierarchy_mask(::Display *display,
+ int *nmasks_out,
+ bool *was_set,
+ bool *was_created)
+{
+ XIEventMask *masks;
+ int nmasks;
+ bool mask_toggled = false;
+ bool new_mask_created = false;
+ XIEventMask *all_devices_mask = NULL;
+
+ masks = XIGetSelectedEvents(display, DefaultRootWindow(display), &nmasks);
+
+ /* masks is in a quirky data format (one chunk of memory). Change into a
+ format easier to manipulate. */
+
+ /* extra one, in case we have zero masks or no XIAllDevices mask */
+ XIEventMask *new_masks = new XIEventMask[nmasks + 1];
+ for (int i = 0; i < nmasks; i++) {
+ XIEventMask *m = &new_masks[i];
+ *m = masks[i];
+
+ if (masks[i].deviceid == XIAllDevices) {
+ all_devices_mask = m;
+ if (masks[i].mask_len < XIMaskLen(XI_HierarchyChanged)) {
+ m->mask_len = XIMaskLen(XI_HierarchyChanged);
+ mask_toggled = true;
+ } else
+ mask_toggled = !XIMaskIsSet(m->mask, XI_HierarchyChanged);
+ }
+
+ m->mask = new unsigned char[m->mask_len]();
+ memcpy(m->mask, masks[i].mask, masks[i].mask_len);
+
+ if (mask_toggled && m->deviceid == XIAllDevices)
+ XISetMask(m->mask, XI_HierarchyChanged);
+ }
+
+ if (!all_devices_mask) {
+ all_devices_mask = &new_masks[nmasks++];
+ all_devices_mask->deviceid = XIAllDevices;
+ all_devices_mask->mask_len = XIMaskLen(XI_HierarchyChanged);
+ all_devices_mask->mask = new unsigned char[all_devices_mask->mask_len]();
+ XISetMask(all_devices_mask->mask, XI_HierarchyChanged);
+ new_mask_created = true;
+ }
+
+ XFree(masks);
+ masks = NULL;
+
+ if (new_mask_created || mask_toggled) {
+ XISelectEvents(display, DefaultRootWindow(display), new_masks, nmasks);
+ XFlush(display);
+ }
+
+ *was_set = mask_toggled;
+ *was_created = new_mask_created;
+ *nmasks_out = nmasks;
+
+ return new_masks;
+}
+
+static void unset_hierarchy_mask(::Display *display,
+ XIEventMask *masks, int nmasks,
+ bool was_set, bool was_created)
+{
+ if (was_set || was_created) {
+ if (was_set) {
+ for (int i = 0; i < nmasks; i++) {
+ if (masks[i].deviceid == XIAllDevices)
+ XIClearMask(masks[i].mask, XI_HierarchyChanged);
+ }
+ } else if (was_created)
+ masks[nmasks - 1].mask_len = 0;
+ XISelectEvents(display, DefaultRootWindow(display), masks, nmasks);
+ XFlush(display);
+ }
+
+ for (int i = 0; i < nmasks; i++)
+ delete[] masks[i].mask;
+ delete[] masks;
+}
+
bool xorg::testing::XServer::WaitForDevice(::Display *display, const std::string &name,
time_t timeout)
{
int opcode;
int event_start;
int error_start;
+ bool device_found = false;
if (!XQueryExtension(display, "XInputExtension", &opcode, &event_start,
&error_start))
throw std::runtime_error("Failed to query XInput extension");
- while (WaitForEventOfType(display, GenericEvent, opcode,
+ XIEventMask *masks;
+ int nmasks;
+ bool mask_set, mask_created;
+ masks = set_hierarchy_mask(display, &nmasks, &mask_set, &mask_created);
+
+ XIDeviceInfo *info;
+ int ndevices;
+
+ info = XIQueryDevice(display, XIAllDevices, &ndevices);
+ for (int i = 0; !device_found && i < ndevices; i++) {
+ device_found = (name.compare(info[i].name) == 0);
+ }
+ XIFreeDeviceInfo(info);
+
+ while (!device_found &&
+ WaitForEventOfType(display, GenericEvent, opcode,
XI_HierarchyChanged, timeout)) {
XEvent event;
if (XNextEvent(display, &event) != Success)
@@ -194,7 +293,7 @@ bool xorg::testing::XServer::WaitForDevice(::Display *display, const std::string
continue;
}
- bool device_found = false;
+ device_found = false;
for (int i = 0; i < hierarchy_event->num_info; i++) {
if (!(hierarchy_event->info[i].flags & XIDeviceEnabled))
continue;
@@ -215,10 +314,12 @@ bool xorg::testing::XServer::WaitForDevice(::Display *display, const std::string
XFreeEventData(display, xcookie);
if (device_found)
- return true;
+ break;
}
- return false;
+ unset_hierarchy_mask(display, masks, nmasks, mask_set, mask_created);
+
+ return device_found;
}
void xorg::testing::XServer::WaitForConnections(void) {
diff --git a/test/xserver-test.cpp b/test/xserver-test.cpp
index 5faa6ca..932dff6 100644
--- a/test/xserver-test.cpp
+++ b/test/xserver-test.cpp
@@ -5,6 +5,7 @@
#include <fstream>
#include <xorg/gtest/xorg-gtest.h>
+#include <X11/extensions/XInput2.h>
using namespace xorg::testing;
@@ -71,6 +72,125 @@ TEST(XServer, WaitForSIGUSR1)
}
}
+static void assert_masks_equal(Display *dpy)
+{
+ int nmasks_before;
+ XIEventMask *masks_before;
+ int nmasks_after;
+ XIEventMask *masks_after;
+
+ masks_before = XIGetSelectedEvents(dpy, DefaultRootWindow(dpy), &nmasks_before);
+ XServer::WaitForDevice(dpy, "not actually waiting for device", 1);
+ masks_after = XIGetSelectedEvents(dpy, DefaultRootWindow(dpy), &nmasks_after);
+
+ ASSERT_EQ(nmasks_before, nmasks_after);
+
+ for (int i = 0; i < nmasks_before; i++) {
+ ASSERT_EQ(masks_before[i].deviceid, masks_after[i].deviceid);
+ ASSERT_EQ(masks_before[i].mask_len, masks_after[i].mask_len);
+ ASSERT_EQ(memcmp(masks_before[i].mask, masks_after[i].mask, masks_before[i].mask_len), 0);
+ }
+
+ XFree(masks_before);
+ XFree(masks_after);
+
+}
+
+TEST(XServer, WaitForDeviceEventMask)
+{
+ XORG_TESTCASE("The event mask is left as-is by WaitForDevice");
+
+ XServer server;
+ server.SetOption("-logfile", "/tmp/Xorg-WaitForDevice.log");
+ server.SetOption("-noreset", "");
+ server.Start();
+ ASSERT_EQ(server.GetState(), Process::RUNNING);
+ ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
+ ASSERT_TRUE(dpy != NULL);
+ int major = 2, minor = 0;
+ XIQueryVersion(dpy, &major, &minor);
+
+ /* empty mask */
+ assert_masks_equal(dpy);
+
+ /* device specific mask */
+ XIEventMask m;
+ m.deviceid = 2;
+ m.mask_len = 1;
+ m.mask = new unsigned char[m.mask_len]();
+ XISetMask(m.mask, XI_Motion);
+ XISelectEvents(dpy, DefaultRootWindow(dpy), &m, 1);
+
+ assert_masks_equal(dpy);
+ delete m.mask;
+
+ /* XIAllDevices mask with short mask */
+ m.deviceid = XIAllDevices;
+ m.mask_len = 1;
+ m.mask = new unsigned char[m.mask_len]();
+ XISetMask(m.mask, XI_Motion);
+ XISelectEvents(dpy, DefaultRootWindow(dpy), &m, 1);
+
+ assert_masks_equal(dpy);
+ delete m.mask;
+
+ /* XIAllDevices mask with hierarchy bit not set */
+ m.deviceid = XIAllDevices;
+ m.mask_len = XIMaskLen(XI_HierarchyChanged);
+ m.mask = new unsigned char[m.mask_len]();
+ XISetMask(m.mask, XI_Motion);
+ XISelectEvents(dpy, DefaultRootWindow(dpy), &m, 1);
+
+ assert_masks_equal(dpy);
+ delete m.mask;
+
+ /* XIAllDevices mask with hierarchy bit set */
+ m.deviceid = XIAllDevices;
+ m.mask_len = XIMaskLen(XI_HierarchyChanged);
+ m.mask = new unsigned char[m.mask_len]();
+ XISetMask(m.mask, XI_HierarchyChanged);
+ XISelectEvents(dpy, DefaultRootWindow(dpy), &m, 1);
+
+ assert_masks_equal(dpy);
+ delete m.mask;
+}
+
+#ifdef HAVE_EVEMU
+TEST(XServer, WaitForExistingDevice)
+{
+ XORG_TESTCASE("WaitForDevice() returns true for already existing device");
+
+ xorg::testing::evemu::Device d(TEST_ROOT_DIR "PIXART-USB-OPTICAL-MOUSE.desc");
+
+ XServer server;
+ server.SetOption("-logfile", "/tmp/Xorg-WaitForDevice.log");
+ server.SetOption("-noreset", "");
+ server.Start();
+ ASSERT_EQ(server.GetState(), Process::RUNNING);
+ ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
+ ASSERT_TRUE(dpy != NULL);
+
+ ASSERT_TRUE(XServer::WaitForDevice(dpy, "PIXART USB OPTICAL MOUSE", 1000));
+}
+
+TEST(XServer, WaitForNewDevice)
+{
+ XORG_TESTCASE("WaitForDevice() waits for newly created dvice");
+
+ XServer server;
+ server.SetOption("-logfile", "/tmp/Xorg-WaitForDevice.log");
+ server.SetOption("-noreset", "");
+ server.Start();
+ ASSERT_EQ(server.GetState(), Process::RUNNING);
+ ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
+ ASSERT_TRUE(dpy != NULL);
+
+ xorg::testing::evemu::Device d(TEST_ROOT_DIR "PIXART-USB-OPTICAL-MOUSE.desc");
+
+ ASSERT_TRUE(XServer::WaitForDevice(dpy, "PIXART USB OPTICAL MOUSE", 1000));
+}
+#endif
+
int main(int argc, char *argv[]) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();