#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include "swaybar/event_loop.h" #include "list.h" struct event_item { void (*cb)(int fd, short mask, void *data); void *data; }; struct timer_item { timer_t timer; void (*cb)(timer_t timer, void *data); void *data; }; static struct { // The order of each must be kept consistent struct { /* pollfd array */ struct pollfd *items; int capacity; int length; } fds; list_t *items; /* event_item list */ // Timer list list_t *timers; } event_loop; void add_timer(timer_t timer, void(*cb)(timer_t timer, void *data), void *data) { struct timer_item *item = malloc(sizeof(struct timer_item)); item->timer = timer; item->cb = cb; item->data = data; list_add(event_loop.timers, item); } void add_event(int fd, short mask, void(*cb)(int fd, short mask, void *data), void *data) { struct pollfd pollfd = { fd, mask, 0, }; // Resize if (event_loop.fds.length == event_loop.fds.capacity) { event_loop.fds.capacity += 10; event_loop.fds.items = realloc(event_loop.fds.items, sizeof(struct pollfd) * event_loop.fds.capacity); } event_loop.fds.items[event_loop.fds.length++] = pollfd; struct event_item *item = malloc(sizeof(struct event_item)); item->cb = cb; item->data = data; list_add(event_loop.items, item); return; } bool remove_event(int fd) { /* * Instead of removing events immediately, we mark them for deletion * and clean them up later. This is so we can call remove_event inside * an event callback safely. */ for (int i = 0; i < event_loop.fds.length; ++i) { if (event_loop.fds.items[i].fd == fd) { event_loop.fds.items[i].fd = -1; return true; } } return false; } static int timer_item_timer_cmp(const void *_timer_item, const void *_timer) { const struct timer_item *timer_item = _timer_item; const timer_t *timer = _timer; if (timer_item->timer == *timer) { return 0; } else { return -1; } } bool remove_timer(timer_t timer) { int index = list_seq_find(event_loop.timers, timer_item_timer_cmp, &timer); if (index != -1) { free(event_loop.timers->items[index]); list_del(event_loop.timers, index); return true; } return false; } void event_loop_poll() { poll(event_loop.fds.items, event_loop.fds.length, -1); for (int i = 0; i < event_loop.fds.length; ++i) { struct pollfd pfd = event_loop.fds.items[i]; struct event_item *item = (struct event_item *)event_loop.items->items[i]; // Always send these events unsigned events = pfd.events | POLLHUP | POLLERR; if (pfd.revents & events) { item->cb(pfd.fd, pfd.revents, item->data); } } // Cleanup removed events int end = 0; int length = event_loop.fds.length; for (int i = 0; i < length; ++i) { if (event_loop.fds.items[i].fd == -1) { free(event_loop.items->items[i]); list_del(event_loop.items, i); --event_loop.fds.length; } else if (end != i) { event_loop.fds.items[end++] = event_loop.fds.items[i]; } else { end = i + 1; } } // check timers // not tested, but seems to work for (int i = 0; i < event_loop.timers->length; ++i) { struct timer_item *item = event_loop.timers->items[i]; int overrun = timer_getoverrun(item->timer); if (overrun && overrun != -1) { item->cb(item->timer, item->data); } } } void init_event_loop() { event_loop.fds.length = 0; event_loop.fds.capacity = 10; event_loop.fds.items = malloc( event_loop.fds.capacity * sizeof(struct pollfd)); event_loop.items = create_list(); event_loop.timers = create_list(); }