Created
January 2, 2013 13:06
-
-
Save richardgv/4434479 to your computer and use it in GitHub Desktop.
chjj/compton: Patch to use libevent for the main loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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