[PATCH xorg-gtest 16/16] Add Device::GetDeviceNode() to return device node path from an evemu device

Peter Hutterer peter.hutterer at who-t.net
Tue Jul 3 23:03:47 PDT 2012


On Tue, Jul 03, 2012 at 11:40:25AM -0700, Chase Douglas wrote:
> On 07/02/2012 11:44 PM, Peter Hutterer wrote:
> >evemu doesn't export this information and even evemu-device just trawls
> >through the file system to print this info. So do the same here, noting the
> >time before evemu_create() and the ctime of the new device file. If the
> >latter is later than the former and the device names match, we can assume
> >this is our device.
> 
> I just want to point out that it's a deficiency in uinput, not in
> evemu, in case anyone had any thoughts of trying to fix evemu
> instead.

I did try. This could easily be part of evemu, running this exact same code
right after the UI_DEV_CREATE call. Unfortunately evemu_create() takes a
const struct so we can't save it easily without breaking ABI.

[memleak fixed, thanks for pointing this out]

Cheers,
  Peter

> >Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> >---
> >  include/xorg/gtest/evemu/xorg-gtest-device.h |   13 ++++++
> >  src/device.cpp                               |   65 +++++++++++++++++++++++++-
> >  2 files changed, 77 insertions(+), 1 deletion(-)
> >
> >diff --git a/include/xorg/gtest/evemu/xorg-gtest-device.h b/include/xorg/gtest/evemu/xorg-gtest-device.h
> >index 5f8faa2..359e65b 100644
> >--- a/include/xorg/gtest/evemu/xorg-gtest-device.h
> >+++ b/include/xorg/gtest/evemu/xorg-gtest-device.h
> >@@ -75,6 +75,17 @@ class Device {
> >     */
> >    void Play(const std::string& path) const;
> >
> >+  /**
> >+   * Return the /dev/input/eventX device node for this device.
> >+   *
> >+   * Note that evemu doesn't know the device node, so we traverse the file
> >+   * system looking for it. There is a tiny chance of the device node being
> >+   * wrong.
> >+   *
> >+   * @return The string representing the device node
> >+   */
> >+  const char* GetDeviceNode(void);
> >+
> >   private:
> >    struct Private;
> >    std::auto_ptr<Private> d_;
> >@@ -82,6 +93,8 @@ class Device {
> >    /* Disable copy constructor & assignment operator */
> >    Device(const Device&);
> >    Device& operator=(const Device&);
> >+
> >+  void GuessDeviceNode(time_t ctime);
> >  };
> >
> >  } // namespace evemu
> >diff --git a/src/device.cpp b/src/device.cpp
> >index 226d4e0..555e3e0 100644
> >--- a/src/device.cpp
> >+++ b/src/device.cpp
> >@@ -28,18 +28,74 @@
> >  #include "xorg/gtest/evemu/xorg-gtest-device.h"
> >
> >  #include <fcntl.h>
> >+#include <dirent.h>
> >
> >  #include <stdexcept>
> >
> >  #include <gtest/gtest.h>
> >
> >+#define SYS_INPUT_DIR "/sys/class/input"
> >+#define DEV_INPUT_DIR "/dev/input/"
> >+
> >  struct xorg::testing::evemu::Device::Private {
> >-  Private() : fd(-1), device(NULL) {}
> >+  Private() : fd(-1), device(NULL), device_node() {}
> >
> >    int fd;
> >    struct evemu_device* device;
> >+  std::string device_node;
> >  };
> >
> >+static int _event_device_compare(const struct dirent **a,
> >+                                 const struct dirent **b) {
> >+  int na, nb;
> >+
> >+  sscanf((*a)->d_name, "event%d", &na);
> >+  sscanf((*b)->d_name, "event%d", &nb);
> >+
> >+  return (na > nb) ? 1 : (na < nb) ? -1 : 0;
> >+
> >+}
> >+
> >+static int _event_device_filter(const struct dirent *d) {
> >+  return (strncmp("event", d->d_name, sizeof("event") - 1) == 0);
> >+}
> >+
> >+void xorg::testing::evemu::Device::GuessDeviceNode(time_t ctime) {
> >+  struct dirent **event_devices;
> >+  int n_event_devices;
> >+
> >+  n_event_devices = scandir(SYS_INPUT_DIR, &event_devices,
> >+                            _event_device_filter, _event_device_compare);
> >+
> >+  if (n_event_devices < 0) {
> >+    std::cerr << "Failed to guess device node." << std::endl;
> >+    return;
> >+  }
> >+
> >+  bool found = false;
> >+  for (int i = 0; i < n_event_devices && !found; i++) {
> >+    std::stringstream s;
> >+    s << DEV_INPUT_DIR << event_devices[i]->d_name;
> >+
> >+    int fd = open(s.str().c_str(), O_RDONLY);
> >+    char device_name[256];
> >+
> >+    ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name);
> >+    if (strcmp(device_name, evemu_get_name(d_->device)) == 0) {
> >+      struct stat buf;
> >+      if (fstat(fd, &buf) == 0) {
> >+        if (buf.st_ctime >= ctime) {
> >+          d_->device_node = s.str();
> >+          found = true;
> >+        }
> >+      }
> >+    }
> >+    close(fd);
> >+    free(event_devices[i]);
> 
> We need to free each event device entry, but if we break out of the
> loops early because we've found a matching device, then we won't
> free the rest of the devices in the array.
> 
> >+  }
> >+  free(event_devices);
> >+}
> >+
> >  xorg::testing::evemu::Device::Device(const std::string& path)
> >      : d_(new Private) {
> >    static const char UINPUT_NODE[] = "/dev/uinput";
> >@@ -68,11 +124,14 @@ xorg::testing::evemu::Device::Device(const std::string& path)
> >      throw std::runtime_error("Failed to open uinput node");
> >    }
> >
> >+  time_t ctime = time(NULL);
> >    if (evemu_create(d_->device, d_->fd) < 0) {
> >      close(d_->fd);
> >      evemu_delete(d_->device);
> >      throw std::runtime_error("Failed to create evemu device");
> >    }
> >+
> >+  GuessDeviceNode(ctime);
> >  }
> >
> >  void xorg::testing::evemu::Device::Play(const std::string& path) const {
> >@@ -88,6 +147,10 @@ void xorg::testing::evemu::Device::Play(const std::string& path) const {
> >    fclose(file);
> >  }
> >
> >+const char* xorg::testing::evemu::Device::GetDeviceNode(void) {
> >+  return d_->device_node.length() > 0 ? d_->device_node.c_str() : NULL;
> >+}
> >+
> >  xorg::testing::evemu::Device::~Device() {
> >    close(d_->fd);
> >    evemu_delete(d_->device);
> >
> 


More information about the xorg-devel mailing list