1 #pragma once
2 
3 #include <fcntl.h>
4 #include <libevdev/libevdev.h>
5 #include <memory>
6 #include <phosphor-logging/elog.hpp>
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <string>
9 #include <tuple>
10 #include <unistd.h>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 namespace evdevpp
14 {
15 namespace evdev
16 {
17 
18 using EvDevPtr = libevdev*;
19 
20 namespace details
21 {
22 
23 /** @brief unique_ptr functor to release an evdev reference. */
24 struct EvDevDeleter
25 {
26     void operator()(libevdev* ptr) const
27     {
28         deleter(ptr);
29     }
30 
31     decltype(&libevdev_free) deleter = libevdev_free;
32 };
33 
34 /* @brief Alias evdev to a unique_ptr type for auto-release. */
35 using EvDev = std::unique_ptr<libevdev, EvDevDeleter>;
36 
37 } // namespace details
38 
39 /** @class EvDev
40  *  @brief Provides C++ bindings to the libevdev C API.
41  */
42 class EvDev
43 {
44     private:
45         using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
46             Error::InternalFailure;
47 
48     public:
49         /* Define all of the basic class operations:
50          *     Not allowed:
51          *         - Default constructor to avoid nullptrs.
52          *         - Copy operations due to internal unique_ptr.
53          *     Allowed:
54          *         - Move operations.
55          *         - Destructor.
56          */
57         EvDev() = delete;
58         EvDev(const EvDev&) = delete;
59         EvDev& operator=(const EvDev&) = delete;
60         EvDev(EvDev&&) = default;
61         EvDev& operator=(EvDev&&) = default;
62         ~EvDev() = default;
63 
64         /** @brief Conversion constructor from evdev. */
65         explicit EvDev(EvDevPtr ptr) : evdev(ptr) {}
66 
67         /** @brief Get the current event state. */
68         auto fetch(unsigned int type, unsigned int code)
69         {
70             int val;
71             auto rc = libevdev_fetch_event_value(
72                     evdev.get(), type, code, &val);
73             if (!rc)
74             {
75                 phosphor::logging::elog<InternalFailure>();
76             }
77 
78             return val;
79         }
80 
81         /** @brief Get the next event. */
82         auto next()
83         {
84             struct input_event ev;
85             while (true)
86             {
87                 auto rc = libevdev_next_event(
88                         evdev.get(), LIBEVDEV_READ_FLAG_NORMAL, &ev);
89                 if (rc < 0)
90                 {
91                     phosphor::logging::elog<InternalFailure>();
92                 }
93 
94                 if (ev.type == EV_SYN && ev.code == SYN_REPORT)
95                     continue;
96 
97                 break;
98             }
99             return std::make_tuple(ev.type, ev.code, ev.value);
100         }
101 
102     private:
103         EvDevPtr get()
104         {
105             return evdev.get();
106         }
107 
108         details::EvDev evdev;
109 };
110 
111 inline auto newFromFD(int fd)
112 {
113     using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
114         Error::InternalFailure;
115 
116     EvDevPtr dev = nullptr;
117     auto rc = libevdev_new_from_fd(fd, &dev);
118 
119     if (rc)
120     {
121         phosphor::logging::elog<InternalFailure>();
122     }
123 
124     return EvDev(dev);
125 }
126 } // namespace evdev
127 } // namespace evdevpp
128