summaryrefslogtreecommitdiffhomepage
path: root/Touchpad.cpp
blob: 9d3fe22b45097e963fe02d13366038c3cae70439 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "Touchpad.h"

#include <filesystem>
#include <fmt/format.h>
#include <sys/poll.h>
#include <fcntl.h>

#include "log.h"

namespace fs = std::filesystem;

namespace {
  fs::path eventfile_from_index(int i)
  {
    fs::path result{"/dev/input/event"};
    result += fs::path{std::to_string(i)};
    return result;
  }

  libevdev* get_evdev(int& result_fd)
  {
    for (int i = 0; ; ++i) {
      fs::path filename = eventfile_from_index(i);
      if (!fs::exists(filename)) {
        log_cout << "Warning: No suitable evdev device found.\n";
        return nullptr;
      }

      int fd = open(filename.string().c_str(), O_RDONLY | O_NONBLOCK);
      if (fd < 0) {
        log_cout << fmt::format("Warning: evdev open() failed for {}", filename.string()) << std::endl;
        continue;
      }
      libevdev* result{};
      int rc = libevdev_new_from_fd(fd, &result);
      if (rc < 0) {
        log_cout << fmt::format("Warning: Failed to init libevdev ({})\n", strerror(-rc));
        continue;
      }

      if (!libevdev_has_event_type(result, EV_REL) ||
        !libevdev_has_event_code(result, EV_KEY, BTN_LEFT) ||
        !libevdev_has_event_code(result, EV_KEY, BTN_RIGHT))
      {
        // Not the correct device
        continue;
      }
      log_cout << fmt::format("evdev using: Input device name: \"{}\"\n", libevdev_get_name(result));
      log_cout << fmt::format("evdev using: Input device ID: bus {:x} vendor {:x} product {:x}\n",
        libevdev_get_id_bustype(result),
        libevdev_get_id_vendor(result),
        libevdev_get_id_product(result));
      result_fd = fd;
      return result;
    }
  }

  void free_evdev(libevdev* evdev, int fd)
  {
    if (evdev) {
      libevdev_free(evdev);
      close(fd);
    }
  }
}

Touchpad::Touchpad(): m_fd{}, m_dev{}, m_valid{}, m_ev{}
{
  m_dev = get_evdev(m_fd);
  if (m_dev != nullptr) {
    m_valid = true;
  }
}

Touchpad::~Touchpad()
{
  free_evdev(m_dev, m_fd);
}

bool Touchpad::is_valid()
{
  return m_valid;
}

bool Touchpad::event_available()
{
  if (!m_valid) {
    return false;
  }

  if (m_ev.has_value()) {
    return true;
  }

  input_event ev{};
  int rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
  if (rc != 0) {
    return false;
  }
  log_cout << fmt::format("Event: {} {} {}\n",
                       libevdev_event_type_get_name(ev.type),
                       libevdev_event_code_get_name(ev.type, ev.code),
                       ev.value);

  m_ev = ev;

  return true;
}

input_event Touchpad::get_event()
{
  if (m_ev.has_value()) {
    input_event result = *m_ev;
    m_ev.reset();
    return result;
  }

  input_event ev{};
  int rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
  if (rc != 0) {
    throw std::runtime_error("Touchpad::get_event() couldn't read event");
  }
  log_cout << fmt::format("Event: {} {} {}\n",
                       libevdev_event_type_get_name(ev.type),
                       libevdev_event_code_get_name(ev.type, ev.code),
                       ev.value);
  return ev;
}

bool Touchpad::event_is_button1(const input_event& ev)
{
  return ev.type == EV_KEY && ev.code == BTN_LEFT && ev.value == 0; // Press down
}

bool Touchpad::event_is_button2(const input_event& ev)
{
  return ev.type == EV_KEY && ev.code == BTN_RIGHT && ev.value == 0; // Press down
}