[PATCH xorg-gtest 1/2] xserver: set the hierarchy mask before WaitForDevice

Peter Hutterer peter.hutterer at who-t.net
Tue Oct 16 20:16:23 PDT 2012


Don't rely on the caller to set up the mask correctly, modify its current
mask to include the hierarchy mask and then un-do that modification (if
needed)

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/xserver.cpp       | 100 +++++++++++++++++++++++++++++++++++++++--
 test/xserver-test.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 216 insertions(+), 4 deletions(-)

diff --git a/src/xserver.cpp b/src/xserver.cpp
index 1ba4e08..a774ad5 100644
--- a/src/xserver.cpp
+++ b/src/xserver.cpp
@@ -164,18 +164,108 @@ 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);
+
+    while (!device_found &&
+           WaitForEventOfType(display, GenericEvent, opcode,
                               XI_HierarchyChanged, timeout)) {
         XEvent event;
         if (XNextEvent(display, &event) != Success)
@@ -194,7 +284,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 +305,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();
-- 
1.7.11.4



More information about the xorg-devel mailing list