mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
input: use a timer fd to handle keyboard key repeat
Instead of running a repeater thread that writes the key to repeat over a pipe, use a simple timer fd. No more locking or condition signalling. No need to track start/stop/exist states. We simply set up the initial timeout value to be the 'delay', and the interval to be the repeat 'rate'.
This commit is contained in:
parent
c62ce72778
commit
a82f12dd2b
3 changed files with 65 additions and 156 deletions
78
input.c
78
input.c
|
|
@ -7,6 +7,7 @@
|
|||
#include <locale.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
|
|
@ -65,49 +66,58 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|||
term_focus_in(term);
|
||||
}
|
||||
|
||||
static bool
|
||||
start_repeater(struct terminal *term, uint32_t key)
|
||||
{
|
||||
if (term->kbd.repeat.dont_re_repeat)
|
||||
return true;
|
||||
|
||||
struct itimerspec t = {
|
||||
.it_value = {.tv_sec = 0, .tv_nsec = term->kbd.repeat.delay * 1000000},
|
||||
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / term->kbd.repeat.rate},
|
||||
};
|
||||
|
||||
if (t.it_value.tv_nsec >= 1000000000) {
|
||||
t.it_value.tv_sec += t.it_value.tv_nsec / 1000000000;
|
||||
t.it_value.tv_nsec %= 1000000000;
|
||||
}
|
||||
if (t.it_interval.tv_nsec >= 1000000000) {
|
||||
t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000;
|
||||
t.it_interval.tv_nsec %= 1000000000;
|
||||
}
|
||||
if (timerfd_settime(term->kbd.repeat.fd, 0, &t, NULL) < 0) {
|
||||
LOG_ERRNO("failed to arm keyboard repeat timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
term->kbd.repeat.key = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
stop_repeater(struct terminal *term, uint32_t key)
|
||||
{
|
||||
if (key != -1 && key != term->kbd.repeat.key)
|
||||
return true;
|
||||
|
||||
if (timerfd_settime(term->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) {
|
||||
LOG_ERRNO("failed to disarm keyboard repeat timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||
struct wl_surface *surface)
|
||||
{
|
||||
struct terminal *term = data;
|
||||
|
||||
mtx_lock(&term->kbd.repeat.mutex);
|
||||
if (term->kbd.repeat.cmd != REPEAT_EXIT) {
|
||||
term->kbd.repeat.cmd = REPEAT_STOP;
|
||||
cnd_signal(&term->kbd.repeat.cond);
|
||||
}
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
|
||||
stop_repeater(term, -1);
|
||||
term_focus_out(term);
|
||||
}
|
||||
|
||||
static void
|
||||
start_repeater(struct terminal *term, uint32_t key)
|
||||
{
|
||||
mtx_lock(&term->kbd.repeat.mutex);
|
||||
if (!term->kbd.repeat.dont_re_repeat) {
|
||||
if (term->kbd.repeat.cmd != REPEAT_EXIT) {
|
||||
term->kbd.repeat.cmd = REPEAT_START;
|
||||
term->kbd.repeat.key = key;
|
||||
cnd_signal(&term->kbd.repeat.cond);
|
||||
}
|
||||
}
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_repeater(struct terminal *term, uint32_t key)
|
||||
{
|
||||
mtx_lock(&term->kbd.repeat.mutex);
|
||||
if (term->kbd.repeat.key == key) {
|
||||
if (term->kbd.repeat.cmd != REPEAT_EXIT) {
|
||||
term->kbd.repeat.cmd = REPEAT_STOP;
|
||||
cnd_signal(&term->kbd.repeat.cond);
|
||||
}
|
||||
}
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||
uint32_t time, uint32_t key, uint32_t state)
|
||||
|
|
|
|||
136
main.c
136
main.c
|
|
@ -192,83 +192,6 @@ static const struct wl_registry_listener registry_listener = {
|
|||
.global_remove = &handle_global_remove,
|
||||
};
|
||||
|
||||
static int
|
||||
keyboard_repeater(void *arg)
|
||||
{
|
||||
struct terminal *term = arg;
|
||||
|
||||
char proc_title[16];
|
||||
snprintf(proc_title, sizeof(proc_title), "foot:kbd-repeat");
|
||||
|
||||
if (prctl(PR_SET_NAME, proc_title, 0, 0, 0) < 0)
|
||||
LOG_ERRNO("kbd repeat: failed to set process title");
|
||||
|
||||
while (true) {
|
||||
LOG_DBG("repeater: waiting for start");
|
||||
|
||||
mtx_lock(&term->kbd.repeat.mutex);
|
||||
while (term->kbd.repeat.cmd == REPEAT_STOP)
|
||||
cnd_wait(&term->kbd.repeat.cond, &term->kbd.repeat.mutex);
|
||||
|
||||
if (term->kbd.repeat.cmd == REPEAT_EXIT) {
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
restart:
|
||||
|
||||
LOG_DBG("repeater: started");
|
||||
assert(term->kbd.repeat.cmd == REPEAT_START);
|
||||
|
||||
const long rate_delay = 1000000000 / term->kbd.repeat.rate;
|
||||
long delay = term->kbd.repeat.delay * 1000000;
|
||||
|
||||
while (true) {
|
||||
assert(term->kbd.repeat.rate > 0);
|
||||
|
||||
struct timespec timeout;
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
|
||||
timeout.tv_nsec += delay;
|
||||
if (timeout.tv_nsec >= 1000000000) {
|
||||
timeout.tv_sec += timeout.tv_nsec / 1000000000;
|
||||
timeout.tv_nsec %= 1000000000;
|
||||
}
|
||||
|
||||
int ret = cnd_timedwait(&term->kbd.repeat.cond, &term->kbd.repeat.mutex, &timeout);
|
||||
if (ret == thrd_success) {
|
||||
if (term->kbd.repeat.cmd == REPEAT_START)
|
||||
goto restart;
|
||||
else if (term->kbd.repeat.cmd == REPEAT_STOP) {
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
break;
|
||||
} else if (term->kbd.repeat.cmd == REPEAT_EXIT) {
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
assert(ret == thrd_timedout);
|
||||
assert(term->kbd.repeat.cmd == REPEAT_START);
|
||||
LOG_DBG("repeater: repeat: %u", term->kbd.repeat.key);
|
||||
|
||||
if (write(term->kbd.repeat.pipe_write_fd, &term->kbd.repeat.key,
|
||||
sizeof(term->kbd.repeat.key)) != sizeof(term->kbd.repeat.key))
|
||||
{
|
||||
LOG_ERRNO("faile to write repeat key to repeat pipe");
|
||||
mtx_unlock(&term->kbd.repeat.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
delay = rate_delay;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *const *argv)
|
||||
{
|
||||
|
|
@ -319,12 +242,6 @@ main(int argc, char *const *argv)
|
|||
setlocale(LC_ALL, "");
|
||||
setenv("TERM", conf.term, 1);
|
||||
|
||||
int repeat_pipe_fds[2] = {-1, -1};
|
||||
if (pipe2(repeat_pipe_fds, O_CLOEXEC) == -1) {
|
||||
LOG_ERRNO("failed to create pipe for repeater thread");
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct terminal term = {
|
||||
.quit = false,
|
||||
.ptmx = posix_openpt(O_RDWR | O_NOCTTY),
|
||||
|
|
@ -347,9 +264,7 @@ main(int argc, char *const *argv)
|
|||
},
|
||||
.kbd = {
|
||||
.repeat = {
|
||||
.pipe_read_fd = repeat_pipe_fds[0],
|
||||
.pipe_write_fd = repeat_pipe_fds[1],
|
||||
.cmd = REPEAT_STOP,
|
||||
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC),
|
||||
},
|
||||
},
|
||||
.colors = {
|
||||
|
|
@ -415,15 +330,14 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
if (term.ptmx == -1) {
|
||||
LOG_ERRNO("failed to open pseudo terminal");
|
||||
LOG_ERR("failed to open pseudo terminal");
|
||||
goto out;
|
||||
}
|
||||
|
||||
mtx_init(&term.kbd.repeat.mutex, mtx_plain);
|
||||
cnd_init(&term.kbd.repeat.cond);
|
||||
|
||||
thrd_t keyboard_repeater_id;
|
||||
thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term);
|
||||
if (term.flash.fd == -1 || term.blink.fd == -1 || term.kbd.repeat.fd == -1) {
|
||||
LOG_ERR("failed to create timers");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sem_init(&term.render.workers.start, 0, 0);
|
||||
sem_init(&term.render.workers.done, 0, 0);
|
||||
|
|
@ -706,7 +620,7 @@ main(int argc, char *const *argv)
|
|||
struct pollfd fds[] = {
|
||||
{.fd = wl_display_get_fd(term.wl.display), .events = POLLIN},
|
||||
{.fd = term.ptmx, .events = POLLIN},
|
||||
{.fd = term.kbd.repeat.pipe_read_fd, .events = POLLIN},
|
||||
{.fd = term.kbd.repeat.fd, .events = POLLIN},
|
||||
{.fd = term.flash.fd, .events = POLLIN},
|
||||
{.fd = term.blink.fd, .events = POLLIN},
|
||||
};
|
||||
|
|
@ -789,20 +703,20 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
if (fds[2].revents & POLLIN) {
|
||||
uint32_t key;
|
||||
if (read(term.kbd.repeat.pipe_read_fd, &key, sizeof(key)) != sizeof(key)) {
|
||||
LOG_ERRNO("failed to read repeat key from repeat pipe");
|
||||
break;
|
||||
uint64_t expiration_count;
|
||||
ssize_t ret = read(
|
||||
term.kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
||||
|
||||
if (ret < 0)
|
||||
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
||||
else {
|
||||
term.kbd.repeat.dont_re_repeat = true;
|
||||
for (size_t i = 0; i < expiration_count; i++)
|
||||
input_repeat(&term, term.kbd.repeat.key);
|
||||
term.kbd.repeat.dont_re_repeat = false;
|
||||
}
|
||||
|
||||
term.kbd.repeat.dont_re_repeat = true;
|
||||
input_repeat(&term, key);
|
||||
term.kbd.repeat.dont_re_repeat = false;
|
||||
}
|
||||
|
||||
if (fds[2].revents & POLLHUP)
|
||||
LOG_ERR("keyboard repeat handling thread died");
|
||||
|
||||
if (fds[3].revents & POLLIN) {
|
||||
uint64_t expiration_count;
|
||||
ssize_t ret = read(
|
||||
|
|
@ -852,11 +766,6 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
out:
|
||||
mtx_lock(&term.kbd.repeat.mutex);
|
||||
term.kbd.repeat.cmd = REPEAT_EXIT;
|
||||
cnd_signal(&term.kbd.repeat.cond);
|
||||
mtx_unlock(&term.kbd.repeat.mutex);
|
||||
|
||||
mtx_lock(&term.render.workers.lock);
|
||||
assert(tll_length(term.render.workers.queue) == 0);
|
||||
for (size_t i = 0; i < term.render.workers.count; i++) {
|
||||
|
|
@ -942,14 +851,12 @@ out:
|
|||
close(term.flash.fd);
|
||||
if (term.blink.fd != -1)
|
||||
close(term.blink.fd);
|
||||
if (term.kbd.repeat.fd != -1)
|
||||
close(term.kbd.repeat.fd);
|
||||
|
||||
if (term.ptmx != -1)
|
||||
close(term.ptmx);
|
||||
|
||||
thrd_join(keyboard_repeater_id, NULL);
|
||||
cnd_destroy(&term.kbd.repeat.cond);
|
||||
mtx_destroy(&term.kbd.repeat.mutex);
|
||||
|
||||
for (size_t i = 0; i < term.render.workers.count; i++)
|
||||
thrd_join(term.render.workers.threads[i], NULL);
|
||||
free(term.render.workers.threads);
|
||||
|
|
@ -960,9 +867,6 @@ out:
|
|||
assert(tll_length(term.render.workers.queue) == 0);
|
||||
tll_free(term.render.workers.queue);
|
||||
|
||||
close(term.kbd.repeat.pipe_read_fd);
|
||||
close(term.kbd.repeat.pipe_write_fd);
|
||||
|
||||
config_free(conf);
|
||||
|
||||
cairo_debug_reset_static_data();
|
||||
|
|
|
|||
|
|
@ -156,12 +156,7 @@ struct kbd {
|
|||
struct xkb_compose_table *xkb_compose_table;
|
||||
struct xkb_compose_state *xkb_compose_state;
|
||||
struct {
|
||||
mtx_t mutex;
|
||||
cnd_t cond;
|
||||
int trigger;
|
||||
int pipe_read_fd;
|
||||
int pipe_write_fd;
|
||||
enum {REPEAT_STOP, REPEAT_START, REPEAT_EXIT} cmd;
|
||||
int fd;
|
||||
|
||||
bool dont_re_repeat;
|
||||
int32_t delay;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue