mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-23 16:58:09 +02:00
Implement timers for the linux event loops
Needed for dbus integration. Also cleanup the event handling code. X11 and Wayland now share most of their event polling and dispatch logic.
This commit is contained in:
150
glfw/backend_utils.c
vendored
150
glfw/backend_utils.c
vendored
@@ -8,8 +8,10 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "backend_utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#define ppoll pollts
|
||||
@@ -42,24 +44,25 @@ addWatch(EventLoopData *eld, int fd, int events, int enabled, watch_callback_fun
|
||||
return w->id;
|
||||
}
|
||||
|
||||
#define removeX(which, item_id, update_func) {\
|
||||
for (nfds_t i = 0; i < eld->which##_count; i++) { \
|
||||
if (eld->which[i].id == item_id) { \
|
||||
eld->which##_count--; \
|
||||
if (i < eld->which##_count) { \
|
||||
memmove(eld->which + i, eld->which + i + 1, sizeof(eld->which[0]) * (eld->which##_count - i)); \
|
||||
} \
|
||||
update_func(eld); break; \
|
||||
}}}
|
||||
|
||||
void
|
||||
removeWatch(EventLoopData *eld, id_type watch_id) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
||||
if (eld->watches[i].id == watch_id) {
|
||||
eld->watches_count--;
|
||||
if (i < eld->watches_count) {
|
||||
memmove(eld->watches + i, eld->watches + i + 1, sizeof(eld->watches[0]) * (eld->watches_count - i));
|
||||
}
|
||||
update_fds(eld);
|
||||
break;
|
||||
}
|
||||
}
|
||||
removeX(watches, watch_id, update_fds);
|
||||
}
|
||||
|
||||
void
|
||||
toggleWatch(EventLoopData *eld, id_type watch_id, int enabled) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
||||
if (eld->watches[i].fd == watch_id) {
|
||||
if (eld->watches[i].id == watch_id) {
|
||||
if (eld->watches[i].enabled != enabled) {
|
||||
eld->watches[i].enabled = enabled;
|
||||
update_fds(eld);
|
||||
@@ -69,9 +72,72 @@ toggleWatch(EventLoopData *eld, id_type watch_id, int enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
static id_type timer_counter = 0;
|
||||
extern double glfwGetTime(void);
|
||||
|
||||
static int
|
||||
compare_timers(const void *a_, const void *b_) {
|
||||
const Timer *a = (const Timer*)a_, *b = (const Timer*)b_;
|
||||
return (a->trigger_at > b->trigger_at) ? 1 : (a->trigger_at < b->trigger_at) ? -1 : 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_timers(EventLoopData *eld) {
|
||||
if (eld->timers_count > 1) qsort(eld->timers, eld->timers_count, sizeof(eld->timers[0]), compare_timers);
|
||||
}
|
||||
|
||||
id_type
|
||||
addTimer(EventLoopData *eld, double interval, int enabled, timer_callback_func cb, void *cb_data) {
|
||||
if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) return 0;
|
||||
Timer *t = eld->timers + eld->timers_count++;
|
||||
t->interval = interval;
|
||||
t->trigger_at = enabled ? glfwGetTime() + interval : DBL_MAX;
|
||||
t->callback = cb;
|
||||
t->callback_data = cb_data;
|
||||
t->id = ++timer_counter;
|
||||
update_timers(eld);
|
||||
return t->id;
|
||||
}
|
||||
|
||||
void
|
||||
prepareForPoll(EventLoopData *eld) {
|
||||
removeTimer(EventLoopData *eld, id_type timer_id) {
|
||||
removeX(timers, timer_id, update_timers);
|
||||
}
|
||||
|
||||
void
|
||||
toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
double trigger_at = enabled ? (glfwGetTime() + eld->timers[i].interval) : DBL_MAX;
|
||||
if (trigger_at != eld->timers[i].trigger_at) {
|
||||
eld->timers[i].trigger_at = trigger_at;
|
||||
update_timers(eld);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
eld->timers[i].interval = interval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
prepareForPoll(EventLoopData *eld, double timeout) {
|
||||
for (nfds_t i = 0; i < eld->fds_count; i++) eld->fds[i].revents = 0;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return timeout;
|
||||
double now = glfwGetTime(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (timeout < 0 || now + timeout > next_repeat_at) {
|
||||
timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -82,7 +148,7 @@ pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) {
|
||||
return ppoll(fds, nfds, &tv, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
dispatchEvents(EventLoopData *eld) {
|
||||
for (unsigned w = 0, f = 0; f < eld->fds_count; f++) {
|
||||
while(eld->watches[w].fd != eld->fds[f].fd) w++;
|
||||
@@ -94,6 +160,29 @@ dispatchEvents(EventLoopData *eld) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
dispatchTimers(EventLoopData *eld) {
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return 0;
|
||||
static struct { timer_callback_func func; id_type id; void* data; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
|
||||
unsigned num_dispatches = 0;
|
||||
double now = glfwGetTime();
|
||||
for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at < DBL_MAX; i++) {
|
||||
if (eld->timers[i].trigger_at <= now) {
|
||||
eld->timers[i].trigger_at = now + eld->timers[i].interval;
|
||||
dispatches[num_dispatches].func = eld->timers[i].callback;
|
||||
dispatches[num_dispatches].id = eld->timers[i].id;
|
||||
dispatches[num_dispatches].data = eld->timers[i].callback_data;
|
||||
num_dispatches++;
|
||||
}
|
||||
}
|
||||
// we dispatch separately so that the callbacks can modify timers
|
||||
for (unsigned i = 0; i < num_dispatches; i++) {
|
||||
dispatches[i].func(dispatches[i].id, dispatches[i].data);
|
||||
}
|
||||
if (num_dispatches) update_timers(eld);
|
||||
return num_dispatches;
|
||||
}
|
||||
|
||||
static void
|
||||
drain_wakeup_fd(int fd, int events, void* data) {
|
||||
static char drain_buf[64];
|
||||
@@ -106,6 +195,41 @@ initPollData(EventLoopData *eld, int wakeup_fd, int display_fd) {
|
||||
addWatch(eld, wakeup_fd, POLLIN, 1, drain_wakeup_fd, NULL);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pollForEvents(EventLoopData *eld, double timeout) {
|
||||
int read_ok = 0;
|
||||
timeout = prepareForPoll(eld, timeout);
|
||||
int result;
|
||||
double end_time = glfwGetTime() + timeout;
|
||||
|
||||
while(1) {
|
||||
if (timeout >= 0) {
|
||||
result = pollWithTimeout(eld->fds, eld->fds_count, timeout);
|
||||
dispatchTimers(eld);
|
||||
if (result > 0) {
|
||||
dispatchEvents(eld);
|
||||
read_ok = eld->watches[0].ready;
|
||||
break;
|
||||
}
|
||||
timeout = end_time - glfwGetTime();
|
||||
if (timeout <= 0) break;
|
||||
if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
|
||||
break;
|
||||
} else {
|
||||
result = poll(eld->fds, eld->fds_count, -1);
|
||||
dispatchTimers(eld);
|
||||
if (result > 0) {
|
||||
dispatchEvents(eld);
|
||||
read_ok = eld->watches[0].ready;
|
||||
}
|
||||
if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return read_ok;
|
||||
}
|
||||
|
||||
void
|
||||
closeFds(int *fds, size_t count) {
|
||||
while(count--) {
|
||||
|
||||
Reference in New Issue
Block a user