aboutsummaryrefslogtreecommitdiffstats
path: root/common/loop.c
blob: da3c21422194efd85e7efbc2070de816e0e6646c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "list.h"
#include "log.h"
#include "loop.h"

struct loop_event {
	void (*callback)(int fd, short mask, void *data);
	void *data;
	bool is_timer;
};

struct loop {
	struct pollfd *fds;
	int fd_length;
	int fd_capacity;

	list_t *events; // struct loop_event
};

struct loop *loop_create(void) {
	struct loop *loop = calloc(1, sizeof(struct loop));
	if (!loop) {
		wlr_log(WLR_ERROR, "Unable to allocate memory for loop");
		return NULL;
	}
	loop->fd_capacity = 10;
	loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity);
	loop->events = create_list();
	return loop;
}

void loop_destroy(struct loop *loop) {
	list_foreach(loop->events, free);
	list_free(loop->events);
	free(loop);
}

void loop_poll(struct loop *loop) {
	poll(loop->fds, loop->fd_length, -1);

	for (int i = 0; i < loop->fd_length; ++i) {
		struct pollfd pfd = loop->fds[i];
		struct loop_event *event = loop->events->items[i];

		// Always send these events
		unsigned events = pfd.events | POLLHUP | POLLERR;

		if (pfd.revents & events) {
			event->callback(pfd.fd, pfd.revents, event->data);

			if (event->is_timer) {
				loop_remove_event(loop, event);
				--i;
			}
		}
	}
}

struct loop_event *loop_add_fd(struct loop *loop, int fd, short mask,
		void (*callback)(int fd, short mask, void *data), void *data) {
	struct pollfd pfd = {fd, mask, 0};

	if (loop->fd_length == loop->fd_capacity) {
		loop->fd_capacity += 10;
		loop->fds = realloc(loop->fds, sizeof(struct pollfd) * loop->fd_capacity);
	}

	loop->fds[loop->fd_length++] = pfd;

	struct loop_event *event = calloc(1, sizeof(struct loop_event));
	event->callback = callback;
	event->data = data;

	list_add(loop->events, event);

	return event;
}

struct loop_event *loop_add_timer(struct loop *loop, int ms,
		void (*callback)(int fd, short mask, void *data), void *data) {
	int fd = timerfd_create(CLOCK_MONOTONIC, 0);
	struct itimerspec its;
	its.it_interval.tv_sec = 0;
	its.it_interval.tv_nsec = 0;
	its.it_value.tv_sec = ms / 1000;
	its.it_value.tv_nsec = (ms % 1000) * 1000000;
	timerfd_settime(fd, 0, &its, NULL);

	struct loop_event *event = loop_add_fd(loop, fd, POLLIN, callback, data);
	event->is_timer = true;

	return event;
}

bool loop_remove_event(struct loop *loop, struct loop_event *event) {
	for (int i = 0; i < loop->events->length; ++i) {
		if (loop->events->items[i] == event) {
			list_del(loop->events, i);

			if (event->is_timer) {
				close(loop->fds[i].fd);
			}

			loop->fd_length--;
			memmove(&loop->fds[i], &loop->fds[i + 1], sizeof(void*) * (loop->fd_length - i));

			free(event);
			return true;
		}
	}
	return false;
}