evdevpp: Add C++ bindings for libevdev

Change-Id: Iff27168588ee0767902801e6df3c6745a643ac9a
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/configure.ac b/configure.ac
index e9b8065..b7842fd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,6 +30,8 @@
 [AC_MSG_ERROR([Could not find systemd...systemd developement package required])])
 PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces], ,
 [AC_MSG_ERROR([Could not find phosphor-dbus-interfaces...openbmc/phosphor-dbus-interfaces package required])])
+PKG_CHECK_MODULES([LIBEVDEV], [libevdev], ,
+[AC_MSG_ERROR([The libevdev package is required])])
 
 # Checks for library functions.
 LT_INIT # Required for systemd linking
@@ -132,8 +134,6 @@
 ])
 
 AS_IF([test "x$enable_cooling_type" != "xno"], [
-       PKG_CHECK_MODULES([LIBEVDEV], [libevdev], ,
-                         [AC_MSG_ERROR([The libevdev package is required])])
        AC_CONFIG_FILES([cooling-type/Makefile])
 ])
 AS_IF([test "x$enable_monitor" != "xno"], [
diff --git a/evdevpp/evdev.hpp b/evdevpp/evdev.hpp
new file mode 100644
index 0000000..1861982
--- /dev/null
+++ b/evdevpp/evdev.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <fcntl.h>
+#include <libevdev/libevdev.h>
+#include <memory>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <string>
+#include <tuple>
+#include <unistd.h>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace evdevpp
+{
+namespace evdev
+{
+
+using EvDevPtr = libevdev*;
+
+namespace details
+{
+
+/** @brief unique_ptr functor to release an evdev reference. */
+struct EvDevDeleter
+{
+    void operator()(libevdev* ptr) const
+    {
+        deleter(ptr);
+    }
+
+    decltype(&libevdev_free) deleter = libevdev_free;
+};
+
+/* @brief Alias evdev to a unique_ptr type for auto-release. */
+using EvDev = std::unique_ptr<libevdev, EvDevDeleter>;
+
+} // namespace details
+
+/** @class EvDev
+ *  @brief Provides C++ bindings to the libevdev C API.
+ */
+class EvDev
+{
+    private:
+        using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
+            Error::InternalFailure;
+
+    public:
+        /* Define all of the basic class operations:
+         *     Not allowed:
+         *         - Default constructor to avoid nullptrs.
+         *         - Copy operations due to internal unique_ptr.
+         *     Allowed:
+         *         - Move operations.
+         *         - Destructor.
+         */
+        EvDev() = delete;
+        EvDev(const EvDev&) = delete;
+        EvDev& operator=(const EvDev&) = delete;
+        EvDev(EvDev&&) = default;
+        EvDev& operator=(EvDev&&) = default;
+        ~EvDev() = default;
+
+        /** @brief Conversion constructor from evdev. */
+        explicit EvDev(EvDevPtr ptr) : evdev(ptr) {}
+
+        /** @brief Get the current event state. */
+        auto fetch(unsigned int type, unsigned int code)
+        {
+            int val;
+            auto rc = libevdev_fetch_event_value(
+                    evdev.get(), type, code, &val);
+            if (!rc)
+            {
+                phosphor::logging::elog<InternalFailure>();
+            }
+
+            return val;
+        }
+
+        /** @brief Get the next event. */
+        auto next()
+        {
+            struct input_event ev;
+            while (true)
+            {
+                auto rc = libevdev_next_event(
+                        evdev.get(), LIBEVDEV_READ_FLAG_NORMAL, &ev);
+                if (rc < 0)
+                {
+                    phosphor::logging::elog<InternalFailure>();
+                }
+
+                if (ev.type == EV_SYN && ev.code == SYN_REPORT)
+                    continue;
+
+                break;
+            }
+            return std::make_tuple(ev.type, ev.code, ev.value);
+        }
+
+    private:
+        EvDevPtr get()
+        {
+            return evdev.get();
+        }
+
+        details::EvDev evdev;
+};
+
+inline auto newFromFD(int fd)
+{
+    using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
+        Error::InternalFailure;
+
+    EvDevPtr dev = nullptr;
+    auto rc = libevdev_new_from_fd(fd, &dev);
+
+    if (rc)
+    {
+        phosphor::logging::elog<InternalFailure>();
+    }
+
+    return EvDev(dev);
+}
+} // namespace evdev
+} // namespace evdevpp
diff --git a/presence/Makefile.am b/presence/Makefile.am
index 0f7e7dd..c3c32a5 100644
--- a/presence/Makefile.am
+++ b/presence/Makefile.am
@@ -15,11 +15,13 @@
 	$(top_builddir)/libfan.la \
 	$(SDBUSPLUS_LIBS) \
 	$(PHOSPHOR_LOGGING_LIBS) \
-	${PHOSPHOR_DBUS_INTERFACES_LIBS}
+	${PHOSPHOR_DBUS_INTERFACES_LIBS} \
+	$(LIBEVDEV_LIBS)
 phosphor_fan_presence_tach_CXXFLAGS = \
 	$(SDBUSPLUS_CFLAGS) \
 	$(PHOSPHOR_LOGGING_CFLAGS) \
-	${PHOSPHOR_DBUS_INTERFACES_CFLAGS}
+	${PHOSPHOR_DBUS_INTERFACES_CFLAGS} \
+	$(LIBEVDEV_CFLAGS)
 
 BUILT_SOURCES = fan_detect_defs.cpp