summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Grenville <pyxlcy@gmail.com>2013-01-11 21:31:02 +0800
committerRichard Grenville <pyxlcy@gmail.com>2013-01-11 21:31:02 +0800
commit5871e8f4029d3a0dd532e2b4cc0f7733b8f28fac (patch)
tree21fc3b84ceb736836a16ef99d2f260d5311c7519
parent6e74af148e53a29734d0cfdcddc26211f5a5ff90 (diff)
downloadtdebase-5871e8f4.tar.gz
tdebase-5871e8f4.zip
Improvement: Use select() for main loop
- Back to using select() for main loop. Thus we are not longer relying on libevent. - Add generic timeout system (untested) to prepare for D-Bus support. - Drop troff man pages. Revise Makefile to improve documentation building, fix double LDFLAGS inclusion, and re-add -lrt. This turns asciidoc into a build time dependency. - Change fading time calculation. - Add --logpath and ostream_reopen() for debugging with -b. - Drop unused lceil_ntimes() and other helper functions. - Only very limited tests are done. Bugs to be expected.
-rw-r--r--compton.c361
-rw-r--r--compton.h225
2 files changed, 429 insertions, 157 deletions
diff --git a/compton.c b/compton.c
index e1b8ade72..941134a10 100644
--- a/compton.c
+++ b/compton.c
@@ -57,8 +57,7 @@ static int
fade_timeout(session_t *ps) {
int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time;
- if (diff < 0)
- diff = 0;
+ diff = normalize_i_range(diff, 0, ps->o.fade_delta * 2);
return diff;
}
@@ -1196,11 +1195,14 @@ paint_preprocess(session_t *ps, win *list) {
bool is_highest = true;
// Fading step calculation
- time_ms_t steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta;
- if (steps < 0L) {
- // Time disorder
+ time_ms_t steps = 0L;
+ if (ps->fade_time) {
+ steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta;
+ }
+ // Reset fade_time if unset, or there appears to be a time disorder
+ if (!ps->fade_time || steps < 0L) {
ps->fade_time = get_time_ms();
- steps = 0;
+ steps = 0L;
}
ps->fade_time += steps * ps->o.fade_delta;
@@ -3921,10 +3923,28 @@ register_cm(session_t *ps, bool want_glxct) {
}
/**
+ * Reopen streams for logging.
+ */
+static bool
+ostream_reopen(session_t *ps, const char *path) {
+ if (!path)
+ path = ps->o.logpath;
+ if (!path)
+ path = "/dev/null";
+
+ bool success = freopen(path, "a", stdout);
+ success = freopen(path, "a", stderr) && success;
+ if (!success)
+ printf_errfq(1, "(%s): freopen() failed.", path);
+
+ return success;
+}
+
+/**
* Fork program to background and disable all I/O streams.
*/
static bool
-fork_after(void) {
+fork_after(session_t *ps) {
if (getppid() == 1)
return true;
@@ -3941,18 +3961,13 @@ fork_after(void) {
// Mainly to suppress the _FORTIFY_SOURCE warning
bool success = freopen("/dev/null", "r", stdin);
- success = freopen("/dev/null", "w", stdout) && success;
- if (!success) {
- printf_errf("(): freopen() failed.");
- return false;
- }
- success = freopen("/dev/null", "w", stderr);
if (!success) {
printf_errf("(): freopen() failed.");
return false;
}
+ success = ostream_reopen(ps, NULL);
- return true;
+ return success;
}
#ifdef CONFIG_LIBCONFIG
@@ -4299,6 +4314,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
{ "blur-background-frame", no_argument, NULL, 284 },
{ "blur-background-fixed", no_argument, NULL, 285 },
{ "dbus", no_argument, NULL, 286 },
+ { "logpath", required_argument, NULL, 287 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@@ -4405,8 +4421,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
case 'n':
case 'a':
case 's':
- fprintf(stderr, "Warning: "
- "-n, -a, and -s have been removed.\n");
+ printf_errfq(1, "(): -n, -a, and -s have been removed.");
break;
case 'b':
ps->o.fork_after_register = true;
@@ -4535,8 +4550,13 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
// --dbus
ps->o.dbus = true;
break;
+ case 287:
+ // --logpath
+ ps->o.logpath = mstrcpy(optarg);
+ break;
default:
usage();
+ break;
}
}
@@ -4695,22 +4715,6 @@ swopti_init(session_t *ps) {
}
/**
- * Get the smaller number that is bigger than <code>dividend</code> and is
- * N times of <code>divisor</code>.
- */
-static inline long
-lceil_ntimes(long dividend, long divisor) {
- // It's possible to use the more beautiful expression here:
- // ret = ((dividend - 1) / divisor + 1) * divisor;
- // But it does not work well for negative values.
- long ret = dividend / divisor * divisor;
- if (ret < dividend)
- ret += divisor;
-
- return ret;
-}
-
-/**
* Modify a struct timeval timeout value to render at a fixed pace.
*
* @param ps current session
@@ -4744,32 +4748,6 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) {
}
/**
- * Libevent callback function to handle X events.
- */
-static void
-evcallback_x(evutil_socket_t fd, short what, void *arg) {
- session_t *ps = ps_g;
-
- // Sometimes poll() returns 1 but no events are actually read,
- // causing XNextEvent() to block, I have no idea what's wrong, so we
- // check for the number of events here
- if (XEventsQueued(ps->dpy, QueuedAfterReading)) {
- XEvent ev = { };
-
- XNextEvent(ps->dpy, &ev);
- ev_handle(ps, &ev);
- ps->ev_received = true;
- }
-}
-
-/**
- * NULL libevent callback function.
- */
-static void
-evcallback_null(evutil_socket_t fd, short what, void *arg) {
-}
-
-/**
* Initialize DRM VSync.
*
* @return true for success, false otherwise
@@ -5004,6 +4982,141 @@ redir_start(session_t *ps) {
}
/**
+ * Get the poll time.
+ */
+static time_ms_t
+timeout_get_poll_time(session_t *ps) {
+ const time_ms_t now = get_time_ms();
+ time_ms_t wait = TIME_MS_MAX;
+
+ // Traverse throught the timeout linked list
+ for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) {
+ if (ptmout->enabled) {
+ // Truncate the last run time to the closest interval
+ time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval;
+ if (newrun <= now) {
+ wait = 0;
+ break;
+ }
+ else {
+ time_ms_t newwait = newrun - now;
+ if (newwait < wait)
+ wait = newwait;
+ }
+ }
+ }
+
+ return wait;
+}
+
+/**
+ * Insert a new timeout.
+ */
+static timeout_t *
+timeout_insert(session_t *ps, time_ms_t interval,
+ bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) {
+ const static timeout_t tmout_def = {
+ .enabled = true,
+ .data = NULL,
+ .callback = NULL,
+ .firstrun = 0L,
+ .lastrun = 0L,
+ .interval = 0L,
+ };
+
+ const time_ms_t now = get_time_ms();
+ timeout_t *ptmout = malloc(sizeof(timeout_t));
+ if (!ptmout)
+ printf_errfq(1, "(): Failed to allocate memory for timeout.");
+ memcpy(ptmout, &tmout_def, sizeof(timeout_t));
+
+ ptmout->interval = interval;
+ ptmout->firstrun = now;
+ ptmout->lastrun = now;
+ ptmout->data = data;
+ ptmout->callback = callback;
+ ptmout->next = ps->tmout_lst;
+ ps->tmout_lst = ptmout;
+
+ return ptmout;
+}
+
+/**
+ * Drop a timeout.
+ *
+ * @return true if we have found the timeout and removed it, false
+ * otherwise
+ */
+static bool
+timeout_drop(session_t *ps, timeout_t *prm) {
+ timeout_t **pplast = &ps->tmout_lst;
+
+ for (timeout_t *ptmout = ps->tmout_lst; ptmout;
+ pplast = &ptmout->next, ptmout = ptmout->next) {
+ if (prm == ptmout) {
+ *pplast = ptmout->next;
+ free(ptmout);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Clear all timeouts.
+ */
+static void
+timeout_clear(session_t *ps) {
+ timeout_t *ptmout = ps->tmout_lst;
+ timeout_t *next = NULL;
+ while (ptmout) {
+ next = ptmout->next;
+ free(ptmout);
+ ptmout = next;
+ }
+}
+
+/**
+ * Run timeouts.
+ *
+ * @return true if we have ran a timeout, false otherwise
+ */
+static bool
+timeout_run(session_t *ps) {
+ const time_ms_t now = get_time_ms();
+ bool ret = false;
+
+ for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) {
+ if (ptmout->enabled) {
+ const time_ms_t max = now +
+ (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE);
+ time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval;
+ if (newrun <= max) {
+ ret = true;
+ timeout_invoke(ps, ptmout);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Invoke a timeout.
+ */
+static void
+timeout_invoke(session_t *ps, timeout_t *ptmout) {
+ const time_ms_t now = get_time_ms();
+ ptmout->lastrun = now;
+ // Avoid modifying the timeout structure after running timeout, to
+ // make it possible to remove timeout in callback
+ if (ptmout->callback)
+ ptmout->callback(ps, ptmout);
+}
+
+/**
* Unredirect all windows.
*/
static void
@@ -5037,36 +5150,74 @@ redir_stop(session_t *ps) {
*/
static bool
mainloop(session_t *ps) {
- bool infinite_wait = false;
-
// Process existing events
+ // Sometimes poll() returns 1 but no events are actually read,
+ // causing XNextEvent() to block, I have no idea what's wrong, so we
+ // check for the number of events here.
if (XEventsQueued(ps->dpy, QueuedAfterReading)) {
- evcallback_x(ConnectionNumber(ps->dpy), 0, NULL);
+ XEvent ev = { };
+
+ XNextEvent(ps->dpy, &ev);
+ ev_handle(ps, &ev);
+ ps->ev_received = true;
+
return true;
}
- // Add timeout
- if (ps->ev_received || !ps->idling) {
- struct timeval tv = ms_to_tv(ps->ev_received ? 0: fade_timeout(ps));
- if (ps->o.sw_opti)
- swopti_handle_timeout(ps, &tv);
- assert(tv.tv_sec >= 0 && tv.tv_usec >= 0);
- if (timeval_isempty(tv))
+ if (ps->reset)
+ return false;
+
+ // Calculate timeout
+ struct timeval *ptv = NULL;
+ {
+ // Consider ev_received firstly
+ if (ps->ev_received) {
+ ptv = malloc(sizeof(struct timeval));
+ ptv->tv_sec = 0L;
+ ptv->tv_usec = 0L;
+ }
+ // Then consider fading timeout
+ else if (!ps->idling) {
+ ptv = malloc(sizeof(struct timeval));
+ *ptv = ms_to_tv(fade_timeout(ps));
+ }
+
+ // Software optimization is to be applied on timeouts that require
+ // immediate painting only
+ if (ptv && ps->o.sw_opti)
+ swopti_handle_timeout(ps, ptv);
+
+ // Don't continue looping for 0 timeout
+ if (ptv && timeval_isempty(ptv)) {
+ free(ptv);
return false;
- evtimer_add(ps->ev_tmout, &tv);
- }
- else {
- infinite_wait = true;
- }
+ }
- // Run libevent main loop
- if (event_base_loop(ps->ev_base, EVLOOP_ONCE))
- printf_errfq(1, "(): Unexpected error when running event loop.");
+ // Now consider the waiting time of other timeouts
+ time_ms_t tmout_ms = timeout_get_poll_time(ps);
+ if (tmout_ms < TIME_MS_MAX) {
+ if (!ptv) {
+ ptv = malloc(sizeof(struct timeval));
+ *ptv = ms_to_tv(tmout_ms);
+ }
+ else if (timeval_ms_cmp(ptv, tmout_ms) > 0) {
+ *ptv = ms_to_tv(tmout_ms);
+ }
+ }
+
+ // Don't continue looping for 0 timeout
+ if (ptv && timeval_isempty(ptv)) {
+ free(ptv);
+ return false;
+ }
+ }
- evtimer_del(ps->ev_tmout);
+ // Polling
+ fds_poll(ps, ptv);
+ free(ptv);
+ ptv = NULL;
- if (infinite_wait)
- ps->fade_time = get_time_ms();
+ timeout_run(ps);
return true;
}
@@ -5106,6 +5257,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
.detect_rounded_corners = false,
.paint_on_overlay = false,
.unredir_if_possible = false,
+ .dbus = false,
+ .logpath = NULL,
.refresh_rate = 0,
.sw_opti = false,
@@ -5156,6 +5309,12 @@ session_init(session_t *ps_old, int argc, char **argv) {
.track_leader = false,
},
+ .pfds_read = NULL,
+ .pfds_write = NULL,
+ .pfds_except = NULL,
+ .nfds_max = 0,
+ .tmout_lst = NULL,
+
.all_damage = None,
.time_start = { 0, 0 },
.redirected = false,
@@ -5163,7 +5322,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.alpha_picts = NULL,
.reg_ignore_expire = false,
.idling = false,
- .fade_time = 0,
+ .fade_time = 0L,
.ignore_head = NULL,
.ignore_tail = NULL,
.reset = false,
@@ -5406,24 +5565,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue);
}
- ps->all_damage = None;
-
- // Build event base
- if (!(ps->ev_base =
-#ifndef CONFIG_LIBEVENT_LEGACY
- event_base_new()
-#else
- event_init()
-#endif
- ))
- printf_errfq(1, "(): Failed to build event base.");
- if (!(ps->ev_x = EVENT_NEW(ps->ev_base, ConnectionNumber(ps->dpy),
- EV_READ | EV_PERSIST, evcallback_x, NULL)))
- printf_errfq(1, "(): Failed to build event.");
- if (event_add(ps->ev_x, NULL))
- printf_errfq(1, "(): Failed to add event.");
- if (!(ps->ev_tmout = evtimer_new(ps->ev_base, evcallback_null, NULL)))
- printf_errfq(1, "(): Failed to build event.");
+ fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN);
XGrabServer(ps->dpy);
@@ -5459,14 +5601,10 @@ session_init(session_t *ps_old, int argc, char **argv) {
// Fork to background, if asked
if (ps->o.fork_after_register) {
- if (!fork_after()) {
+ if (!fork_after(ps)) {
session_destroy(ps);
return NULL;
}
-
- // Reinitialize event base
- if (event_reinit(ps->ev_base) < 0)
- printf_errfq(1, "Failed to reinitialize event base.");
}
// Free the old session
@@ -5565,6 +5703,10 @@ session_destroy(session_t *ps) {
free(ps->shadow_top);
free(ps->gaussian_map);
free(ps->o.display);
+ free(ps->o.logpath);
+ free(ps->pfds_read);
+ free(ps->pfds_write);
+ free(ps->pfds_except);
// Free reg_win and glx_context
if (ps->reg_win) {
@@ -5598,14 +5740,12 @@ session_destroy(session_t *ps) {
ps->overlay = None;
}
- // Free libevent things
- event_free(ps->ev_x);
- event_free(ps->ev_tmout);
- event_base_free(ps->ev_base);
-
// Flush all events
XSync(ps->dpy, True);
+ // Free timeouts
+ timeout_clear(ps);
+
if (ps == ps_g)
ps_g = NULL;
}
@@ -5624,8 +5764,6 @@ session_run(session_t *ps) {
ps->reg_ignore_expire = true;
- ps->fade_time = get_time_ms();
-
t = paint_preprocess(ps, ps->list);
if (ps->redirected)
@@ -5658,6 +5796,9 @@ session_run(session_t *ps) {
XSync(ps->dpy, False);
ps->all_damage = None;
}
+
+ if (ps->idling)
+ ps->fade_time = 0L;
}
}
diff --git a/compton.h b/compton.h
index 9bf25e240..cb4f623f9 100644
--- a/compton.h
+++ b/compton.h
@@ -43,9 +43,11 @@
#include <string.h>
#include <inttypes.h>
#include <math.h>
+#include <sys/select.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <time.h>
+#include <limits.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
@@ -54,20 +56,6 @@
#include <fnmatch.h>
#include <signal.h>
-// libevent
-#ifndef CONFIG_LIBEVENT_LEGACY
-#include <event2/event.h>
-#else
-#include <event.h>
-typedef int evutil_socket_t;
-typedef void(* event_callback_fn)(evutil_socket_t, short, void *);
-#define event_free(ev) (event_del((ev)), free((ev)))
-#endif
-
-#ifndef evtimer_new
-#define evtimer_new(b, cb, arg) EVENT_NEW((b), -1, 0, (cb), (arg))
-#endif
-
// libpcre
#ifdef CONFIG_REGEX_PCRE
#include <pcre.h>
@@ -127,8 +115,10 @@ typedef void(* event_callback_fn)(evutil_socket_t, short, void *);
#define OPAQUE 0xffffffff
#define REGISTER_PROP "_NET_WM_CM_S"
+#define TIME_MS_MAX LONG_MAX
#define FADE_DELTA_TOLERANCE 0.2
#define SWOPTI_TOLERANCE 3000
+#define TIMEOUT_RUN_TOLERANCE 0.2
#define WIN_GET_LEADER_MAX_RECURSION 20
#define SEC_WRAP (15L * 24L * 60L * 60L)
@@ -286,6 +276,8 @@ typedef struct {
double *data;
} conv;
+struct _timeout_t;
+
struct _win;
/// Structure representing all options.
@@ -308,6 +300,8 @@ typedef struct {
bool unredir_if_possible;
/// Whether to enable D-Bus support.
bool dbus;
+ /// Path to log file.
+ char *logpath;
/// Whether to work under synchronized mode for debugging.
bool synchronize;
@@ -447,12 +441,16 @@ typedef struct {
// === Operation related ===
/// Program options.
options_t o;
- /// Libevent event base.
- struct event_base *ev_base;
- /// Libevent event for X connection.
- struct event *ev_x;
- /// Libevent event for timeout.
- struct event *ev_tmout;
+ /// File descriptors to check for reading.
+ fd_set *pfds_read;
+ /// File descriptors to check for writing.
+ fd_set *pfds_write;
+ /// File descriptors to check for exceptions.
+ fd_set *pfds_except;
+ /// Largest file descriptor in fd_set-s above.
+ int nfds_max;
+ /// Linked list of all timeouts.
+ struct _timeout_t *tmout_lst;
/// Whether we have received an event in this cycle.
bool ev_received;
/// Whether the program is idling. I.e. no fading, no potential window
@@ -770,6 +768,18 @@ struct options_tmp {
double menu_opacity;
};
+/// Structure for a recorded timeout.
+typedef struct _timeout_t {
+ bool enabled;
+ void *data;
+ bool (*callback)(session_t *ps, struct _timeout_t *ptmout);
+ time_ms_t interval;
+ time_ms_t firstrun;
+ time_ms_t lastrun;
+ struct _timeout_t *next;
+} timeout_t;
+
+/// Enumeration for window event hints.
typedef enum {
WIN_EVMODE_UNKNOWN,
WIN_EVMODE_FRAME,
@@ -840,22 +850,6 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg,
// == Functions ==
-/**
- * Wrapper of libevent event_new(), for compatibility with libevent-1\.x.
- */
-static inline struct event *
-EVENT_NEW(struct event_base *base, evutil_socket_t fd,
- short what, event_callback_fn cb, void *arg) {
-#ifndef CONFIG_LIBEVENT_LEGACY
- return event_new(base, fd, what, cb, arg);
-#else
- struct event *pev = malloc(sizeof(struct event));
- if (pev)
- event_set(pev, fd, what, cb, arg);
- return pev;
-#endif
-}
-
// inline functions must be made static to compile correctly under clang:
// http://clang.llvm.org/compatibility.html#inline
@@ -1008,6 +1002,14 @@ min_i(int a, int b) {
}
/**
+ * Select the smaller long integer of two.
+ */
+static inline long __attribute__((const))
+min_l(long a, long b) {
+ return (a > b ? b : a);
+}
+
+/**
* Normalize a double value to a specific range.
*
* @param d double value to normalize
@@ -1055,8 +1057,41 @@ array_wid_exists(const Window *arr, int count, Window wid) {
* Return whether a struct timeval value is empty.
*/
static inline bool
-timeval_isempty(struct timeval tv) {
- return tv.tv_sec <= 0 && tv.tv_usec <= 0;
+timeval_isempty(struct timeval *ptv) {
+ if (!ptv)
+ return false;
+
+ return ptv->tv_sec <= 0 && ptv->tv_usec <= 0;
+}
+
+/**
+ * Compare a struct timeval with a time in milliseconds.
+ *
+ * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms
+ */
+static inline int
+timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) {
+ assert(ptv);
+
+ // We use those if statement instead of a - expression because of possible
+ // truncation problem from long to int.
+ {
+ long sec = ms / MS_PER_SEC;
+ if (ptv->tv_sec > sec)
+ return 1;
+ if (ptv->tv_sec < sec)
+ return -1;
+ }
+
+ {
+ long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC);
+ if (ptv->tv_usec > usec)
+ return 1;
+ if (ptv->tv_usec < usec)
+ return -1;
+ }
+
+ return 0;
}
/*
@@ -1290,8 +1325,94 @@ ms_to_tv(int timeout) {
};
}
-static int
-fade_timeout(session_t *ps);
+/**
+ * Add a file descriptor to a select() fd_set.
+ */
+static inline bool
+fds_insert_select(fd_set **ppfds, int fd) {
+ assert(fd <= FD_SETSIZE);
+
+ if (!*ppfds) {
+ if ((*ppfds = malloc(sizeof(fd_set)))) {
+ FD_ZERO(*ppfds);
+ }
+ else {
+ fprintf(stderr, "Failed to allocate memory for select() fdset.\n");
+ exit(1);
+ }
+ }
+
+ FD_SET(fd, *ppfds);
+
+ return true;
+}
+
+/**
+ * Add a new file descriptor to wait for.
+ */
+static inline bool
+fds_insert(session_t *ps, int fd, short events) {
+ bool result = true;
+
+ ps->nfds_max = max_i(fd + 1, ps->nfds_max);
+
+ if (POLLIN & events)
+ result = fds_insert_select(&ps->pfds_read, fd) && result;
+ if (POLLOUT & events)
+ result = fds_insert_select(&ps->pfds_write, fd) && result;
+ if (POLLPRI & events)
+ result = fds_insert_select(&ps->pfds_except, fd) && result;
+
+ return result;
+}
+
+/**
+ * Delete a file descriptor to wait for.
+ */
+static inline void
+fds_drop(session_t *ps, int fd, short events) {
+ // Drop fd from respective fd_set-s
+ if (POLLIN & events)
+ FD_CLR(fd, ps->pfds_read);
+ if (POLLOUT & events)
+ FD_CLR(fd, ps->pfds_write);
+ if (POLLPRI & events)
+ FD_CLR(fd, ps->pfds_except);
+}
+
+#define CPY_FDS(key) \
+ fd_set * key = NULL; \
+ if (ps->key) { \
+ key = malloc(sizeof(fd_set)); \
+ memcpy(key, ps->key, sizeof(fd_set)); \
+ if (!key) { \
+ fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \
+ exit(1); \
+ } \
+ } \
+
+/**
+ * Poll for changes.
+ *
+ * poll() is much better than select(), but ppoll() does not exist on
+ * *BSD.
+ */
+static inline int
+fds_poll(session_t *ps, struct timeval *ptv) {
+ // Copy fds
+ CPY_FDS(pfds_read);
+ CPY_FDS(pfds_write);
+ CPY_FDS(pfds_except);
+
+ int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv);
+
+ free(pfds_read);
+ free(pfds_write);
+ free(pfds_except);
+
+ return ret;
+}
+#undef CPY_FDS
static void
run_fade(session_t *ps, win *w, unsigned steps);
@@ -2030,7 +2151,7 @@ inline static void
ev_handle(session_t *ps, XEvent *ev);
static bool
-fork_after(void);
+fork_after(session_t *ps);
#ifdef CONFIG_LIBCONFIG
/**
@@ -2095,12 +2216,6 @@ swopti_init(session_t *ps);
static void
swopti_handle_timeout(session_t *ps, struct timeval *ptv);
-static void
-evcallback_x(evutil_socket_t fd, short what, void *arg);
-
-static void
-evcallback_null(evutil_socket_t fd, short what, void *arg);
-
static bool
vsync_drm_init(session_t *ps);
@@ -2135,6 +2250,22 @@ redir_start(session_t *ps);
static void
redir_stop(session_t *ps);
+static time_ms_t
+timeout_get_poll_time(session_t *ps);
+
+static timeout_t *
+timeout_insert(session_t *ps, time_ms_t interval,
+ bool (*callback)(session_t *ps, timeout_t *ptmout), void *data);
+
+static void
+timeout_invoke(session_t *ps, timeout_t *ptmout);
+
+static bool
+timeout_drop(session_t *ps, timeout_t *prm);
+
+static void
+timeout_clear(session_t *ps);
+
static bool
mainloop(session_t *ps);