Skip to content

Commit 0d94c7f

Browse files
committed
Implement input device discovery
Instead of using hardcoded paths to input devices, VNSee now enumerates all available input devices and opens the ones that match expected criteria. This makes it robust to device path changes, such as the one that happened on rM1 version 2.9.
1 parent 2c244b0 commit 0d94c7f

20 files changed

Lines changed: 399 additions & 113 deletions

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.7)
2-
project(VNSee VERSION 0.3.1)
2+
project(VNSee VERSION 0.4.1)
33

44
find_package(PkgConfig REQUIRED)
55
pkg_check_modules(LibVNCClient libvncclient)
@@ -57,6 +57,7 @@ add_executable(vnsee
5757
src/main.cpp
5858
src/rmioc/buttons.cpp
5959
src/rmioc/device.cpp
60+
src/rmioc/file.cpp
6061
src/rmioc/input.cpp
6162
src/rmioc/mxcfb.cpp
6263
src/rmioc/pen.cpp

src/app/client.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ client::~client()
125125
rfbClientCleanup(this->vnc_client);
126126
}
127127

128+
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
128129
auto client::event_loop() -> bool
129130
{
130131
// Maximum time to wait before timeout in the next poll
131-
int timeout = -1;
132+
long timeout = -1;
132133

133134
// Flag used for quitting the event loop
134135
bool quit = false;
@@ -158,7 +159,7 @@ auto client::event_loop() -> bool
158159
while (poll(
159160
this->polled_fds.data(),
160161
this->polled_fds.size(),
161-
timeout) == -1)
162+
static_cast<int>(timeout)) == -1)
162163
{
163164
if (errno != EAGAIN)
164165
{

src/app/event_loop.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct event_loop_status
1919
* The minimum timeout among all the event loop subroutines will be used.
2020
* Can be -1 if no more work is needed (wait indefinitely).
2121
*/
22-
int timeout;
22+
long timeout;
2323
};
2424

2525
/** List of mouse button flags used by the VNC protocol. */

src/app/screen.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,17 @@ constexpr chrono::milliseconds fast_repaint_delay{50};
3333
namespace app
3434
{
3535

36+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-avoid-non-const-global-variables,cppcoreguidelines-avoid-magic-numbers)
37+
void* screen::instance_tag = reinterpret_cast<void*>(6803);
38+
3639
screen::screen(rmioc::screen& device, rfbClient* vnc_client)
3740
: device(device)
3841
, vnc_client(vnc_client)
3942
, repaint_mode(repaint_modes::standard)
4043
{
4144
rfbClientSetClientData(
4245
this->vnc_client,
43-
// ↓ Use of C library
44-
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
45-
reinterpret_cast<void*>(screen::instance_tag),
46+
screen::instance_tag,
4647
this
4748
);
4849

@@ -117,7 +118,7 @@ auto screen::event_loop() -> event_loop_status
117118
);
118119

119120
auto now = chrono::steady_clock::now();
120-
int wait_time = chrono::duration_cast<chrono::milliseconds>(
121+
long wait_time = chrono::duration_cast<chrono::milliseconds>(
121122
next_update_time - now
122123
).count();
123124

@@ -144,8 +145,7 @@ auto screen::create_framebuf(rfbClient* vnc_client) -> rfbBool
144145
auto* that = reinterpret_cast<screen*>(
145146
rfbClientGetClientData(
146147
vnc_client,
147-
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
148-
reinterpret_cast<void*>(screen::instance_tag)
148+
screen::instance_tag
149149
));
150150

151151
int xres = static_cast<int>(that->device.get_xres());
@@ -179,8 +179,7 @@ void screen::recv_update(
179179
auto* that = reinterpret_cast<screen*>(
180180
rfbClientGetClientData(
181181
vnc_client,
182-
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
183-
reinterpret_cast<void*>(screen::instance_tag)
182+
screen::instance_tag
184183
));
185184

186185
if (x < 0 || y < 0
@@ -226,8 +225,7 @@ void screen::commit_updates(rfbClient* vnc_client, int x, int y, int w, int h)
226225
auto* that = reinterpret_cast<screen*>(
227226
rfbClientGetClientData(
228227
vnc_client,
229-
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
230-
reinterpret_cast<void*>(screen::instance_tag)
228+
screen::instance_tag
231229
));
232230

233231
// Register the region as pending update, potentially extending

src/app/screen.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class screen
132132
std::chrono::steady_clock::time_point last_repaint;
133133

134134
/** Tag used for accessing the instance from C callbacks. */
135-
static constexpr std::size_t instance_tag = 6803;
135+
static void* instance_tag;
136136

137137
/** Current repaint mode. */
138138
repaint_modes repaint_mode;

src/app/touch.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ auto touch::process_events(bool inhibit) -> event_loop_status
5454
// Compute the mean touch position
5555
int summed_x = 0;
5656
int summed_y = 0;
57-
int total_points = device_state.size();
57+
int total_points = static_cast<int>(device_state.size());
5858

5959
for (const auto& [id, slot] : device_state)
6060
{

src/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ constexpr int default_server_port = 5900;
4646
constexpr int min_port = 1;
4747
constexpr int max_port = (1U << 16U) - 1;
4848

49+
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
4950
auto main(int argc, const char* argv[]) -> int
5051
{
5152
// Read options from the command line

src/rmioc/buttons.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "buttons.hpp"
22
#include <vector>
3+
#include <fcntl.h>
34
#include <linux/input-event-codes.h>
45
#include <linux/input.h>
56

@@ -10,6 +11,20 @@ buttons::buttons(const char* device_path)
1011
: input(device_path)
1112
{}
1213

14+
auto buttons::is(const char* device_path) -> bool
15+
{
16+
file_descriptor input_fd{device_path, O_RDONLY};
17+
auto supp_events = supported_input_events(input_fd);
18+
19+
if (!supp_events.has_key())
20+
{
21+
return false;
22+
}
23+
24+
auto supp_keys = supported_key_types(input_fd);
25+
return supp_keys.has_power();
26+
}
27+
1328
auto buttons::process_events() -> bool
1429
{
1530
auto events = this->fetch_events();

src/rmioc/buttons.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class buttons : public input
1919
*/
2020
buttons(const char* device_path);
2121

22+
/** Check if an input device is a buttons device. */
23+
static bool is(const char* device_path);
24+
2225
/**
2326
* Fetch new events from the buttons and process them.
2427
*

src/rmioc/device.cpp

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#include "device.hpp"
22
#include "screen_mxcfb.hpp"
33
#include "screen_rm2fb.hpp"
4+
#include <fcntl.h>
45
#include <fstream>
6+
#include <filesystem>
57
#include <stdexcept>
68
#include <string>
79
#include <utility>
810

11+
namespace fs = std::filesystem;
12+
913
namespace rmioc
1014
{
1115

@@ -23,78 +27,94 @@ device::device(
2327
, screen_device(std::move(screen_device))
2428
{}
2529

26-
auto device::detect(device_request request) -> device
30+
auto get_device_type() -> device::types
2731
{
2832
std::ifstream device_id_file{"/sys/devices/soc0/machine"};
2933
std::string device_id;
3034
std::getline(device_id_file, device_id);
3135

32-
types type = types::reMarkable1;
33-
std::unique_ptr<buttons> buttons_device;
34-
std::unique_ptr<touch> touch_device;
35-
std::unique_ptr<pen> pen_device;
36-
std::unique_ptr<screen> screen_device;
37-
3836
if (device_id == "reMarkable 1.0" || device_id == "reMarkable Prototype 1")
3937
{
40-
type = types::reMarkable1;
41-
42-
if (request.has_buttons())
43-
{
44-
buttons_device = std::make_unique<buttons>("/dev/input/event2");
45-
}
38+
return device::types::reMarkable1;
39+
}
4640

47-
if (request.has_touch())
48-
{
49-
touch_device = std::make_unique<touch>(
50-
"/dev/input/event1",
51-
/* flip_x = */ true,
52-
/* flip_y = */ true
53-
);
54-
}
41+
if (device_id == "reMarkable 2.0")
42+
{
43+
return device::types::reMarkable2;
44+
}
5545

56-
if (request.has_pen())
57-
{
58-
pen_device = std::make_unique<pen>(
59-
"/dev/input/event0",
60-
/* flip_x = */ false,
61-
/* flip_y = */ true
62-
);
63-
}
46+
throw std::runtime_error{"Unknown reMarkable model (device identifier \
47+
is “" + device_id + ""};
48+
}
6449

65-
if (request.has_screen())
50+
void discover_input_devices(
51+
const char* base_path,
52+
device::types type,
53+
device_request request,
54+
std::unique_ptr<buttons>& buttons_device,
55+
std::unique_ptr<touch>& touch_device,
56+
std::unique_ptr<pen>& pen_device
57+
)
58+
{
59+
for (const auto& entry : fs::directory_iterator(base_path))
60+
{
61+
if (entry.is_character_file())
6662
{
67-
screen_device = std::make_unique<screen_mxcfb>("/dev/fb0");
63+
const char* path = entry.path().c_str();
64+
65+
if (buttons::is(path))
66+
{
67+
if (request.has_buttons())
68+
{
69+
buttons_device = std::make_unique<buttons>(path);
70+
}
71+
}
72+
else if (touch::is(path))
73+
{
74+
if (request.has_touch())
75+
{
76+
touch_device = std::make_unique<touch>(
77+
path,
78+
/* flip_x = */ type == device::types::reMarkable1,
79+
/* flip_y = */ true
80+
);
81+
}
82+
}
83+
else if (pen::is(path))
84+
{
85+
if (request.has_pen())
86+
{
87+
pen_device = std::make_unique<pen>(
88+
path,
89+
/* flip_x = */ false,
90+
/* flip_y = */ true
91+
);
92+
}
93+
}
6894
}
6995
}
70-
else if (device_id == "reMarkable 2.0")
71-
{
72-
type = types::reMarkable2;
96+
}
7397

74-
if (request.has_buttons())
75-
{
76-
buttons_device = std::make_unique<buttons>("/dev/input/event0");
77-
}
98+
auto device::detect(device_request request) -> device
99+
{
100+
std::ifstream device_id_file{"/sys/devices/soc0/machine"};
101+
std::string device_id;
102+
std::getline(device_id_file, device_id);
78103

79-
if (request.has_touch())
80-
{
81-
touch_device = std::make_unique<touch>(
82-
"/dev/input/event2",
83-
/* flip_x = */ false,
84-
/* flip_y = */ true
85-
);
86-
}
104+
types type = get_device_type();
105+
std::unique_ptr<buttons> buttons_device;
106+
std::unique_ptr<touch> touch_device;
107+
std::unique_ptr<pen> pen_device;
108+
std::unique_ptr<screen> screen_device;
87109

88-
if (request.has_pen())
110+
// Use the appropriate screen driver based on current device type
111+
if (request.has_screen())
112+
{
113+
if (type == types::reMarkable1)
89114
{
90-
pen_device = std::make_unique<pen>(
91-
"/dev/input/event1",
92-
/* flip_x = */ false,
93-
/* flip_y = */ true
94-
);
115+
screen_device = std::make_unique<screen_mxcfb>("/dev/fb0");
95116
}
96-
97-
if (request.has_screen())
117+
else
98118
{
99119
constexpr auto rm2fb_msgqueue_key = 0x2257c;
100120
screen_device = std::make_unique<screen_rm2fb>(
@@ -103,11 +123,12 @@ auto device::detect(device_request request) -> device
103123
);
104124
}
105125
}
106-
else
107-
{
108-
throw std::runtime_error{"Unknown reMarkable model (device identifier \
109-
says “" + device_id + ""};
110-
}
126+
127+
// Auto-detect and open requested input devices
128+
discover_input_devices(
129+
"/dev/input", type, request,
130+
buttons_device, touch_device, pen_device
131+
);
111132

112133
return device(
113134
type,

0 commit comments

Comments
 (0)