123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * 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"
- #include "internal.h"
- #include "memfd.h"
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <time.h>
- #include <stdio.h>
- #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;
- eld->fds[i].fd = w->fd;
- eld->fds[i].events = w->enabled ? w->events : 0;
- }
- }
- static id_type watch_counter = 0;
- id_type
- addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data) {
- if (eld->watches_count >= sizeof(eld->watches)/sizeof(eld->watches[0])) {
- _glfwInputError(GLFW_PLATFORM_ERROR, "Too many watches added");
- return 0;
- }
- Watch *w = eld->watches + eld->watches_count++;
- w->name = name;
- w->fd = fd; w->events = events; w->enabled = enabled;
- w->callback = cb;
- w->callback_data = cb_data;
- w->free = NULL;
- w->id = ++watch_counter;
- update_fds(eld);
- 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 (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; \
- } \
- 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) {
- 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].id == watch_id) {
- if (eld->watches[i].enabled != enabled) {
- eld->watches[i].enabled = enabled;
- update_fds(eld);
- }
- break;
- }
- }
- }
- 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;
- }
- static 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, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
- if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {
- _glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
- return 0;
- }
- Timer *t = eld->timers + eld->timers_count++;
- t->interval = interval;
- t->name = name;
- t->trigger_at = enabled ? monotonic() + interval : MONOTONIC_T_MAX;
- t->repeats = repeats;
- t->callback = cb;
- t->callback_data = cb_data;
- t->free = free;
- t->id = ++timer_counter;
- update_timers(eld);
- return timer_counter;
- }
- void
- removeTimer(EventLoopData *eld, id_type timer_id) {
- removeX(timers, timer_id, update_timers);
- }
- 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;
- }
- 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) {
- monotonic_t trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : MONOTONIC_T_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, monotonic_t interval) {
- for (nfds_t i = 0; i < eld->timers_count; i++) {
- if (eld->timers[i].id == timer_id) {
- eld->timers[i].interval = interval;
- break;
- }
- }
- }
- monotonic_t
- prepareForPoll(EventLoopData *eld, monotonic_t timeout) {
- for (nfds_t i = 0; i < eld->watches_count; i++) eld->fds[i].revents = 0;
- 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;
- if (timeout < 0 || now + timeout > next_repeat_at) {
- timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
- }
- return timeout;
- }
- static struct timespec
- calc_time(monotonic_t nsec) {
- struct timespec result;
- result.tv_sec = nsec / (1000LL * 1000LL * 1000LL);
- result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);
- return result;
- }
- int
- pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout) {
- struct timespec tv = calc_time(timeout);
- return ppoll(fds, nfds, &tv, NULL);
- }
- static void
- dispatchEvents(EventLoopData *eld) {
- 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) {
- ww->ready = 1;
- if (ww->callback) ww->callback(ww->fd, pfd->revents, ww->callback_data);
- } else ww->ready = 0;
- }
- }
- unsigned
- dispatchTimers(EventLoopData *eld) {
- if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return 0;
- static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
- unsigned num_dispatches = 0;
- monotonic_t now = monotonic();
- 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;
- dispatches[num_dispatches].repeats = eld->timers[i].repeats;
- 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 (!dispatches[i].repeats) {
- removeTimer(eld, dispatches[i].id);
- }
- }
- if (num_dispatches) update_timers(eld);
- return num_dispatches;
- }
- static void
- drain_wakeup_fd(int fd, EventLoopData* eld) {
- static char drain_buf[64];
- eld->wakeup_data_read = false;
- while(true) {
- ssize_t ret = read(fd, drain_buf, sizeof(drain_buf));
- if (ret < 0) {
- if (errno == EINTR) continue;
- break;
- }
- if (ret > 0) { eld->wakeup_data_read = true; continue; }
- break;
- }
- }
- static void
- mark_wakep_fd_ready(int fd UNUSED, int events UNUSED, void *data) {
- ((EventLoopData*)(data))->wakeup_fd_ready = true;
- }
- bool
- initPollData(EventLoopData *eld, int display_fd) {
- if (!addWatch(eld, "display", display_fd, POLLIN, 1, NULL, NULL)) return false;
- #ifdef HAS_EVENT_FD
- eld->wakeupFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
- if (eld->wakeupFd == -1) return false;
- const int wakeup_fd = eld->wakeupFd;
- #else
- if (pipe2(eld->wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0) return false;
- const int wakeup_fd = eld->wakeupFds[0];
- #endif
- if (!addWatch(eld, "wakeup", wakeup_fd, POLLIN, 1, mark_wakep_fd_ready, eld)) return false;
- return true;
- }
- 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);
- }
- void
- wakeupEventLoop(EventLoopData *eld) {
- #ifdef HAS_EVENT_FD
- static const uint64_t value = 1;
- while (write(eld->wakeupFd, &value, sizeof value) < 0 && (errno == EINTR || errno == EAGAIN));
- #else
- while (write(eld->wakeupFds[1], "w", 1) < 0 && (errno == EINTR || errno == EAGAIN));
- #endif
- }
- #ifndef HAS_EVENT_FD
- static void
- closeFds(int *fds, size_t count) {
- while(count--) {
- if (*fds > 0) {
- close(*fds);
- *fds = -1;
- }
- fds++;
- }
- }
- #endif
- void
- finalizePollData(EventLoopData *eld) {
- #ifdef HAS_EVENT_FD
- close(eld->wakeupFd); eld->wakeupFd = -1;
- #else
- closeFds(eld->wakeupFds, arraysz(eld->wakeupFds));
- #endif
- }
- int
- pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func display_callback) {
- int read_ok = 0;
- timeout = prepareForPoll(eld, timeout);
- EVDBG("pollForEvents final timeout: %.3f", monotonic_t_to_s_double(timeout));
- int result;
- monotonic_t end_time = monotonic() + timeout;
- eld->wakeup_fd_ready = false;
- while(1) {
- if (timeout >= 0) {
- errno = 0;
- result = pollWithTimeout(eld->fds, eld->watches_count, timeout);
- int saved_errno = errno;
- if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
- dispatchTimers(eld);
- if (result > 0) {
- dispatchEvents(eld);
- read_ok = eld->watches[0].ready;
- break;
- }
- timeout = end_time - monotonic();
- if (timeout <= 0) break;
- if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;
- break;
- } else {
- errno = 0;
- result = poll(eld->fds, eld->watches_count, -1);
- int saved_errno = errno;
- if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
- dispatchTimers(eld);
- if (result > 0) {
- dispatchEvents(eld);
- read_ok = eld->watches[0].ready;
- }
- if (result < 0 && (saved_errno == EINTR || saved_errno == EAGAIN)) continue;
- break;
- }
- }
- return read_ok;
- }
- // 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;
- }
- /*
- * 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
- fd = glfw_memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
- 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;
- }
|