Skip to content

Commit de2f0ca

Browse files
committed
Adding support for udev instead of the sketchy globbing for devices
1 parent c64cd88 commit de2f0ca

3 files changed

Lines changed: 120 additions & 94 deletions

File tree

CMakeLists.txt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ cmake_minimum_required(VERSION 3.10)
2020
project(uiohook VERSION 1.2.0 LANGUAGES C)
2121

2222

23-
if (WIN32 OR WIN64)
23+
if(WIN32 OR WIN64)
2424
set(UIOHOOK_SOURCE_DIR "windows")
25-
elseif (APPLE)
25+
elseif(APPLE)
2626
set(UIOHOOK_SOURCE_DIR "darwin")
2727
else()
2828
set(UIOHOOK_SOURCE_DIR "evdev")
@@ -71,8 +71,8 @@ export(TARGETS uiohook FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.
7171
install(EXPORT ${PROJECT_NAME}-config DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
7272

7373

74-
if (BUILD_DEMO)
75-
if (NOT BUILD_SHARED_LIBS)
74+
if(BUILD_DEMO)
75+
if(NOT BUILD_SHARED_LIBS)
7676
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
7777
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
7878
endif()
@@ -111,7 +111,7 @@ if (BUILD_DEMO)
111111
add_compile_definitions(all_demos PRIVATE inline=__inline)
112112
add_compile_definitions(all_demos PRIVATE _CRT_SECURE_NO_WARNINGS)
113113

114-
if (MSVC_VERSION LESS "1900")
114+
if(MSVC_VERSION LESS "1900")
115115
add_compile_definitions(all_demos PRIVATE snprintf=_snprintf)
116116
endif()
117117
endif()
@@ -143,6 +143,10 @@ if(UNIX AND NOT APPLE)
143143
target_include_directories(uiohook PRIVATE "${EVDEV_INCLUDE_DIRS}")
144144
target_link_libraries(uiohook "${EVDEV_LDFLAGS}")
145145

146+
pkg_check_modules(UDEV REQUIRED libudev)
147+
target_include_directories(uiohook PRIVATE "${UDEV_INCLUDE_DIRS}")
148+
target_link_libraries(uiohook "${UDEV_LDFLAGS}")
149+
146150
#pkg_check_modules(XCB REQUIRED xcb)
147151
#target_include_directories(uiohook PRIVATE "${XCB_INCLUDE_DIRS}")
148152
#target_link_libraries(uiohook "${XCB_LDFLAGS}")

demo/demo_hook.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@
2323
#include <inttypes.h>
2424
#include <locale.h>
2525
#include <stdarg.h>
26-
#include <stdbool.h>
2726
#include <stdio.h>
28-
#include <string.h>
2927
#include <uiohook.h>
3028

3129

3230
static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) {
3331
switch (level) {
32+
default:
3433
case LOG_LEVEL_INFO:
3534
vfprintf(stdout, format, args);
3635
break;

src/evdev/input_hook.c

Lines changed: 110 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818

1919
#include <errno.h>
2020
#include <fcntl.h>
21-
#include <glob.h>
2221
#include <libevdev/libevdev.h>
2322
#include <libevdev/libevdev-uinput.h>
23+
#include <libudev.h>
2424
#include <stdlib.h>
25+
#include <string.h>
2526
#include <sys/epoll.h>
2627
#include <uiohook.h>
2728
#include <unistd.h>
@@ -30,14 +31,16 @@
3031
#include "input_helper.h"
3132
#include "logger.h"
3233

33-
#define EVENT_GLOB_PATTERN "/dev/input/event*"
34-
35-
3634
struct input_hook {
3735
struct libevdev *evdev;
3836
struct libevdev_uinput *uinput;
3937
};
4038

39+
struct epoll_event_listener {
40+
struct epoll_event epoll;
41+
struct epoll_event_listener *next;
42+
};
43+
4144
static int rel_x = 0, rel_y = 0;
4245
static int wheel_h = 0, wheel_v = 0;
4346

@@ -148,7 +151,7 @@ static bool hook_event_proc(struct input_event *ev) {
148151
return consumed;
149152
}
150153

151-
static int create_hook(char *path, struct input_hook **hook) {
154+
static int create_hook(const char *path, struct input_hook **hook) {
152155
int fd = open(path, O_RDONLY | O_NONBLOCK);
153156
if (fd < 0) {
154157
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to open input device: %s! (%d)\n",
@@ -158,21 +161,21 @@ static int create_hook(char *path, struct input_hook **hook) {
158161
}
159162

160163
*hook = malloc(sizeof(struct input_hook));
161-
if (hook == NULL) {
164+
if (*hook == NULL) {
162165
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for evdev buffer!\n",
163166
__FUNCTION__, __LINE__);
164167
return UIOHOOK_ERROR_OUT_OF_MEMORY;
165168
}
166169

167170
int err = libevdev_new_from_fd(fd, &(*hook)->evdev);
168171
if (err < 0) {
169-
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create evdev from file descriptor! (%d)\n",
172+
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create evdev from file descriptor: %s! (%d)\n",
170173
__FUNCTION__, __LINE__,
171-
err);
174+
path, err);
172175
return UIOHOOK_FAILURE;
173176
}
174177

175-
// We need to wait until no key is being pressed becasue we are going to grab below and that will cause keys to stick.
178+
// We need to wait until no key is being pressed because we are going to grab below and that will cause keys to stick.
176179
struct input_event ev;
177180
for (unsigned int i = 0; i < KEY_MAX; i++) {
178181
while (libevdev_get_event_value((*hook)->evdev, EV_KEY, i)) {
@@ -219,7 +222,7 @@ static int create_hook(char *path, struct input_hook **hook) {
219222
return UIOHOOK_FAILURE;
220223
}
221224

222-
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found %s device: %s.\n",
225+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Registering %s device: %s.\n",
223226
__FUNCTION__, __LINE__,
224227
label, path);
225228

@@ -228,107 +231,127 @@ static int create_hook(char *path, struct input_hook **hook) {
228231

229232
static void destroy_hook(struct input_hook **hook) {
230233
if (*hook != NULL) {
231-
int fd = libevdev_get_fd((*hook)->evdev);
232-
233-
if ((*hook)->evdev != NULL) {
234-
libevdev_grab((*hook)->evdev, LIBEVDEV_UNGRAB);
235-
libevdev_free((*hook)->evdev);
236-
(*hook)->evdev = NULL;
237-
}
238-
239-
if ((*hook)->uinput != NULL) {
240-
libevdev_uinput_destroy((*hook)->uinput);
241-
(*hook)->uinput = NULL;
242-
}
243-
244-
free(*hook);
245-
*hook = NULL;
246-
247-
if (fd >= 0) {
248-
close(fd);
249-
}
234+
return;
250235
}
251-
}
252236

237+
const int fd = libevdev_get_fd((*hook)->evdev);
253238

254-
static int create_glob_buffer(glob_t *glob_buffer) {
255-
int status = glob(EVENT_GLOB_PATTERN, GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, glob_buffer);
256-
switch (status) {
257-
case GLOB_NOSPACE:
258-
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for glob!\n",
259-
__FUNCTION__, __LINE__);
260-
return UIOHOOK_ERROR_OUT_OF_MEMORY;
261-
262-
default:
263-
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to call glob()! (%d)\n",
264-
__FUNCTION__, __LINE__,
265-
status);
266-
return UIOHOOK_FAILURE;
239+
if ((*hook)->evdev != NULL) {
240+
libevdev_grab((*hook)->evdev, LIBEVDEV_UNGRAB);
241+
libevdev_free((*hook)->evdev);
242+
(*hook)->evdev = NULL;
243+
}
267244

268-
case 0:
269-
// Success
245+
if ((*hook)->uinput != NULL) {
246+
libevdev_uinput_destroy((*hook)->uinput);
247+
(*hook)->uinput = NULL;
270248
}
271249

272-
return UIOHOOK_SUCCESS;
273-
}
250+
free(*hook);
251+
*hook = NULL;
274252

275-
static void destroy_glob(glob_t *glob_buffer) {
276-
globfree(glob_buffer);
253+
if (fd >= 0) {
254+
close(fd);
255+
}
277256
}
278257

279-
280-
static int create_event_listeners(int epoll_fd, struct epoll_event **listeners) {
281-
glob_t glob_buffer;
282-
int status = create_glob_buffer(&glob_buffer);
283-
if (status != UIOHOOK_SUCCESS) {
284-
destroy_glob(&glob_buffer);
285-
return status;
258+
static int create_event_listeners(int epoll_fd, struct epoll_event_listener **listeners) {
259+
struct udev *udev = udev_new();
260+
if (udev == NULL) {
261+
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create new udev context!\n",
262+
__FUNCTION__, __LINE__);
263+
return UIOHOOK_FAILURE;
286264
}
287265

288-
struct epoll_event *event_buffer = *listeners = malloc(sizeof(struct epoll_event) * glob_buffer.gl_pathc);
289-
if (event_buffer == NULL) {
290-
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for epoll event devices!\n",
291-
__FUNCTION__, __LINE__);
266+
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
267+
udev_enumerate_add_match_subsystem(enumerate, "input");
268+
udev_enumerate_add_match_sysname(enumerate, "event*");
269+
udev_enumerate_scan_devices(enumerate);
292270

293-
destroy_glob(&glob_buffer);
294-
return UIOHOOK_ERROR_OUT_OF_MEMORY;
295-
}
271+
struct epoll_event_listener **listener_next = listeners;
272+
273+
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
274+
struct udev_list_entry *dev_list_entry;
296275

297-
int found = 0;
298-
for (int i = 0; i < glob_buffer.gl_pathc; i++) {
299-
struct input_hook *hook = NULL;
300-
if (create_hook(glob_buffer.gl_pathv[i], &hook) != UIOHOOK_SUCCESS) {
301-
destroy_hook(&hook);
276+
udev_list_entry_foreach(dev_list_entry, devices) {
277+
const char *path = udev_list_entry_get_name(dev_list_entry);
278+
if (path == NULL) {
279+
logger(LOG_LEVEL_WARN, "%s [%u]: Failed to get udev entry name!\n",
280+
__FUNCTION__, __LINE__);
302281
continue;
303282
}
304283

305-
event_buffer[found].events = EPOLLIN;
306-
event_buffer[found].data.ptr = hook;
284+
struct udev_device *dev = udev_device_new_from_syspath(udev, path);
285+
if (dev == NULL) {
286+
logger(LOG_LEVEL_WARN, "%s [%u]: Failed to create new udev device: %s!\n",
287+
__FUNCTION__, __LINE__,
288+
path);
289+
continue;
290+
}
307291

308-
int fd = libevdev_get_fd(hook->evdev);
309-
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event_buffer[found]) < 0) {
310-
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to add file descriptor to epoll! (%d)\n",
311-
__FUNCTION__, __LINE__);
312-
313-
destroy_hook(&hook);
314-
event_buffer[found].data.ptr = NULL;
292+
const char *devnode = udev_device_get_devnode(dev);
293+
if (devnode == NULL) {
294+
logger(LOG_LEVEL_WARN, "%s [%u]: Failed to get udev device node: %s!\n",
295+
__FUNCTION__, __LINE__,
296+
path);
297+
udev_device_unref(dev);
315298
continue;
316299
}
317300

318-
found++;
319-
}
301+
const char *is_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
302+
const char *is_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
303+
if ((is_keyboard && strcmp(is_keyboard, "1") == 0) || (is_mouse && strcmp(is_mouse, "1") == 0)) {
304+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found udev input device: %s %s.\n",
305+
__FUNCTION__, __LINE__,
306+
udev_device_get_property_value(dev, "ID_VENDOR"),
307+
udev_device_get_property_value(dev, "ID_MODEL"));
308+
309+
*listener_next = malloc(sizeof(struct epoll_event_listener));
310+
if (*listener_next == NULL) {
311+
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for epoll event devices!\n",
312+
__FUNCTION__, __LINE__);
313+
udev_device_unref(dev);
314+
continue;
315+
}
320316

321-
destroy_glob(&glob_buffer);
317+
struct input_hook *hook = NULL;
318+
if (create_hook(devnode, &hook) != UIOHOOK_SUCCESS) {
319+
logger(LOG_LEVEL_ERROR, "%s [%u]: Staring destroy_hook 0x%p\n",
320+
__FUNCTION__, __LINE__, hook);
321+
destroy_hook(&hook);
322+
udev_device_unref(dev);
323+
free(*listener_next);
324+
*listener_next = NULL;
325+
continue;
326+
}
322327

323-
*listeners = realloc(event_buffer, sizeof(struct epoll_event *) * found);
324-
if (*listeners == NULL) {
325-
logger(LOG_LEVEL_WARN, "%s [%u]: Failed to realloc event listeners! (%d)\n",
326-
__FUNCTION__, __LINE__);
328+
struct epoll_event_listener *listener_current = *listener_next;
329+
listener_current->epoll.events = EPOLLIN;
330+
listener_current->epoll.data.ptr = hook;
331+
listener_current->next = NULL;
332+
333+
const int fd = libevdev_get_fd(hook->evdev);
334+
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &listener_current->epoll) < 0) {
335+
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to add file descriptor to epoll! (%d)\n",
336+
__FUNCTION__, __LINE__);
337+
338+
destroy_hook(&hook);
339+
listener_current->epoll.data.ptr = NULL;
340+
udev_device_unref(dev);
341+
free(*listener_next);
342+
*listener_next = NULL;
343+
continue;
344+
}
327345

328-
*listeners = event_buffer;
329-
event_buffer = NULL;
346+
listener_next = &listener_current->next;
347+
}
348+
349+
udev_device_unref(dev);
330350
}
331351

352+
udev_enumerate_unref(enumerate);
353+
udev_unref(udev);
354+
332355
return UIOHOOK_SUCCESS;
333356
}
334357

@@ -346,7 +369,7 @@ UIOHOOK_API int hook_run() {
346369
return UIOHOOK_ERROR_EPOLL_CREATE;
347370
}
348371

349-
struct epoll_event *listeners = NULL;
372+
struct epoll_event_listener *listeners = NULL;
350373
int status = create_event_listeners(epoll_fd, &listeners);
351374
if (status != UIOHOOK_SUCCESS) {
352375
close(epoll_fd);

0 commit comments

Comments
 (0)