2018-07-09 10:58:51 +03:00
|
|
|
|
/*
|
|
|
|
|
* backend_utils.c
|
|
|
|
|
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
|
*
|
|
|
|
|
* Distributed under terms of the GPL3 license.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
#include "backend_utils.h"
|
2018-07-11 08:33:52 +03:00
|
|
|
|
#include "internal.h"
|
2021-03-25 13:34:12 +03:00
|
|
|
|
#include "memfd.h"
|
2018-07-09 10:58:51 +03:00
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
#include <stdlib.h>
|
2018-07-09 10:58:51 +03:00
|
|
|
|
#include <string.h>
|
2019-07-05 07:04:51 +03:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <fcntl.h>
|
2018-07-09 10:58:51 +03:00
|
|
|
|
#include <errno.h>
|
2018-07-09 17:43:05 +03:00
|
|
|
|
#include <float.h>
|
2018-07-09 18:30:12 +03:00
|
|
|
|
#include <time.h>
|
2018-07-10 06:33:52 +03:00
|
|
|
|
#include <stdio.h>
|
2018-07-09 10:58:51 +03:00
|
|
|
|
|
|
|
|
|
#ifdef __NetBSD__
|
|
|
|
|
#define ppoll pollts
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
update_fds(EventLoopData *eld) {
|
|
|
|
|
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
|
|
|
|
Watch *w = eld->watches + i;
|
2018-07-10 06:33:52 +03:00
|
|
|
|
eld->fds[i].fd = w->fd;
|
|
|
|
|
eld->fds[i].events = w->enabled ? w->events : 0;
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 12:55:19 +03:00
|
|
|
|
static id_type watch_counter = 0;
|
|
|
|
|
|
|
|
|
|
id_type
|
2018-07-10 06:48:11 +03:00
|
|
|
|
addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data) {
|
2018-07-11 08:33:52 +03:00
|
|
|
|
if (eld->watches_count >= sizeof(eld->watches)/sizeof(eld->watches[0])) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many watches added");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-07-09 10:58:51 +03:00
|
|
|
|
Watch *w = eld->watches + eld->watches_count++;
|
2018-07-10 06:48:11 +03:00
|
|
|
|
w->name = name;
|
2018-07-09 10:58:51 +03:00
|
|
|
|
w->fd = fd; w->events = events; w->enabled = enabled;
|
|
|
|
|
w->callback = cb;
|
|
|
|
|
w->callback_data = cb_data;
|
2019-02-25 11:28:45 +03:00
|
|
|
|
w->free = NULL;
|
2018-07-09 12:55:19 +03:00
|
|
|
|
w->id = ++watch_counter;
|
2018-07-09 10:58:51 +03:00
|
|
|
|
update_fds(eld);
|
2018-07-09 12:55:19 +03:00
|
|
|
|
return w->id;
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
#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--; \
|
2019-02-28 11:29:49 +03:00
|
|
|
|
if (eld->which[i].callback_data && eld->which[i].free) { \
|
|
|
|
|
eld->which[i].free(eld->which[i].id, eld->which[i].callback_data); \
|
|
|
|
|
eld->which[i].callback_data = NULL; eld->which[i].free = NULL; \
|
|
|
|
|
} \
|
2018-07-09 17:43:05 +03:00
|
|
|
|
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; \
|
|
|
|
|
}}}
|
|
|
|
|
|
2018-07-09 10:58:51 +03:00
|
|
|
|
void
|
2018-07-09 12:55:19 +03:00
|
|
|
|
removeWatch(EventLoopData *eld, id_type watch_id) {
|
2018-07-09 17:43:05 +03:00
|
|
|
|
removeX(watches, watch_id, update_fds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
toggleWatch(EventLoopData *eld, id_type watch_id, int enabled) {
|
2018-07-09 10:58:51 +03:00
|
|
|
|
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
2018-07-09 12:55:19 +03:00
|
|
|
|
if (eld->watches[i].id == watch_id) {
|
2018-07-09 17:43:05 +03:00
|
|
|
|
if (eld->watches[i].enabled != enabled) {
|
|
|
|
|
eld->watches[i].enabled = enabled;
|
|
|
|
|
update_fds(eld);
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
static id_type timer_counter = 0;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-03 06:41:17 +03:00
|
|
|
|
static void
|
2018-07-09 17:43:05 +03:00
|
|
|
|
update_timers(EventLoopData *eld) {
|
|
|
|
|
if (eld->timers_count > 1) qsort(eld->timers, eld->timers_count, sizeof(eld->timers[0]), compare_timers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
id_type
|
2019-08-02 19:49:02 +03:00
|
|
|
|
addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
|
2018-07-11 08:33:52 +03:00
|
|
|
|
if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {
|
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-07-09 17:43:05 +03:00
|
|
|
|
Timer *t = eld->timers + eld->timers_count++;
|
|
|
|
|
t->interval = interval;
|
2018-07-10 06:48:11 +03:00
|
|
|
|
t->name = name;
|
2019-08-02 19:49:02 +03:00
|
|
|
|
t->trigger_at = enabled ? monotonic() + interval : MONOTONIC_T_MAX;
|
2019-02-25 11:28:45 +03:00
|
|
|
|
t->repeats = repeats;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
t->callback = cb;
|
|
|
|
|
t->callback_data = cb_data;
|
2019-02-25 11:28:45 +03:00
|
|
|
|
t->free = free;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
t->id = ++timer_counter;
|
|
|
|
|
update_timers(eld);
|
2019-02-27 03:47:20 +03:00
|
|
|
|
return timer_counter;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 10:58:51 +03:00
|
|
|
|
void
|
2018-07-09 17:43:05 +03:00
|
|
|
|
removeTimer(EventLoopData *eld, id_type timer_id) {
|
|
|
|
|
removeX(timers, timer_id, update_timers);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-25 11:28:45 +03:00
|
|
|
|
void
|
|
|
|
|
removeAllTimers(EventLoopData *eld) {
|
|
|
|
|
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
|
|
|
|
if (eld->timers[i].free && eld->timers[i].callback_data) eld->timers[i].free(eld->timers[i].id, eld->timers[i].callback_data);
|
|
|
|
|
}
|
|
|
|
|
eld->timers_count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
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) {
|
2019-08-02 19:49:02 +03:00
|
|
|
|
monotonic_t trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : MONOTONIC_T_MAX;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
if (trigger_at != eld->timers[i].trigger_at) {
|
|
|
|
|
eld->timers[i].trigger_at = trigger_at;
|
|
|
|
|
update_timers(eld);
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-08-02 19:49:02 +03:00
|
|
|
|
changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval) {
|
2018-07-09 17:43:05 +03:00
|
|
|
|
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
|
|
|
|
if (eld->timers[i].id == timer_id) {
|
|
|
|
|
eld->timers[i].interval = interval;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-08-02 19:49:02 +03:00
|
|
|
|
monotonic_t
|
|
|
|
|
prepareForPoll(EventLoopData *eld, monotonic_t timeout) {
|
2018-07-10 06:33:52 +03:00
|
|
|
|
for (nfds_t i = 0; i < eld->watches_count; i++) eld->fds[i].revents = 0;
|
2019-08-02 19:49:02 +03:00
|
|
|
|
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return timeout;
|
|
|
|
|
monotonic_t now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
if (timeout < 0 || now + timeout > next_repeat_at) {
|
|
|
|
|
timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
|
|
|
|
|
}
|
|
|
|
|
return timeout;
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-03 06:41:17 +03:00
|
|
|
|
static struct timespec
|
2019-12-19 14:13:51 +03:00
|
|
|
|
calc_time(monotonic_t nsec) {
|
|
|
|
|
struct timespec result;
|
|
|
|
|
result.tv_sec = nsec / (1000LL * 1000LL * 1000LL);
|
|
|
|
|
result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 10:58:51 +03:00
|
|
|
|
int
|
2019-08-02 19:49:02 +03:00
|
|
|
|
pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout) {
|
|
|
|
|
struct timespec tv = calc_time(timeout);
|
2018-07-09 10:58:51 +03:00
|
|
|
|
return ppoll(fds, nfds, &tv, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
static void
|
2018-07-09 10:58:51 +03:00
|
|
|
|
dispatchEvents(EventLoopData *eld) {
|
2018-07-10 06:33:52 +03:00
|
|
|
|
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
|
|
|
|
Watch *ww = eld->watches + i;
|
|
|
|
|
struct pollfd *pfd = eld->fds + i;
|
|
|
|
|
if (pfd->revents & ww->events) {
|
2018-07-09 10:58:51 +03:00
|
|
|
|
ww->ready = 1;
|
2018-07-10 06:33:52 +03:00
|
|
|
|
if (ww->callback) ww->callback(ww->fd, pfd->revents, ww->callback_data);
|
2018-07-09 10:58:51 +03:00
|
|
|
|
} else ww->ready = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 17:43:05 +03:00
|
|
|
|
unsigned
|
|
|
|
|
dispatchTimers(EventLoopData *eld) {
|
2019-08-02 19:49:02 +03:00
|
|
|
|
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return 0;
|
2019-02-25 11:28:45 +03:00
|
|
|
|
static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
|
2018-07-09 17:43:05 +03:00
|
|
|
|
unsigned num_dispatches = 0;
|
2019-08-02 19:49:02 +03:00
|
|
|
|
monotonic_t now = monotonic();
|
2018-07-09 18:44:54 +03:00
|
|
|
|
for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) {
|
|
|
|
|
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;
|
2019-02-25 11:28:45 +03:00
|
|
|
|
dispatches[num_dispatches].repeats = eld->timers[i].repeats;
|
2018-07-09 18:44:54 +03:00
|
|
|
|
num_dispatches++;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
}
|
|
|
|
|
// 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);
|
2019-02-25 11:28:45 +03:00
|
|
|
|
if (!dispatches[i].repeats) {
|
|
|
|
|
removeTimer(eld, dispatches[i].id);
|
|
|
|
|
}
|
2018-07-09 17:43:05 +03:00
|
|
|
|
}
|
|
|
|
|
if (num_dispatches) update_timers(eld);
|
|
|
|
|
return num_dispatches;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 10:58:51 +03:00
|
|
|
|
static void
|
2019-07-18 11:55:11 +03:00
|
|
|
|
drain_wakeup_fd(int fd, EventLoopData* eld) {
|
2018-07-09 10:58:51 +03:00
|
|
|
|
static char drain_buf[64];
|
2019-07-18 11:55:11 +03:00
|
|
|
|
eld->wakeup_data_read = false;
|
2019-07-09 16:17:21 +03:00
|
|
|
|
while(true) {
|
|
|
|
|
ssize_t ret = read(fd, drain_buf, sizeof(drain_buf));
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-07-18 11:55:11 +03:00
|
|
|
|
if (ret > 0) { eld->wakeup_data_read = true; continue; }
|
2019-07-09 16:17:21 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-18 11:55:11 +03:00
|
|
|
|
static void
|
|
|
|
|
mark_wakep_fd_ready(int fd UNUSED, int events UNUSED, void *data) {
|
|
|
|
|
((EventLoopData*)(data))->wakeup_fd_ready = true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 07:04:51 +03:00
|
|
|
|
bool
|
|
|
|
|
initPollData(EventLoopData *eld, int display_fd) {
|
2019-07-05 07:30:39 +03:00
|
|
|
|
if (!addWatch(eld, "display", display_fd, POLLIN, 1, NULL, NULL)) return false;
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#ifdef HAS_EVENT_FD
|
2019-07-05 07:38:15 +03:00
|
|
|
|
eld->wakeupFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
2019-07-05 07:21:52 +03:00
|
|
|
|
if (eld->wakeupFd == -1) return false;
|
2019-07-05 07:30:39 +03:00
|
|
|
|
const int wakeup_fd = eld->wakeupFd;
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#else
|
|
|
|
|
if (pipe2(eld->wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0) return false;
|
2019-07-05 07:30:39 +03:00
|
|
|
|
const int wakeup_fd = eld->wakeupFds[0];
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#endif
|
2019-07-18 11:55:11 +03:00
|
|
|
|
if (!addWatch(eld, "wakeup", wakeup_fd, POLLIN, 1, mark_wakep_fd_ready, eld)) return false;
|
2019-07-05 07:04:51 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-18 11:55:11 +03:00
|
|
|
|
void
|
|
|
|
|
check_for_wakeup_events(EventLoopData *eld) {
|
|
|
|
|
#ifdef HAS_EVENT_FD
|
|
|
|
|
int fd = eld->wakeupFd;
|
|
|
|
|
#else
|
|
|
|
|
int fd = eld->wakeupFds[0];
|
|
|
|
|
#endif
|
|
|
|
|
drain_wakeup_fd(fd, eld);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 07:04:51 +03:00
|
|
|
|
void
|
|
|
|
|
wakeupEventLoop(EventLoopData *eld) {
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#ifdef HAS_EVENT_FD
|
2019-07-09 16:12:17 +03:00
|
|
|
|
static const uint64_t value = 1;
|
2019-07-09 16:10:51 +03:00
|
|
|
|
while (write(eld->wakeupFd, &value, sizeof value) < 0 && (errno == EINTR || errno == EAGAIN));
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#else
|
2019-07-09 16:10:51 +03:00
|
|
|
|
while (write(eld->wakeupFds[1], "w", 1) < 0 && (errno == EINTR || errno == EAGAIN));
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#endif
|
2019-07-05 07:04:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#ifndef HAS_EVENT_FD
|
2021-08-03 06:41:17 +03:00
|
|
|
|
static void
|
2019-07-05 07:04:51 +03:00
|
|
|
|
closeFds(int *fds, size_t count) {
|
|
|
|
|
while(count--) {
|
|
|
|
|
if (*fds > 0) {
|
|
|
|
|
close(*fds);
|
|
|
|
|
*fds = -1;
|
|
|
|
|
}
|
|
|
|
|
fds++;
|
|
|
|
|
}
|
2018-07-09 10:58:51 +03:00
|
|
|
|
}
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#endif
|
2018-07-09 10:58:51 +03:00
|
|
|
|
|
2019-07-05 07:04:51 +03:00
|
|
|
|
void
|
|
|
|
|
finalizePollData(EventLoopData *eld) {
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#ifdef HAS_EVENT_FD
|
|
|
|
|
close(eld->wakeupFd); eld->wakeupFd = -1;
|
|
|
|
|
#else
|
2019-07-05 07:04:51 +03:00
|
|
|
|
closeFds(eld->wakeupFds, arraysz(eld->wakeupFds));
|
2019-07-05 07:21:52 +03:00
|
|
|
|
#endif
|
2019-07-05 07:04:51 +03:00
|
|
|
|
}
|
2018-07-09 17:43:05 +03:00
|
|
|
|
|
|
|
|
|
int
|
2020-01-23 12:37:22 +03:00
|
|
|
|
pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func display_callback) {
|
2018-07-09 17:43:05 +03:00
|
|
|
|
int read_ok = 0;
|
|
|
|
|
timeout = prepareForPoll(eld, timeout);
|
2019-08-02 19:49:02 +03:00
|
|
|
|
EVDBG("pollForEvents final timeout: %.3f", monotonic_t_to_s_double(timeout));
|
2018-07-09 17:43:05 +03:00
|
|
|
|
int result;
|
2019-08-02 19:49:02 +03:00
|
|
|
|
monotonic_t end_time = monotonic() + timeout;
|
2019-07-18 11:55:11 +03:00
|
|
|
|
eld->wakeup_fd_ready = false;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
|
if (timeout >= 0) {
|
2018-07-13 08:03:18 +03:00
|
|
|
|
errno = 0;
|
2018-07-10 06:33:52 +03:00
|
|
|
|
result = pollWithTimeout(eld->fds, eld->watches_count, timeout);
|
2018-07-13 08:03:18 +03:00
|
|
|
|
int saved_errno = errno;
|
2020-01-23 12:37:22 +03:00
|
|
|
|
if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
|
2018-07-09 17:43:05 +03:00
|
|
|
|
dispatchTimers(eld);
|
|
|
|
|
if (result > 0) {
|
|
|
|
|
dispatchEvents(eld);
|
|
|
|
|
read_ok = eld->watches[0].ready;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-07-09 18:30:12 +03:00
|
|
|
|
timeout = end_time - monotonic();
|
2018-07-09 17:43:05 +03:00
|
|
|
|
if (timeout <= 0) break;
|
2018-07-13 08:03:18 +03:00
|
|
|
|
if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
break;
|
|
|
|
|
} else {
|
2018-07-13 08:03:18 +03:00
|
|
|
|
errno = 0;
|
2018-07-10 06:33:52 +03:00
|
|
|
|
result = poll(eld->fds, eld->watches_count, -1);
|
2018-07-13 08:03:18 +03:00
|
|
|
|
int saved_errno = errno;
|
2020-01-23 12:37:22 +03:00
|
|
|
|
if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
|
2018-07-09 17:43:05 +03:00
|
|
|
|
dispatchTimers(eld);
|
|
|
|
|
if (result > 0) {
|
|
|
|
|
dispatchEvents(eld);
|
|
|
|
|
read_ok = eld->watches[0].ready;
|
|
|
|
|
}
|
2018-07-13 08:03:18 +03:00
|
|
|
|
if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;
|
2018-07-09 17:43:05 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return read_ok;
|
|
|
|
|
}
|
2020-02-17 17:14:35 +03:00
|
|
|
|
|
|
|
|
|
// Duplicate a UTF-8 encoded string
|
|
|
|
|
// but cut it so that it has at most max_length bytes plus the null byte.
|
|
|
|
|
// This does not take combining characters into account.
|
|
|
|
|
GLFWAPI char* utf_8_strndup(const char* source, size_t max_length) {
|
|
|
|
|
if (!source) return NULL;
|
|
|
|
|
size_t length = strnlen(source, max_length);
|
|
|
|
|
if (length >= max_length) {
|
|
|
|
|
for (length = max_length; length > 0; length--) {
|
|
|
|
|
if ((source[length] & 0xC0) != 0x80) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* result = malloc(length + 1);
|
|
|
|
|
memcpy(result, source, length);
|
|
|
|
|
result[length] = 0;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2021-03-25 13:34:12 +03:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create a new, unique, anonymous file of the given size, and
|
|
|
|
|
* return the file descriptor for it. The file descriptor is set
|
|
|
|
|
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
|
|
|
|
* the given size at offset zero.
|
|
|
|
|
*
|
|
|
|
|
* The file should not have a permanent backing store like a disk,
|
|
|
|
|
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
|
|
|
|
*
|
|
|
|
|
* The file name is deleted from the file system.
|
|
|
|
|
*
|
|
|
|
|
* The file is suitable for buffer sharing between processes by
|
|
|
|
|
* transmitting the file descriptor over Unix sockets using the
|
|
|
|
|
* SCM_RIGHTS methods.
|
|
|
|
|
*
|
|
|
|
|
* posix_fallocate() is used to guarantee that disk space is available
|
|
|
|
|
* for the file at the given size. If disk space is insufficient, errno
|
|
|
|
|
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
|
|
|
|
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
|
|
|
|
*/
|
|
|
|
|
int createAnonymousFile(off_t size) {
|
|
|
|
|
int ret, fd = -1, shm_anon = 0;
|
|
|
|
|
#ifdef HAS_MEMFD_CREATE
|
2021-07-20 19:18:22 +03:00
|
|
|
|
fd = glfw_memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
2021-03-25 13:34:12 +03:00
|
|
|
|
if (fd < 0) return -1;
|
|
|
|
|
// We can add this seal before calling posix_fallocate(), as the file
|
|
|
|
|
// is currently zero-sized anyway.
|
|
|
|
|
//
|
|
|
|
|
// There is also no need to check for the return value, we couldn’t do
|
|
|
|
|
// anything with it anyway.
|
|
|
|
|
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
|
|
|
|
#elif defined(SHM_ANON)
|
|
|
|
|
fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
|
|
|
|
|
if (fd < 0) return -1;
|
|
|
|
|
shm_anon = 1;
|
|
|
|
|
#else
|
|
|
|
|
static const char template[] = "/glfw-shared-XXXXXX";
|
|
|
|
|
const char* path;
|
|
|
|
|
char* name;
|
|
|
|
|
|
|
|
|
|
path = getenv("XDG_RUNTIME_DIR");
|
|
|
|
|
if (!path)
|
|
|
|
|
{
|
|
|
|
|
errno = ENOENT;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name = calloc(strlen(path) + sizeof(template), 1);
|
|
|
|
|
strcpy(name, path);
|
|
|
|
|
strcat(name, template);
|
|
|
|
|
|
|
|
|
|
fd = createTmpfileCloexec(name);
|
|
|
|
|
|
|
|
|
|
free(name);
|
|
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
#endif
|
|
|
|
|
// posix_fallocate does not work on SHM descriptors
|
|
|
|
|
ret = shm_anon ? ftruncate(fd, size) : posix_fallocate(fd, 0, size);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
close(fd);
|
|
|
|
|
errno = ret;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return fd;
|
|
|
|
|
}
|