@@ 10,12 10,15 @@ project('fakeyd', 'c', default_options : [
version = get_option('version')
build_tests = get_option('build-tests')
+varchunk = subproject('varchunk')
reuse = find_program('reuse', required : false)
add_project_arguments('-DFAKEYD_VERSION="'+version+'"', language : 'c')
add_project_arguments('-D_GNU_SOURCE', language : 'c')
+thread_dep = dependency('threads')
+varchunk_dep = varchunk.get_variable('varchunk')
input_dep = dependency('libinput', version : '>=1.6.0')
udev_dep = dependency('libudev')
evdev_dep = dependency('libevdev', version : '>=1.5.0')
@@ 24,7 27,7 @@ if not lua_dep.found()
lua_dep = dependency('lua5.4')
endif
-deps = [ input_dep, udev_dep, evdev_dep, lua_dep ]
+deps = [ thread_dep, varchunk_dep, input_dep, udev_dep, evdev_dep, lua_dep ]
srcs = [
join_paths('src', 'fakeyd.c'),
join_paths('src', 'event.c'),
@@ 13,6 13,8 @@
#include <string.h>
#include <syslog.h>
#include <ctype.h>
+#include <pthread.h>
+#include <semaphore.h>
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
@@ 20,6 22,8 @@
#include <fakeyd/fakeyd.h>
+#include <varchunk/varchunk.h>
+
#define INPUTS_MAX 32
typedef enum _out_type_t {
@@ 29,11 33,16 @@ typedef enum _out_type_t {
OUTPUTS_MAX // sentinel
} out_type_t;
+typedef struct _job_t job_t;
typedef struct _inp_t inp_t;
typedef struct _out_t out_t;
typedef struct _map_t map_t;
typedef struct _app_t app_t;
+struct _job_t {
+ struct libinput_event *ev;
+};
+
struct _inp_t {
char path [PATH_MAX];
};
@@ 49,6 58,9 @@ struct _app_t {
lua_State *L;
const char *cfg;
size_t num_inputs;
+ varchunk_t *rb;
+ sem_t sem;
+ pthread_t thread;
inp_t inputs [INPUTS_MAX];
out_t outputs [OUTPUTS_MAX];
};
@@ 64,7 76,7 @@ struct _map_t {
#define EVENT_MAP(ID) { .name = #ID, .code = LIBINPUT_EVENT_##ID }
#define TERMINATOR { .name = NULL, .code = 0 }
-static volatile sig_atomic_t done = false;
+static atomic_bool done = ATOMIC_VAR_INIT(false);
static const char *output_labels [OUTPUTS_MAX] = {
[OUT_TYPE_KEYBOARD] = "fakeyd keyboard",
@@ 467,10 479,16 @@ static const map_t event_maps [] = {
TERMINATOR
};
+static bool
+_notdone()
+{
+ return !atomic_load_explicit(&done, memory_order_relaxed);
+}
+
static void
_sigint(int sig __attribute__((unused)))
{
- done = true;
+ atomic_store_explicit(&done, true, memory_order_relaxed);
}
static int
@@ 720,8 738,119 @@ _lua_overwrite(lua_State *L)
}
static int
+_handle_event(app_t *app, struct libinput_event *ev)
+{
+ const enum libinput_event_type type = libinput_event_get_type(ev);
+
+ if(lua_getglobal(app->L, "intercept") != LUA_TFUNCTION)
+ {
+ syslog(LOG_ERR, "[%s] lua: %s", __func__, "'intercept' not a function");
+ lua_pop(app->L, 1); // intercept
+ libinput_event_destroy(ev);
+ return 1;
+ }
+
+ switch(type)
+ {
+ case LIBINPUT_EVENT_DEVICE_ADDED:
+ // fall-through
+ case LIBINPUT_EVENT_DEVICE_REMOVED:
+ {
+ struct libinput_event_device_notify *evn =
+ libinput_event_get_device_notify_event(ev);
+
+ event_device_notify_new(app->L, evn);
+ } break;
+
+ case LIBINPUT_EVENT_KEYBOARD_KEY:
+ {
+ struct libinput_event_keyboard *evk =
+ libinput_event_get_keyboard_event(ev);
+
+ event_keyboard_new(app->L, evk);
+ } break;
+
+ case LIBINPUT_EVENT_POINTER_MOTION:
+ // fall-through
+ case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
+ // fall-through
+ case LIBINPUT_EVENT_POINTER_BUTTON:
+ // fall-through
+ case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
+ // fall-through
+ case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
+ // fall-through
+ case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
+ {
+ struct libinput_event_pointer *evk =
+ libinput_event_get_pointer_event(ev);
+
+ event_pointer_new(app->L, evk);
+ } break;
+
+ case LIBINPUT_EVENT_POINTER_AXIS:
+ // fall-through
+ default:
+ lua_pop(app->L, 1); // intercept
+ libinput_event_destroy(ev);
+ return 1;
+ }
+
+ if(lua_pcall(app->L, 1, 0, 0) != LUA_OK)
+ {
+ syslog(LOG_ERR, "[%s] lua: %s", __func__, lua_tostring(app->L, -1));
+ lua_pop(app->L, 1);
+ }
+
+ return 0;
+}
+
+static void *
+_thread(void *data)
+{
+ app_t *app = data;
+
+ while(_notdone())
+ {
+ if(sem_wait(&app->sem) != 0)
+ {
+ syslog(LOG_ERR, "[%s] sem_wait: %s", __func__, strerror(errno));
+ _sigint(SIGINT);
+ continue;
+ }
+
+ const job_t *job;
+ size_t len;
+
+ while( (job = varchunk_read_request(app->rb, &len)) )
+ {
+ _handle_event(app, job->ev);
+
+ varchunk_read_advance(app->rb);
+ }
+ }
+
+ return NULL;
+}
+
+static int
_app_init(app_t *app)
{
+ int err;
+
+ if(sem_init(&app->sem, 0, 0) != 0)
+ {
+ syslog(LOG_ERR, "[%s] sem_init: %s", __func__, strerror(errno));
+ return 1;
+ }
+
+ app->rb = varchunk_new(2048, true);
+ if(!app->rb)
+ {
+ syslog(LOG_ERR, "[%s] varchunk_new", __func__);
+ return 1;
+ }
+
app->udev = udev_new();
if(!app->udev)
{
@@ 891,6 1020,12 @@ _app_init(app_t *app)
lua_pop(app->L, 1);
return 1;
}
+
+ if ( (err = pthread_create(&app->thread, NULL, _thread, app)) != 0)
+ {
+ syslog(LOG_ERR, "[%s] pthread_create: %s", __func__, strerror(err));
+ return 1;
+ }
return 0;
}
@@ 898,6 1033,14 @@ _app_init(app_t *app)
static void
_app_deinit(app_t *app)
{
+ _sigint(SIGINT);
+ sem_post(&app->sem);
+
+ if(app->thread)
+ {
+ pthread_join(app->thread, NULL);
+ }
+
if(app->L)
{
lua_close(app->L);
@@ 931,14 1074,33 @@ _app_deinit(app_t *app)
udev_unref(app->udev);
app->udev = NULL;
}
+
+ if(app->rb)
+ {
+ const job_t *job;
+ size_t len;
+
+ // drain ringbuffer
+ while( (job = varchunk_read_request(app->rb, &len) ) )
+ {
+ libinput_event_destroy(job->ev);
+
+ varchunk_read_advance(app->rb);
+ }
+
+ varchunk_free(app->rb);
+ app-> rb = NULL;
+ }
+
+ sem_destroy(&app->sem);
}
static void
_app_run(app_t *app)
{
- const int timeout = 300;
+ const int timeout = 500; // ms
- while(!done)
+ while(_notdone())
{
struct pollfd fds = {
.fd = libinput_get_fd(app->li),
@@ 962,66 1124,14 @@ _app_run(app_t *app)
struct libinput_event *ev;
while ( (ev = libinput_get_event(app->li)) )
{
- const enum libinput_event_type type = libinput_event_get_type(ev);
+ job_t *job;
- if(lua_getglobal(app->L, "intercept") != LUA_TFUNCTION)
+ if( (job = varchunk_write_request(app->rb, sizeof(job_t))) )
{
- syslog(LOG_ERR, "[%s] lua: %s", __func__, "'intercept' not a function");
- lua_pop(app->L, 1); // intercept
- libinput_event_destroy(ev);
- continue;
- }
-
- switch(type)
- {
- case LIBINPUT_EVENT_DEVICE_ADDED:
- // fall-through
- case LIBINPUT_EVENT_DEVICE_REMOVED:
- {
- struct libinput_event_device_notify *evn =
- libinput_event_get_device_notify_event(ev);
-
- event_device_notify_new(app->L, evn);
- } break;
+ job->ev = ev;
- case LIBINPUT_EVENT_KEYBOARD_KEY:
- {
- struct libinput_event_keyboard *evk =
- libinput_event_get_keyboard_event(ev);
-
- event_keyboard_new(app->L, evk);
- } break;
-
- case LIBINPUT_EVENT_POINTER_MOTION:
- // fall-through
- case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
- // fall-through
- case LIBINPUT_EVENT_POINTER_BUTTON:
- // fall-through
- case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
- // fall-through
- case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
- // fall-through
- case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
- {
- struct libinput_event_pointer *evk =
- libinput_event_get_pointer_event(ev);
-
- event_pointer_new(app->L, evk);
- } break;
-
- case LIBINPUT_EVENT_POINTER_AXIS:
- // fall-through
- default:
- lua_pop(app->L, 1); // intercept
- libinput_event_destroy(ev);
- continue;
- }
-
- if(lua_pcall(app->L, 1, 0, 0) != LUA_OK)
- {
- syslog(LOG_ERR, "[%s] lua: %s", __func__, lua_tostring(app->L, -1));
- lua_pop(app->L, 1);
+ varchunk_write_advance(app->rb, sizeof(job_t));
+ sem_post(&app->sem);
}
}
}