Skip to content

Instantly share code, notes, and snippets.

@richardgv
Created January 2, 2013 13:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardgv/4434479 to your computer and use it in GitHub Desktop.
Save richardgv/4434479 to your computer and use it in GitHub Desktop.
chjj/compton: Patch to use libevent for the main loop
diff --git a/Makefile b/Makefile
index 10e6f17..bc0dcd2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+# Use tab to indent recipe lines, spaces to indent other lines, otherwise
+# GNU make may get unhappy.
+
CC ?= gcc
PREFIX ?= /usr
@@ -8,44 +11,72 @@ PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr
LIBS = -lm -lrt
INCS =
-# Parse configuration flags
+# Configuration flags
CFG =
+# Seemingly, libevent1 has no pkg-config file!
+# FreeBSD may have issues handling the version in pkg-config.
+ifeq "$(shell pkg-config --modversion --print-errors libevent)" ""
+ $(warning libevent-2.0 not found, assuming libevent-1.4.x.)
+ CFG += -DCONFIG_LIBEVENT_LEGACY
+ LIBS += -levent
+else
+ # Using pkg-config for linking with libevent will result in linking with
+ # libevent.so instead of the smaller libevent_core.so. But FreeBSD keeps
+ # libevent2 .so files at a separate place, and we must learn it from
+ # pkg-config.
+ LIBS += $(shell pkg-config --libs libevent)
+ INCS += $(shell pkg-config --cflags libevent)
+endif
+
ifeq "$(NO_LIBCONFIG)" ""
- CFG += -DCONFIG_LIBCONFIG
- PACKAGES += libconfig
+ CFG += -DCONFIG_LIBCONFIG
+ PACKAGES += libconfig
- # libconfig-1.3* does not define LIBCONFIG_VER* macros, so we use
- # pkg-config to determine its version here
- CFG += $(shell pkg-config --atleast-version=1.4 libconfig || echo '-DCONFIG_LIBCONFIG_LEGACY')
+ # libconfig-1.3* does not define LIBCONFIG_VER* macros, so we use
+ # pkg-config to determine its version here
+ CFG += $(shell pkg-config --atleast-version=1.4 libconfig || echo '-DCONFIG_LIBCONFIG_LEGACY')
endif
ifeq "$(NO_REGEX_PCRE)" ""
- CFG += -DCONFIG_REGEX_PCRE
- LIBS += $(shell pcre-config --libs)
- INCS += $(shell pcre-config --cflags)
- ifeq "$(NO_REGEX_PCRE_JIT)" ""
- CFG += -DCONFIG_REGEX_PCRE_JIT
- endif
+ CFG += -DCONFIG_REGEX_PCRE
+ LIBS += $(shell pcre-config --libs)
+ INCS += $(shell pcre-config --cflags)
+ ifeq "$(NO_REGEX_PCRE_JIT)" ""
+ CFG += -DCONFIG_REGEX_PCRE_JIT
+ endif
endif
ifeq "$(NO_VSYNC_DRM)" ""
- CFG += -DCONFIG_VSYNC_DRM
+ CFG += -DCONFIG_VSYNC_DRM
endif
ifeq "$(NO_VSYNC_OPENGL)" ""
- CFG += -DCONFIG_VSYNC_OPENGL
- LIBS += -lGL
+ CFG += -DCONFIG_VSYNC_OPENGL
+ LIBS += -lGL
endif
-CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2
+# ifeq "$(NO_DBUS)" ""
+# CFG += -DCONFIG_DBUS
+# PACKAGES += dbus-1
+# endif
+
+# Version string
+COMPTON_VERSION ?= git-$(shell git describe --always)
+CFG += -DCOMPTON_VERSION="\"$(COMPTON_VERSION)\""
+
+LDFLAGS ?= -Wl,-O1 -Wl,--as-needed
+CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2 $(LDFLAGS)
CFLAGS += $(CFG)
LIBS += $(shell pkg-config --libs $(PACKAGES))
INCS += $(shell pkg-config --cflags $(PACKAGES))
+
CFLAGS += -Wall -std=c99
OBJS = compton.o
+.DEFAULT_GOAL := compton
+
%.o: src/%.c src/%.h
$(CC) $(CFLAGS) $(INCS) -c src/$*.c
@@ -75,4 +106,4 @@ uninstall:
clean:
@rm -f $(OBJS) compton
-.PHONY: uninstall clean
+.PHONY: uninstall clean docs
diff --git a/README.md b/README.md
index 5d77134..f2d3a8a 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ __R__ for runtime
* libdrm (B) (Will probably be made optional soon)
* libGL (B,R) (Will probably be made optional soon)
* asciidoc (B) (if you wish to run `make docs`)
+* libevent (B,R)
### How to build
diff --git a/src/compton.c b/src/compton.c
index eff195f..04a264e 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -1196,8 +1196,12 @@ paint_preprocess(session_t *ps, win *list) {
bool is_highest = true;
// Fading step calculation
- unsigned steps = (sub_unslong(get_time_ms(), ps->fade_time)
- + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta;
+ 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
+ ps->fade_time = get_time_ms();
+ steps = 0;
+ }
ps->fade_time += steps * ps->o.fade_delta;
XserverRegion last_reg_ignore = None;
@@ -2189,11 +2193,13 @@ static void
determine_shadow(session_t *ps, win *w) {
bool shadow_old = w->shadow;
- w->shadow = (ps->o.wintype_shadow[w->window_type]
- && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst)
- && !(ps->o.shadow_ignore_shaped && w->bounding_shaped
- && !w->rounded_corners)
- && !(ps->o.respect_prop_shadow && 0 == w->attr_shadow));
+ w->shadow = (UNSET == w->shadow_force ?
+ (ps->o.wintype_shadow[w->window_type]
+ && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst)
+ && !(ps->o.shadow_ignore_shaped && w->bounding_shaped
+ && !w->rounded_corners)
+ && !(ps->o.respect_prop_shadow && 0 == w->attr_shadow))
+ : w->shadow_force);
// Window extents need update on shadow state change
if (w->shadow != shadow_old) {
@@ -2340,18 +2346,35 @@ win_recheck_client(session_t *ps, win *w) {
win_mark_client(ps, w, cw);
}
-static void
+static bool
add_win(session_t *ps, Window id, Window prev) {
+ const static win win_def = {
+ .next = NULL,
+ .prev_trans = NULL,
+
+ .id = None,
+ .client_win = None,
+
+ .shadow_force = UNSET,
+ .focused_force = UNSET,
+ };
+
// Reject overlay window and already added windows
if (id == ps->overlay || find_win(ps, id)) {
- return;
+ return false;
}
+ // Allocate and initialize the new win structure
win *new = malloc(sizeof(win));
- win **p;
- if (!new) return;
+ if (!new) {
+ printf_errf("(%#010lx): Failed to allocate memory for the new window.", id);
+ return false;
+ }
+
+ memcpy(new, &win_def, sizeof(win));
+ win **p;
if (prev) {
for (p = &ps->list; *p; p = &(*p)->next) {
if ((*p)->id == prev && !(*p)->destroyed)
@@ -2365,8 +2388,10 @@ add_win(session_t *ps, Window id, Window prev) {
set_ignore_next(ps);
if (!XGetWindowAttributes(ps->dpy, id, &new->a)) {
+ // Failed to get window attributes. Which probably means, the window
+ // is gone already.
free(new);
- return;
+ return false;
}
// Delay window mapping
@@ -2439,7 +2464,6 @@ add_win(session_t *ps, Window id, Window prev) {
new->top_width = 0;
new->bottom_width = 0;
- new->client_win = 0;
new->wmwin = false;
new->flags = 0;
@@ -2452,6 +2476,8 @@ add_win(session_t *ps, Window id, Window prev) {
if (map_state == IsViewable) {
map_win(ps, id);
}
+
+ return true;
}
static void
@@ -2790,22 +2816,27 @@ static void
win_update_focused(session_t *ps, win *w) {
bool focused_old = w->focused;
- w->focused = w->focused_real;
-
- // Use wintype_focus, and treat WM windows and override-redirected
- // windows specially
- if (ps->o.wintype_focus[w->window_type]
- || (ps->o.mark_wmwin_focused && w->wmwin)
- || (ps->o.mark_ovredir_focused
- && w->id == w->client_win && !w->wmwin)
- || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst))
- w->focused = true;
-
- // If window grouping detection is enabled, mark the window active if
- // its group is
- if (ps->o.track_leader && ps->active_leader
- && win_get_leader(ps, w) == ps->active_leader) {
- w->focused = true;
+ if (UNSET != w->focused_force) {
+ w->focused = w->focused_force;
+ }
+ else {
+ w->focused = w->focused_real;
+
+ // Use wintype_focus, and treat WM windows and override-redirected
+ // windows specially
+ if (ps->o.wintype_focus[w->window_type]
+ || (ps->o.mark_wmwin_focused && w->wmwin)
+ || (ps->o.mark_ovredir_focused
+ && w->id == w->client_win && !w->wmwin)
+ || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst))
+ w->focused = true;
+
+ // If window grouping detection is enabled, mark the window active if
+ // its group is
+ if (ps->o.track_leader && ps->active_leader
+ && win_get_leader(ps, w) == ps->active_leader) {
+ w->focused = true;
+ }
}
if (w->focused != focused_old)
@@ -3649,7 +3680,7 @@ ev_handle(session_t *ps, XEvent *ev) {
static void
usage(void) {
const static char *usage_text =
- "compton (development version)\n"
+ "compton (" COMPTON_VERSION ")\n"
"usage: compton [options]\n"
"Options:\n"
"\n"
@@ -3888,15 +3919,16 @@ register_cm(session_t *ps, bool want_glxct) {
/**
* Fork program to background and disable all I/O streams.
*/
-static void
+static bool
fork_after(void) {
- if (getppid() == 1) return;
+ if (getppid() == 1)
+ return true;
int pid = fork();
- if (pid == -1) {
- fprintf(stderr, "Fork failed\n");
- return;
+ if (-1 == pid) {
+ printf_errf("(): fork() failed.");
+ return false;
}
if (pid > 0) _exit(0);
@@ -3906,11 +3938,17 @@ 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)
- fprintf(stderr, "fork_after(): freopen() failed.");
+ if (!success) {
+ printf_errf("(): freopen() failed.");
+ return false;
+ }
success = freopen("/dev/null", "w", stderr);
- if (!success)
- fprintf(stderr, "fork_after(): freopen() failed.");
+ if (!success) {
+ printf_errf("(): freopen() failed.");
+ return false;
+ }
+
+ return true;
}
#ifdef CONFIG_LIBCONFIG
@@ -4058,8 +4096,10 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
f = open_config_file(cpath, &path);
if (!f) {
- if (cpath)
- printf("Failed to read the specified configuration file.\n");
+ if (cpath) {
+ printf_err("Failed to read the specified configuration file \"%s\".", cpath);
+ exit(1);
+ }
return;
}
@@ -4223,8 +4263,9 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
*/
static void
get_cfg(session_t *ps, int argc, char *const *argv) {
- const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb";
+ const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb";
const static struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
{ "config", required_argument, NULL, 256 },
{ "shadow-red", required_argument, NULL, 257 },
{ "shadow-green", required_argument, NULL, 258 },
@@ -4255,6 +4296,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
{ "blur-background", no_argument, NULL, 283 },
{ "blur-background-frame", no_argument, NULL, 284 },
{ "blur-background-fixed", no_argument, NULL, 285 },
+ { "dbus", no_argument, NULL, 286 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@@ -4303,6 +4345,9 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
(o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
switch (o) {
// Short options
+ case 'h':
+ usage();
+ break;
case 'd':
ps->o.display = mstrcpy(optarg);
break;
@@ -4484,6 +4529,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
// --blur-background-fixed
ps->o.blur_background_fixed = true;
break;
+ case 286:
+ // --dbus
+ ps->o.dbus = true;
+ break;
default:
usage();
}
@@ -4609,7 +4658,7 @@ update_refresh_rate(session_t *ps) {
XRRFreeScreenConfigInfo(randr_info);
if (ps->refresh_rate)
- ps->refresh_intv = NS_PER_SEC / ps->refresh_rate;
+ ps->refresh_intv = US_PER_SEC / ps->refresh_rate;
else
ps->refresh_intv = 0;
}
@@ -4625,7 +4674,7 @@ sw_opti_init(session_t *ps) {
// Check if user provides one
ps->refresh_rate = ps->o.refresh_rate;
if (ps->refresh_rate)
- ps->refresh_intv = NS_PER_SEC / ps->refresh_rate;
+ ps->refresh_intv = US_PER_SEC / ps->refresh_rate;
// Auto-detect refresh rate otherwise
if (!ps->refresh_rate && ps->randr_exists) {
@@ -4670,52 +4719,49 @@ lceil_ntimes(long dividend, long divisor) {
* @return > 0 if we get some events, 0 if timeout is reached, < 0 on
* problems
*/
-static int
-evpoll(session_t *ps, int timeout) {
- struct pollfd ufd = {
- .fd = ConnectionNumber(ps->dpy),
- .events = POLLIN
- };
-
- // Always wait infinitely if asked so, to minimize CPU usage
- if (timeout < 0) {
- int ret = poll(&ufd, 1, timeout);
- // Reset ps->fade_time so the fading steps during idling are not counted
- ps->fade_time = get_time_ms();
- return ret;
- }
-
- // Just do a poll() if we are not using optimization
- if (!ps->o.sw_opti)
- return poll(&ufd, 1, timeout);
-
- // Convert the old timeout to struct timespec
- struct timespec next_paint_tmout = {
- .tv_sec = timeout / MS_PER_SEC,
- .tv_nsec = timeout % MS_PER_SEC * (NS_PER_SEC / MS_PER_SEC)
- };
+static void
+swopti_handle_timeout(session_t *ps, struct timeval *ptv) {
+ if (!ptv)
+ return;
- // Get the nanosecond offset of the time when the we reach the timeout
+ // Get the microsecond offset of the time when the we reach the timeout
// I don't think a 32-bit long could overflow here.
- long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - ps->paint_tm_offset) % NS_PER_SEC;
+ long target_relative_offset = (ptv->tv_usec + get_time_timeval().tv_usec - ps->paint_tm_offset) % US_PER_SEC;
if (target_relative_offset < 0)
- target_relative_offset += NS_PER_SEC;
+ target_relative_offset += US_PER_SEC;
assert(target_relative_offset >= 0);
// If the target time is sufficiently close to a refresh time, don't add
// an offset, to avoid certain blocking conditions.
- if ((target_relative_offset % NS_PER_SEC) < SW_OPTI_TOLERANCE)
- return poll(&ufd, 1, timeout);
+ if ((target_relative_offset % US_PER_SEC) < SW_OPTI_TOLERANCE)
+ return;
// Add an offset so we wait until the next refresh after timeout
- next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, ps->refresh_intv) - target_relative_offset;
- if (next_paint_tmout.tv_nsec > NS_PER_SEC) {
- next_paint_tmout.tv_nsec -= NS_PER_SEC;
- ++next_paint_tmout.tv_sec;
+ ptv->tv_usec += lceil_ntimes(target_relative_offset, ps->refresh_intv) - target_relative_offset;
+ if (ptv->tv_usec > US_PER_SEC) {
+ ptv->tv_usec -= US_PER_SEC;
+ ++ptv->tv_sec;
}
+}
- return ppoll(&ufd, 1, &next_paint_tmout, NULL);
+/**
+ * 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;
+ }
}
/**
@@ -4982,6 +5028,42 @@ redir_stop(session_t *ps) {
}
/**
+ * Main loop.
+ */
+static bool
+mainloop(session_t *ps) {
+ bool infinite_wait = false;
+
+ // Process existing events
+ if (XEventsQueued(ps->dpy, QueuedAfterReading)) {
+ evcallback_x(ConnectionNumber(ps->dpy), 0, NULL);
+ 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);
+ if (timeval_isempty(tv))
+ 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.");
+
+ if (infinite_wait)
+ ps->fade_time = get_time_ms();
+
+ return true;
+}
+
+/**
* Initialize a session.
*
* @param ps_old old session, from which the function will take the X
@@ -5097,11 +5179,15 @@ session_init(session_t *ps_old, int argc, char **argv) {
.refresh_intv = 0UL,
.paint_tm_offset = 0L,
+#ifdef CONFIG_VSYNC_DRM
.drm_fd = 0,
+#endif
+#ifdef CONFIG_VSYNC_OPENGL
.glx_context = None,
.glx_get_video_sync = NULL,
.glx_wait_video_sync = NULL,
+#endif
.xfixes_event = 0,
.xfixes_error = 0,
@@ -5119,9 +5205,11 @@ session_init(session_t *ps_old, int argc, char **argv) {
.randr_exists = 0,
.randr_event = 0,
.randr_error = 0,
+#ifdef CONFIG_VSYNC_OPENGL
.glx_exists = false,
.glx_event = 0,
.glx_error = 0,
+#endif
.dbe_exists = false,
.xrfilter_convolution_exists = false,
@@ -5269,8 +5357,6 @@ session_init(session_t *ps_old, int argc, char **argv) {
if (ps->o.dbe)
init_dbe(ps);
- if (ps->o.fork_after_register) fork_after();
-
init_atoms(ps);
init_alpha_picts(ps);
@@ -5313,6 +5399,24 @@ session_init(session_t *ps_old, int argc, char **argv) {
}
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, NULL, NULL)))
+ printf_errfq(1, "(): Failed to build event.");
+
XGrabServer(ps->dpy);
redir_start(ps);
@@ -5345,6 +5449,18 @@ session_init(session_t *ps_old, int argc, char **argv) {
XUngrabServer(ps->dpy);
+ // Fork to background, if asked
+ if (ps->o.fork_after_register) {
+ if (!fork_after()) {
+ 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
if (ps_old)
free(ps_old);
@@ -5447,10 +5563,13 @@ session_destroy(session_t *ps) {
XDestroyWindow(ps->dpy, ps->reg_win);
ps->reg_win = None;
}
+
+#ifdef CONFIG_VSYNC_OPENGL
if (ps->glx_context) {
glXDestroyContext(ps->dpy, ps->glx_context);
ps->glx_context = None;
}
+#endif
// Free double buffer
if (ps->root_dbe) {
@@ -5458,11 +5577,13 @@ session_destroy(session_t *ps) {
ps->root_dbe = None;
}
+#ifdef CONFIG_VSYNC_DRM
// Close file opened for DRM VSync
if (ps->drm_fd) {
close(ps->drm_fd);
ps->drm_fd = 0;
}
+#endif
// Release overlay window
if (ps->overlay) {
@@ -5470,6 +5591,11 @@ 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);
@@ -5487,7 +5613,7 @@ session_run(session_t *ps) {
win *t;
if (ps->o.sw_opti)
- ps->paint_tm_offset = get_time_timespec().tv_nsec;
+ ps->paint_tm_offset = get_time_timeval().tv_usec;
ps->reg_ignore_expire = true;
@@ -5503,22 +5629,10 @@ session_run(session_t *ps) {
// Main loop
while (!ps->reset) {
- bool ev_received = false;
-
- while (XEventsQueued(ps->dpy, QueuedAfterReading)
- || (evpoll(ps,
- (ev_received ? 0: (ps->idling ? -1: fade_timeout(ps)))) > 0)) {
- // 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);
- ev_received = true;
- }
- }
+ ps->ev_received = false;
+
+ while (mainloop(ps))
+ continue;
// idling will be turned off during paint_preprocess() if needed
ps->idling = true;
@@ -5577,6 +5691,10 @@ main(int argc, char **argv) {
session_t *ps_old = ps_g;
while (1) {
ps_g = session_init(ps_old, argc, argv);
+ if (!ps_g) {
+ printf_errf("Failed to create new session.");
+ return 1;
+ }
session_run(ps_g);
ps_old = ps_g;
session_destroy(ps_g);
diff --git a/src/compton.h b/src/compton.h
index 66ed396..4db984a 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -54,6 +54,21 @@
#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>
@@ -72,6 +87,10 @@
#include <libconfig.h>
#endif
+#ifdef CONFIG_DBUS
+#include <dbus/dbus.h>
+#endif
+
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
@@ -112,6 +131,8 @@
#define SW_OPTI_TOLERANCE 1000
#define WIN_GET_LEADER_MAX_RECURSION 20
+#define SEC_WRAP (15L * 24L * 60L * 60L)
+
#define NS_PER_SEC 1000000000L
#define US_PER_SEC 1000000L
#define MS_PER_SEC 1000
@@ -134,10 +155,27 @@
// #define MSTR_(s) #s
// #define MSTR(s) MSTR_(s)
+/// Print out an error message.
+#define printf_err(format, ...) \
+ fprintf(stderr, format "\n", ## __VA_ARGS__)
+
+/// Print out an error message with function name.
+#define printf_errf(format, ...) \
+ printf_err("%s" format, __func__, ## __VA_ARGS__)
+
+/// Print out an error message with function name, and quit with a
+/// specific exit code.
+#define printf_errfq(code, format, ...) { \
+ printf_err("%s" format, __func__, ## __VA_ARGS__); \
+ exit(code); \
+}
+
+/// Print out a debug message.
#define printf_dbg(format, ...) \
printf(format, ## __VA_ARGS__); \
fflush(stdout)
+/// Print out a debug message with function name.
#define printf_dbgf(format, ...) \
printf_dbg("%s" format, __func__, ## __VA_ARGS__)
@@ -148,6 +186,7 @@
// === Types ===
typedef uint32_t opacity_t;
+typedef long time_ms_t;
typedef enum {
WINTYPE_UNKNOWN,
@@ -190,6 +229,8 @@ typedef struct {
long *p32;
} data;
unsigned long nitems;
+ Atom type;
+ int format;
} winprop_t;
typedef struct _ignore {
@@ -263,6 +304,8 @@ typedef struct {
/// Whether to unredirect all windows if a full-screen opaque window
/// is detected.
bool unredir_if_possible;
+ /// Whether to enable D-Bus support.
+ bool dbus;
/// Whether to work under synchronized mode for debugging.
bool synchronize;
@@ -301,7 +344,8 @@ typedef struct {
opacity_t fade_in_step;
/// How much to fade out in a single fading step.
opacity_t fade_out_step;
- unsigned long fade_delta;
+ /// Fading time delta. In milliseconds.
+ time_ms_t fade_delta;
/// Whether to disable fading on window open/close.
bool no_fading_openclose;
/// Fading blacklist. A linked list of conditions.
@@ -401,6 +445,17 @@ 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;
+ /// Whether we have received an event in this cycle.
+ bool ev_received;
+ /// Whether the program is idling. I.e. no fading, no potential window
+ /// changes.
+ bool idling;
/// Program start time.
struct timeval time_start;
/// The region needs to painted on next paint.
@@ -414,11 +469,8 @@ typedef struct {
Picture *alpha_picts;
/// Whether all reg_ignore of windows should expire in this paint.
bool reg_ignore_expire;
- /// Whether the program is idling. I.e. no fading, no potential window
- /// changes.
- bool idling;
/// Time of last fading. In milliseconds.
- unsigned long fade_time;
+ time_ms_t fade_time;
/// Head pointer of the error ignore linked list.
ignore_t *ignore_head;
/// Pointer to the <code>next</code> member of tail element of the error
@@ -471,13 +523,13 @@ typedef struct {
/// Nanosecond offset of the first painting.
long paint_tm_offset;
- #ifdef CONFIG_VSYNC_DRM
+#ifdef CONFIG_VSYNC_DRM
// === DRM VSync related ===
/// File descriptor of DRI device file. Used for DRM VSync.
int drm_fd;
- #endif
+#endif
- #ifdef CONFIG_VSYNC_OPENGL
+#ifdef CONFIG_VSYNC_OPENGL
// === OpenGL VSync related ===
/// GLX context.
GLXContext glx_context;
@@ -485,7 +537,7 @@ typedef struct {
f_GetVideoSync glx_get_video_sync;
/// Pointer to glXWaitVideoSyncSGI function.
f_WaitVideoSync glx_wait_video_sync;
- #endif
+#endif
// === X extension related ===
/// Event base number for X Fixes extension.
@@ -521,14 +573,14 @@ typedef struct {
int randr_event;
/// Error base number for X RandR extension.
int randr_error;
- #ifdef CONFIG_VSYNC_OPENGL
+#ifdef CONFIG_VSYNC_OPENGL
/// Whether X GLX extension exists.
bool glx_exists;
/// Event base number for X GLX extension.
int glx_event;
/// Error base number for X GLX extension.
int glx_error;
- #endif
+#endif
/// Whether X DBE extension exists.
bool dbe_exists;
/// Whether X Render convolution filter exists.
@@ -565,9 +617,11 @@ typedef struct {
} session_t;
/// Structure representing a top-level window compton manages.
-typedef struct _win {
+typedef struct __attribute__((__packed__)) _win {
// Next structure in the linked list.
struct _win *next;
+ // Pointer to the next higher window to paint.
+ struct _win *prev_trans;
// ID of the top-level frame window.
Window id;
@@ -587,10 +641,15 @@ typedef struct _win {
XserverRegion extents;
// Type of the window.
wintype_t window_type;
+
+ // Focus-related members
/// Whether the window is to be considered focused.
bool focused;
+ /// Override value of window focus state. Set by D-Bus method calls.
+ switch_t focused_force;
/// Whether the window is actually focused.
bool focused_real;
+
/// Leader window ID of the window.
Window leader;
/// Cached topmost window ID of the window.
@@ -649,8 +708,10 @@ typedef struct _win {
unsigned int left_width, right_width, top_width, bottom_width;
// Shadow-related members
- /// Whether a window has shadow. Affected by window type.
+ /// Whether a window has shadow. Calculated.
bool shadow;
+ /// Override value of window shadow state. Set by D-Bus method calls.
+ switch_t shadow_force;
/// Opacity of the shadow. Affected by window opacity and frame opacity.
double shadow_opacity;
/// X offset of shadow. Affected by commandline argument.
@@ -691,8 +752,6 @@ typedef struct _win {
/// opacity state, window geometry, window mapped/unmapped state,
/// window mode, of this and all higher windows.
XserverRegion reg_ignore;
-
- struct _win *prev_trans;
} win;
/// Temporary structure used for communication between
@@ -773,6 +832,22 @@ 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
@@ -784,6 +859,9 @@ discard_ignore(session_t *ps, unsigned long sequence);
static void
set_ignore(session_t *ps, unsigned long sequence);
+/**
+ * Ignore X errors caused by next X request.
+ */
static inline void
set_ignore_next(session_t *ps) {
set_ignore(ps, NextRequest(ps->dpy));
@@ -793,6 +871,14 @@ static int
should_ignore(session_t *ps, unsigned long sequence);
/**
+ * Wrapper of XInternAtom() for convience.
+ */
+static inline Atom
+get_atom(session_t *ps, char *atom_name) {
+ return XInternAtom(ps->dpy, atom_name, False);
+}
+
+/**
* Return the painting target window.
*/
static inline Window
@@ -957,6 +1043,14 @@ array_wid_exists(const Window *arr, int count, Window wid) {
return false;
}
+/**
+ * Return whether a struct timeval value is empty.
+ */
+static inline bool
+timeval_isempty(struct timeval tv) {
+ return !(tv.tv_sec && tv.tv_usec);
+}
+
/*
* Subtracting two struct timeval values.
*
@@ -1028,6 +1122,19 @@ timespec_subtract(struct timespec *result,
/**
* Get current time in struct timespec.
+ */
+static inline struct timeval __attribute__((const))
+get_time_timeval(void) {
+ struct timeval tv = { 0, 0 };
+
+ gettimeofday(&tv, NULL);
+
+ // Return a time of all 0 if the call fails
+ return tv;
+}
+
+/**
+ * Get current time in struct timespec.
*
* Note its starting time is unspecified.
*/
@@ -1154,18 +1261,25 @@ free_win_res(session_t *ps, win *w) {
/**
* Get current system clock in milliseconds.
- *
- * The return type must be unsigned long because so many milliseconds have
- * passed since the epoch.
*/
-static unsigned long
+static time_ms_t
get_time_ms(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
- return (unsigned long) tv.tv_sec * 1000
- + (unsigned long) tv.tv_usec / 1000;
+ return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000;
+}
+
+/**
+ * Convert time from milliseconds to struct timeval.
+ */
+static inline struct timeval
+ms_to_tv(int timeout) {
+ return (struct timeval) {
+ .tv_sec = timeout / MS_PER_SEC,
+ .tv_usec = timeout % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC)
+ };
}
static int
@@ -1255,34 +1369,66 @@ wid_has_prop(const session_t *ps, Window w, Atom atom) {
* @return a <code>winprop_t</code> structure containing the attribute
* and number of items. A blank one on failure.
*/
+// TODO: Move to compton.c
static winprop_t
-wid_get_prop(const session_t *ps, Window w, Atom atom, long length,
- Atom rtype, int rformat) {
+wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset,
+ long length, Atom rtype, int rformat) {
Atom type = None;
int format = 0;
unsigned long nitems = 0, after = 0;
unsigned char *data = NULL;
- // Use two if statements to deal with the sequence point issue.
- if (Success == XGetWindowProperty(ps->dpy, w, atom, 0L, length, False,
- rtype, &type, &format, &nitems, &after, &data)) {
- if (type == rtype && format == rformat) {
+ if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length,
+ False, rtype, &type, &format, &nitems, &after, &data)
+ && nitems && (AnyPropertyType == type || type == rtype)
+ && (!format || format == rformat)
+ && (8 == format || 16 == format || 32 == format)) {
return (winprop_t) {
.data.p8 = data,
- .nitems = nitems
+ .nitems = nitems,
+ .type = type,
+ .format = format,
};
- }
}
XFree(data);
return (winprop_t) {
.data.p8 = NULL,
- .nitems = 0
+ .nitems = 0,
+ .type = type,
+ .format = format
};
}
/**
+ * Wrapper of wid_get_prop_adv().
+ */
+static inline winprop_t
+wid_get_prop(const session_t *ps, Window wid, Atom atom, long length,
+ Atom rtype, int rformat) {
+ return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat);
+}
+
+static inline
+long winprop_get_int(winprop_t prop) {
+ long tgt = 0;
+
+ if (!prop.nitems)
+ return 0;
+
+ switch (prop.format) {
+ case 8: tgt = *(prop.data.p8); break;
+ case 16: tgt = *(prop.data.p16); break;
+ case 32: tgt = *(prop.data.p32); break;
+ default: assert(0);
+ break;
+ }
+
+ return tgt;
+}
+
+/**
* Free a <code>winprop_t</code>.
*
* @param pprop pointer to the <code>winprop_t</code> to free.
@@ -1639,7 +1785,7 @@ win_unmark_client(session_t *ps, win *w);
static void
win_recheck_client(session_t *ps, win *w);
-static void
+static bool
add_win(session_t *ps, Window id, Window prev);
static void
@@ -1855,7 +2001,7 @@ ev_window_name(session_t *ps, Window wid, char **name);
inline static void
ev_handle(session_t *ps, XEvent *ev);
-static void
+static bool
fork_after(void);
#ifdef CONFIG_LIBCONFIG
@@ -1918,8 +2064,11 @@ update_refresh_rate(session_t *ps);
static bool
sw_opti_init(session_t *ps);
-static int
-evpoll(session_t *ps, int timeout);
+static void
+swopti_handle_timeout(session_t *ps, struct timeval *ptv);
+
+static void
+evcallback_x(evutil_socket_t fd, short what, void *arg);
static bool
vsync_drm_init(session_t *ps);
@@ -1955,6 +2104,9 @@ redir_start(session_t *ps);
static void
redir_stop(session_t *ps);
+static bool
+mainloop(session_t *ps);
+
static session_t *
session_init(session_t *ps_old, int argc, char **argv);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment