Use libev for inputs and timeouts

Bugs:

* There seems to be a noticeable frame loss when window
  is being opened/closed. But the same problem also exists
  in master/next, so this is not a regression.
* Using --sw-opti and --benchmark at the same time causing
  compton to draw more frequently than permitted by the
  arguments. That is because the sleep interval calculation
  is flawed. Not really a regression either.

Verified still working:

* Usual painting
* Fade
* Benchmark mode
* --sw-opti (with or without benchmark mode)
* DBus
* Unredir delay

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2018-10-02 20:22:26 +01:00
parent e58de49d7a
commit 2a4fed50f4
6 changed files with 492 additions and 684 deletions

View File

@ -10,7 +10,7 @@ APPDIR ?= $(PREFIX)/share/applications
ICODIR ?= $(PREFIX)/share/icons/hicolor/ ICODIR ?= $(PREFIX)/share/icons/hicolor/
PACKAGES = x11 x11-xcb xcb-renderutil xcb-render xcb-damage xcb-randr xcb-composite xcb-shape xcb-image xcb-xfixes xext pixman-1 PACKAGES = x11 x11-xcb xcb-renderutil xcb-render xcb-damage xcb-randr xcb-composite xcb-shape xcb-image xcb-xfixes xext pixman-1
LIBS = -lm -lrt LIBS = -lm -lrt -lev
INCS = INCS =
OBJS = compton.o config.o win.o x.o OBJS = compton.o config.o win.o x.o

View File

@ -94,6 +94,7 @@
#ifdef CONFIG_XINERAMA #ifdef CONFIG_XINERAMA
#include <xcb/xinerama.h> #include <xcb/xinerama.h>
#endif #endif
#include <ev.h>
#include <pixman.h> #include <pixman.h>
// libdbus // libdbus
@ -180,7 +181,6 @@
#define TIME_MS_MAX LONG_MAX #define TIME_MS_MAX LONG_MAX
#define FADE_DELTA_TOLERANCE 0.2 #define FADE_DELTA_TOLERANCE 0.2
#define SWOPTI_TOLERANCE 3000 #define SWOPTI_TOLERANCE 3000
#define TIMEOUT_RUN_TOLERANCE 0.05
#define WIN_GET_LEADER_MAX_RECURSION 20 #define WIN_GET_LEADER_MAX_RECURSION 20
#define SEC_WRAP (15L * 24L * 60L * 60L) #define SEC_WRAP (15L * 24L * 60L * 60L)
@ -504,9 +504,10 @@ typedef struct _latom {
#define REG_DATA_INIT { NULL, 0 } #define REG_DATA_INIT { NULL, 0 }
struct _timeout_t;
typedef struct win win; typedef struct win win;
typedef struct ev_session_timer ev_session_timer;
typedef struct ev_session_idle ev_session_idle;
typedef struct ev_session_prepare ev_session_prepare;
/// Structure representing all options. /// Structure representing all options.
typedef struct options_t { typedef struct options_t {
@ -764,6 +765,10 @@ typedef struct {
/// Structure containing all necessary data for a compton session. /// Structure containing all necessary data for a compton session.
typedef struct session { typedef struct session {
/// ev_io for X connection
ev_io xiow;
/// libev mainloop
struct ev_loop *loop;
// === Display related === // === Display related ===
/// Display in use. /// Display in use.
Display *dpy; Display *dpy;
@ -813,25 +818,27 @@ typedef struct session {
// === Operation related === // === Operation related ===
/// Program options. /// Program options.
options_t o; options_t o;
/// 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;
/// Timeout for delayed unredirection. /// Timeout for delayed unredirection.
struct _timeout_t *tmout_unredir; ev_session_timer *unredir_timer;
/// Timer for fading
ev_session_timer *fade_timer;
/// Timer for delayed drawing, right now only used by
/// swopti
ev_session_timer *delayed_draw_timer;
/// Use an ev_idle callback for drawing
/// So we only start drawing when events are processed
ev_session_idle *draw_idle;
/// Called everytime we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_session_prepare *event_check;
/// Whether we have hit unredirection timeout. /// Whether we have hit unredirection timeout.
bool tmout_unredir_hit; bool tmout_unredir_hit;
/// Whether we have received an event in this cycle. /// Whether we need to redraw the screen
bool ev_received; bool redraw_needed;
/// Whether the program is idling. I.e. no fading, no potential window /// Whether the program is idling. I.e. no fading, no potential window
/// changes. /// changes.
bool idling; bool fade_running;
/// Program start time. /// Program start time.
struct timeval time_start; struct timeval time_start;
/// The region needs to painted on next paint. /// The region needs to painted on next paint.
@ -853,6 +860,8 @@ typedef struct session {
xcb_render_fixed_t *blur_kerns_cache[MAX_BLUR_PASS]; xcb_render_fixed_t *blur_kerns_cache[MAX_BLUR_PASS];
/// Reset program after next paint. /// Reset program after next paint.
bool reset; bool reset;
/// If compton should quit
bool quit;
// === Expose event related === // === Expose event related ===
/// Pointer to an array of <code>XRectangle</code>-s of exposed region. /// Pointer to an array of <code>XRectangle</code>-s of exposed region.
@ -1206,17 +1215,6 @@ struct options_tmp {
double menu_opacity; 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. /// Enumeration for window event hints.
typedef enum { typedef enum {
WIN_EVMODE_UNKNOWN, WIN_EVMODE_UNKNOWN,
@ -1453,7 +1451,7 @@ print_timestamp(session_t *ps) {
if (gettimeofday(&tm, NULL)) return; if (gettimeofday(&tm, NULL)) return;
timeval_subtract(&diff, &tm, &ps->time_start); timeval_subtract(&diff, &tm, &ps->time_start);
fprintf(stderr, "[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); fprintf(stderr, "[ %5ld.%06ld ] ", diff.tv_sec, diff.tv_usec);
} }
/** /**
@ -1684,68 +1682,6 @@ parse_glx_swap_method(session_t *ps, const char *str) {
return true; return true;
} }
timeout_t *
timeout_insert(session_t *ps, time_ms_t interval,
bool (*callback)(session_t *ps, timeout_t *ptmout), void *data);
void
timeout_invoke(session_t *ps, timeout_t *ptmout);
bool
timeout_drop(session_t *ps, timeout_t *prm);
void
timeout_reset(session_t *ps, timeout_t *ptmout);
/**
* Add a file descriptor to a select() fd_set.
*/
static inline void
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");
abort();
}
}
FD_SET(fd, *ppfds);
}
/**
* Add a new file descriptor to wait for.
*/
static inline void
fds_insert(session_t *ps, int fd, short events) {
ps->nfds_max = max_i(fd + 1, ps->nfds_max);
if (POLLIN & events)
fds_insert_select(&ps->pfds_read, fd);
if (POLLOUT & events)
fds_insert_select(&ps->pfds_write, fd);
if (POLLPRI & events)
fds_insert_select(&ps->pfds_except, fd);
}
/**
* 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 && ps->pfds_read)
FD_CLR(fd, ps->pfds_read);
if (POLLOUT & events && ps->pfds_write)
FD_CLR(fd, ps->pfds_write);
if (POLLPRI & events && ps->pfds_except)
FD_CLR(fd, ps->pfds_except);
}
/** /**
* Wrapper of XFree() for convenience. * Wrapper of XFree() for convenience.
* *

View File

@ -15,6 +15,8 @@
#include <xcb/render.h> #include <xcb/render.h>
#include <xcb/xcb_image.h> #include <xcb/xcb_image.h>
#include <ev.h>
#include "compton.h" #include "compton.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "opengl.h" #include "opengl.h"
@ -38,9 +40,6 @@ update_refresh_rate(session_t *ps);
static bool static bool
swopti_init(session_t *ps); swopti_init(session_t *ps);
static void
swopti_handle_timeout(session_t *ps, struct timeval *ptv);
static void static void
cxinerama_upd_scrs(session_t *ps); cxinerama_upd_scrs(session_t *ps);
@ -113,6 +112,24 @@ restack_win(session_t *ps, win *w, Window new_above);
static void static void
update_ewmh_active_win(session_t *ps); update_ewmh_active_win(session_t *ps);
static void
draw_callback(EV_P_ ev_idle *w, int revents);
typedef struct ev_session_timer {
ev_timer w;
session_t *ps;
} ev_session_timer;
typedef struct ev_session_idle {
ev_idle w;
session_t *ps;
} ev_session_idle;
typedef struct ev_session_prepare {
ev_prepare w;
session_t *ps;
} ev_session_prepare;
// === Global constants === // === Global constants ===
/// Name strings for window types. /// Name strings for window types.
@ -214,6 +231,13 @@ set_tgt_clip(session_t *ps, region_t *reg) {
} }
} }
void queue_redraw(session_t *ps) {
// If --benchmark is used, redraw is always queued
if (!ps->redraw_needed && !ps->o.benchmark)
ev_idle_start(ps->loop, &ps->draw_idle->w);
ps->redraw_needed = true;
}
static inline void static inline void
paint_region(session_t *ps, win *w, int x, int y, int wid, int hei, paint_region(session_t *ps, win *w, int x, int y, int wid, int hei,
double opacity, const region_t *reg_paint, xcb_render_picture_t pict) { double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
@ -281,40 +305,6 @@ void add_damage(session_t *ps, const region_t *damage) {
pixman_region32_union(&ps->all_damage, &ps->all_damage, (region_t *)damage); pixman_region32_union(&ps->all_damage, &ps->all_damage, (region_t *)damage);
} }
#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
// === Fading === // === Fading ===
/** /**
@ -322,13 +312,13 @@ fds_poll(session_t *ps, struct timeval *ptv) {
* *
* In milliseconds. * In milliseconds.
*/ */
static int static double
fade_timeout(session_t *ps) { fade_timeout(session_t *ps) {
int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time; int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time;
diff = normalize_i_range(diff, 0, ps->o.fade_delta * 2); diff = normalize_i_range(diff, 0, ps->o.fade_delta * 2);
return diff; return diff / 1000.0;
} }
/** /**
@ -359,7 +349,7 @@ run_fade(session_t *ps, win *w, unsigned steps) {
} }
if (w->opacity != w->opacity_tgt) { if (w->opacity != w->opacity_tgt) {
ps->idling = false; ps->fade_running = true;
} }
} }
@ -367,6 +357,9 @@ run_fade(session_t *ps, win *w, unsigned steps) {
* Set fade callback of a window, and possibly execute the previous * Set fade callback of a window, and possibly execute the previous
* callback. * callback.
* *
* If a callback can cause rendering result to change, it should call
* `queue_redraw`.
*
* @param exec_callback whether the previous callback is to be executed * @param exec_callback whether the previous callback is to be executed
*/ */
void set_fade_callback(session_t *ps, win **_w, void set_fade_callback(session_t *ps, win **_w,
@ -376,12 +369,8 @@ void set_fade_callback(session_t *ps, win **_w,
w->fade_callback = callback; w->fade_callback = callback;
// Must be the last line as the callback could destroy w! // Must be the last line as the callback could destroy w!
if (exec_callback && old_callback) { if (exec_callback && old_callback)
old_callback(ps, _w); old_callback(ps, _w);
// Although currently no callback function affects window state on
// next paint, it could, in the future
ps->idling = false;
}
} }
/** /**
@ -1228,14 +1217,15 @@ paint_preprocess(session_t *ps, win *list) {
if (ps->redirected) { if (ps->redirected) {
if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit)
redir_stop(ps); redir_stop(ps);
else if (!ps->tmout_unredir->enabled) { else if (!ev_is_active(&ps->unredir_timer->w)) {
timeout_reset(ps, ps->tmout_unredir); ev_timer_set(&ps->unredir_timer->w,
ps->tmout_unredir->enabled = true; ps->o.unredir_if_possible_delay / 1000.0, 0);
ev_timer_start(ps->loop, &ps->unredir_timer->w);
} }
} }
} }
else { else {
ps->tmout_unredir->enabled = false; ev_timer_stop(ps->loop, &ps->unredir_timer->w);
redir_start(ps); redir_start(ps);
} }
@ -2466,7 +2456,7 @@ expose_root(session_t *ps, const rect_t *rects, int nrects) {
void void
force_repaint(session_t *ps) { force_repaint(session_t *ps) {
assert(pixman_region32_not_empty(&ps->screen_reg)); assert(pixman_region32_not_empty(&ps->screen_reg));
ps->ev_received = true; queue_redraw(ps);
add_damage(ps, &ps->screen_reg); add_damage(ps, &ps->screen_reg);
} }
@ -2483,7 +2473,7 @@ win_set_shadow_force(session_t *ps, win *w, switch_t val) {
if (val != w->shadow_force) { if (val != w->shadow_force) {
w->shadow_force = val; w->shadow_force = val;
win_determine_shadow(ps, w); win_determine_shadow(ps, w);
ps->ev_received = true; queue_redraw(ps);
} }
} }
@ -2495,7 +2485,7 @@ win_set_fade_force(session_t *ps, win *w, switch_t val) {
if (val != w->fade_force) { if (val != w->fade_force) {
w->fade_force = val; w->fade_force = val;
win_determine_fade(ps, w); win_determine_fade(ps, w);
ps->ev_received = true; queue_redraw(ps);
} }
} }
@ -2507,7 +2497,7 @@ win_set_focused_force(session_t *ps, win *w, switch_t val) {
if (val != w->focused_force) { if (val != w->focused_force) {
w->focused_force = val; w->focused_force = val;
win_update_focused(ps, w); win_update_focused(ps, w);
ps->ev_received = true; queue_redraw(ps);
} }
} }
@ -2519,7 +2509,7 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val) {
if (val != w->invert_color_force) { if (val != w->invert_color_force) {
w->invert_color_force = val; w->invert_color_force = val;
win_determine_invert_color(ps, w); win_determine_invert_color(ps, w);
ps->ev_received = true; queue_redraw(ps);
} }
} }
@ -2555,7 +2545,7 @@ opts_set_no_fading_openclose(session_t *ps, bool newval) {
ps->o.no_fading_openclose = newval; ps->o.no_fading_openclose = newval;
for (win *w = ps->list; w; w = w->next) for (win *w = ps->list; w; w = w->next)
win_determine_fade(ps, w); win_determine_fade(ps, w);
ps->ev_received = true; queue_redraw(ps);
} }
} }
@ -3075,6 +3065,9 @@ ev_handle(session_t *ps, xcb_generic_event_t *ev) {
} }
#endif #endif
// XXX redraw needs to be more fine grained
queue_redraw(ps);
switch (ev->response_type) { switch (ev->response_type) {
case FocusIn: case FocusIn:
ev_focus_in(ps, (xcb_focus_in_event_t *)ev); ev_focus_in(ps, (xcb_focus_in_event_t *)ev);
@ -4215,31 +4208,22 @@ swopti_init(session_t *ps) {
* @param ps current session * @param ps current session
* @param[in,out] ptv pointer to the timeout * @param[in,out] ptv pointer to the timeout
*/ */
static void static double
swopti_handle_timeout(session_t *ps, struct timeval *ptv) { swopti_handle_timeout(session_t *ps) {
if (!ptv)
return;
// Get the microsecond 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. // I don't think a 32-bit long could overflow here.
long offset = (ptv->tv_usec + get_time_timeval().tv_usec - ps->paint_tm_offset) % ps->refresh_intv; long offset = (get_time_timeval().tv_usec - ps->paint_tm_offset) % ps->refresh_intv;
if (offset < 0) if (offset < 0)
offset += ps->refresh_intv; offset += ps->refresh_intv;
assert(offset >= 0 && offset < ps->refresh_intv);
// If the target time is sufficiently close to a refresh time, don't add // If the target time is sufficiently close to a refresh time, don't add
// an offset, to avoid certain blocking conditions. // an offset, to avoid certain blocking conditions.
if (offset < SWOPTI_TOLERANCE if (offset < SWOPTI_TOLERANCE
|| offset > ps->refresh_intv - SWOPTI_TOLERANCE) || offset > ps->refresh_intv - SWOPTI_TOLERANCE)
return; return 0;
// Add an offset so we wait until the next refresh after timeout // Add an offset so we wait until the next refresh after timeout
ptv->tv_usec += ps->refresh_intv - offset; return (ps->refresh_intv - offset) / 1e6;
if (ptv->tv_usec > US_PER_SEC) {
ptv->tv_usec -= US_PER_SEC;
++ptv->tv_sec;
}
} }
/** /**
@ -4663,150 +4647,6 @@ 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) {
time_ms_t newrun = timeout_get_newrun(ptmout);
if (newrun <= now) {
wait = 0;
break;
}
else {
time_ms_t newwait = newrun - now;
if (newwait < wait)
wait = newwait;
}
}
}
return wait;
}
/**
* Insert a new timeout.
*/
timeout_t *
timeout_insert(session_t *ps, time_ms_t interval,
bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) {
static const 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
*/
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;
timeout_t *pnext = NULL;
for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = pnext) {
pnext = ptmout->next;
if (ptmout->enabled) {
const time_ms_t max = now +
(time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE);
time_ms_t newrun = timeout_get_newrun(ptmout);
if (newrun <= max) {
ret = true;
timeout_invoke(ps, ptmout);
}
}
}
return ret;
}
/**
* Invoke a timeout.
*/
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);
}
/**
* Reset a timeout to initial state.
*/
void
timeout_reset(session_t *ps, timeout_t *ptmout) {
ptmout->firstrun = ptmout->lastrun = get_time_ms();
}
/** /**
* Unredirect all windows. * Unredirect all windows.
*/ */
@ -4836,99 +4676,156 @@ redir_stop(session_t *ps) {
} }
} }
/** // Handle queued events before we go to sleep
* Unredirection timeout callback. static void
*/ handle_queued_x_events(EV_P_ ev_prepare *w, int revents) {
static bool ev_session_prepare *sw = (void *)w;
tmout_unredir_callback(session_t *ps, timeout_t *tmout) { session_t *ps = sw->ps;
ps->tmout_unredir_hit = true; xcb_generic_event_t *ev;
tmout->enabled = false; xcb_connection_t *c = XGetXCBConnection(ps->dpy);
while ((ev = xcb_poll_for_queued_event(c))) {
return true; ev_handle(ps, ev);
free(ev);
};
XFlush(ps->dpy);
xcb_flush(c);
} }
/** /**
* Main loop. * Unredirection timeout callback.
*/ */
static bool static void
mainloop(session_t *ps) { tmout_unredir_callback(EV_P_ ev_timer *w, int revents) {
// Don't miss timeouts even when we have a LOT of other events! ev_session_timer *sw = (void *)w;
timeout_run(ps); sw->ps->tmout_unredir_hit = true;
queue_redraw(sw->ps);
}
// Process existing events static void
// Sometimes poll() returns 1 but no events are actually read, fade_timer_callback(EV_P_ ev_timer *w, int revents) {
// causing XNextEvent() to block, I have no idea what's wrong, so we ev_session_timer *sw = (void *)w;
// check for the number of events here. queue_redraw(sw->ps);
}
static void
_draw_callback(EV_P_ session_t *ps, int revents) {
if (ps->o.benchmark) {
if (ps->o.benchmark_wid) {
win *wi = find_win(ps, ps->o.benchmark_wid);
if (!wi) {
printf_errf("(): Couldn't find specified benchmark window.");
exit(1);
}
add_damage_from_win(ps, wi);
}
else {
force_repaint(ps);
}
}
ps->fade_running = false;
win *t = paint_preprocess(ps, ps->list);
ps->tmout_unredir_hit = false;
// Start/stop fade timer depends on whether window are fading
if (!ps->fade_running && ev_is_active(&ps->fade_timer->w))
ev_timer_stop(ps->loop, &ps->fade_timer->w);
else if (ps->fade_running && !ev_is_active(&ps->fade_timer->w)) {
ev_timer_set(&ps->fade_timer->w, fade_timeout(ps), 0);
ev_timer_start(ps->loop, &ps->fade_timer->w);
}
// If the screen is unredirected, free all_damage to stop painting
if (!ps->redirected || ps->o.stoppaint_force == ON)
pixman_region32_clear(&ps->all_damage);
if (pixman_region32_not_empty(&ps->all_damage)) {
region_t all_damage_orig, *region_real = NULL;
pixman_region32_init(&all_damage_orig);
// keep a copy of non-resized all_damage for region_real
if (ps->o.resize_damage > 0) {
copy_region(&all_damage_orig, &ps->all_damage);
resize_region(ps, &ps->all_damage, ps->o.resize_damage);
region_real = &all_damage_orig;
}
static int paint = 0;
paint_all(ps, &ps->all_damage, region_real, t);
pixman_region32_clear(&ps->all_damage);
pixman_region32_fini(&all_damage_orig);
paint++;
if (ps->o.benchmark && paint >= ps->o.benchmark)
exit(0);
}
if (!ps->fade_running)
ps->fade_time = 0L;
ps->redraw_needed = false;
}
static void
draw_callback(EV_P_ ev_idle *w, int revents) {
// This function is not used if we are using --swopti
ev_session_idle *sw = (void *)w;
_draw_callback(EV_A_ sw->ps, revents);
// Don't do painting non-stop unless we are in benchmark mode
if (!sw->ps->o.benchmark)
ev_idle_stop(sw->ps->loop, &sw->ps->draw_idle->w);
}
static void
delayed_draw_timer_callback(EV_P_ ev_timer *w, int revents) {
ev_session_timer *sw = (void *)w;
_draw_callback(EV_A_ sw->ps, revents);
// We might have stopped the ev_idle in delayed_draw_callback,
// so we restart it if we are in benchmark mode
if (sw->ps->o.benchmark)
ev_idle_start(EV_A_ &sw->ps->draw_idle->w);
}
static void
delayed_draw_callback(EV_P_ ev_idle *w, int revents) {
// This function is only used if we are using --swopti
ev_session_idle *sw = (void *)w;
if (ev_is_active(sw->ps->delayed_draw_timer))
return;
double delay = swopti_handle_timeout(sw->ps);
printf_errf("(): %lf", delay);
if (delay < 1e-6)
return _draw_callback(EV_A_ sw->ps, revents);
// This is a little bit hacky. When we get to this point in code, we need
// to update the screen , but we will only be updated after a delay, So
// we want to stop the ev_idle, so this callback doesn't get call repeatedly
// during the delay, we also want queue_redraw to not restart the ev_idle.
// So we stop ev_idle and leave ps->redraw_needed to be true.
//
// We do this anyway even if we are in benchmark mode. That means we will
// have to restart draw_idle after the draw actually happened when we are in
// benchmark mode.
ev_idle_stop(sw->ps->loop, &sw->ps->draw_idle->w);
ev_timer_set(&sw->ps->delayed_draw_timer->w, delay, 0);
ev_timer_start(sw->ps->loop, &sw->ps->delayed_draw_timer->w);
}
static void
x_event_callback(EV_P_ ev_io *w, int revents) {
session_t *ps = (session_t *)w;
xcb_connection_t *c = XGetXCBConnection(ps->dpy); xcb_connection_t *c = XGetXCBConnection(ps->dpy);
xcb_generic_event_t *ev = xcb_poll_for_event(c); xcb_generic_event_t *ev = xcb_poll_for_event(c);
if (ev) { if (ev) {
ev_handle(ps, ev); ev_handle(ps, ev);
ps->ev_received = true;
free(ev); free(ev);
return true;
} }
#ifdef CONFIG_DBUS
if (ps->o.dbus) {
cdbus_loop(ps);
}
#endif
if (ps->reset)
return false;
// Calculate timeout
struct timeval *ptv = NULL;
{
// Consider ev_received firstly
if (ps->ev_received || ps->o.benchmark) {
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;
}
// 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;
}
}
// Polling
fds_poll(ps, ptv);
free(ptv);
ptv = NULL;
return true;
} }
static void static void
@ -5073,20 +4970,14 @@ session_init(session_t *ps_old, int argc, char **argv) {
.track_leader = false, .track_leader = false,
}, },
.pfds_read = NULL,
.pfds_write = NULL,
.pfds_except = NULL,
.nfds_max = 0,
.tmout_lst = NULL,
.time_start = { 0, 0 }, .time_start = { 0, 0 },
.redirected = false, .redirected = false,
.alpha_picts = NULL, .alpha_picts = NULL,
.idling = false, .fade_running = false,
.fade_time = 0L, .fade_time = 0L,
.ignore_head = NULL, .ignore_head = NULL,
.ignore_tail = NULL, .ignore_tail = NULL,
.reset = false, .quit = false,
.expose_rects = NULL, .expose_rects = NULL,
.size_expose = 0, .size_expose = 0,
@ -5159,6 +5050,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
// Allocate a session and copy default values into it // Allocate a session and copy default values into it
session_t *ps = malloc(sizeof(session_t)); session_t *ps = malloc(sizeof(session_t));
*ps = s_def; *ps = s_def;
ps->loop = EV_DEFAULT;
pixman_region32_init(&ps->screen_reg); pixman_region32_init(&ps->screen_reg);
pixman_region32_init(&ps->all_damage); pixman_region32_init(&ps->all_damage);
for (int i = 0; i < CGLX_MAX_BUFFER_AGE; i ++) for (int i = 0; i < CGLX_MAX_BUFFER_AGE; i ++)
@ -5456,10 +5348,44 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue); ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue);
} }
fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN); ev_io_init(&ps->xiow, x_event_callback, ConnectionNumber(ps->dpy), EV_READ);
ps->tmout_unredir = timeout_insert(ps, ps->o.unredir_if_possible_delay, ev_io_start(ps->loop, &ps->xiow);
tmout_unredir_callback, NULL); ps->unredir_timer = calloc(1, sizeof(ev_session_timer));
ps->tmout_unredir->enabled = false; ps->unredir_timer->ps = ps;
ev_init(&ps->unredir_timer->w, tmout_unredir_callback);
ps->draw_idle = calloc(1, sizeof(ev_session_idle));
ps->draw_idle->ps = ps;
if (ps->o.sw_opti)
ev_idle_init(&ps->draw_idle->w, delayed_draw_callback);
else
ev_idle_init(&ps->draw_idle->w, draw_callback);
ps->fade_timer = calloc(1, sizeof(ev_session_timer));
ps->fade_timer->ps = ps;
ev_init(&ps->fade_timer->w, fade_timer_callback);
ps->delayed_draw_timer = calloc(1, sizeof(ev_session_timer));
ps->delayed_draw_timer->ps = ps;
ev_init(&ps->delayed_draw_timer->w, delayed_draw_timer_callback);
// xcb can read multiple events from the socket anytime a request which requires
// reply is made.
//
// Use an ev_prepare to make sure we cannot accidentally forget to handle them
// before we go to sleep.
//
// If we don't drain the queue before goes to sleep (i.e. blocking on socket
// input), we will be sleeping with events available in queue. Which might
// cause us to block indefinitely because arrival of new events could be
// dependent on processing of existing events (e.g. if we don't process damage
// event and do damage subtract, new damage event won't be generated.
ps->event_check = calloc(1, sizeof(ev_session_prepare));
ps->event_check->ps = ps;
ev_prepare_init(&ps->event_check->w, handle_queued_x_events);
// Make sure nothing can cause xcb to read from the X socket after events are
// handled and before we going to sleep.
ev_set_priority(&ps->event_check->w, EV_MINPRI);
ev_prepare_start(ps->loop, &ps->event_check->w);
XGrabServer(ps->dpy); XGrabServer(ps->dpy);
@ -5647,9 +5573,6 @@ session_destroy(session_t *ps) {
free(ps->o.blur_kerns[i]); free(ps->o.blur_kerns[i]);
free(ps->blur_kerns_cache[i]); free(ps->blur_kerns_cache[i]);
} }
free(ps->pfds_read);
free(ps->pfds_write);
free(ps->pfds_except);
free(ps->o.glx_fshader_win_str); free(ps->o.glx_fshader_win_str);
free_xinerama_info(ps); free_xinerama_info(ps);
free(ps->pictfmts); free(ps->pictfmts);
@ -5686,6 +5609,7 @@ session_destroy(session_t *ps) {
// Flush all events // Flush all events
XSync(ps->dpy, True); XSync(ps->dpy, True);
ev_io_stop(ps->loop, &ps->xiow);
#ifdef DEBUG_XRC #ifdef DEBUG_XRC
// Report about resource leakage // Report about resource leakage
@ -5693,8 +5617,21 @@ session_destroy(session_t *ps) {
#endif #endif
// Free timeouts // Free timeouts
ps->tmout_unredir = NULL; ev_timer_stop(ps->loop, &ps->unredir_timer->w);
timeout_clear(ps); free(ps->unredir_timer);
ps->unredir_timer = NULL;
ev_timer_stop(ps->loop, &ps->fade_timer->w);
free(ps->fade_timer);
ps->fade_timer = NULL;
ev_idle_stop(ps->loop, &ps->draw_idle->w);
free(ps->draw_idle);
ps->draw_idle = NULL;
ev_prepare_stop(ps->loop, &ps->event_check->w);
free(ps->event_check);
ps->event_check = NULL;
if (ps == ps_g) if (ps == ps_g)
ps_g = NULL; ps_g = NULL;
@ -5727,68 +5664,10 @@ session_run(session_t *ps) {
if (ps->redirected) if (ps->redirected)
paint_all(ps, NULL, NULL, t); paint_all(ps, NULL, NULL, t);
// Initialize idling if (ps->o.benchmark)
ps->idling = false; ev_idle_start(ps->loop, &ps->draw_idle->w);
// Main loop ev_run(ps->loop, 0);
while (!ps->reset) {
ps->ev_received = false;
while (mainloop(ps))
continue;
if (ps->o.benchmark) {
if (ps->o.benchmark_wid) {
win *w = find_win(ps, ps->o.benchmark_wid);
if (!w) {
printf_errf("(): Couldn't find specified benchmark window.");
session_destroy(ps);
exit(1);
}
add_damage_from_win(ps, w);
}
else {
force_repaint(ps);
}
}
// idling will be turned off during paint_preprocess() if needed
ps->idling = true;
t = paint_preprocess(ps, ps->list);
ps->tmout_unredir_hit = false;
// If the screen is unredirected, free all_damage to stop painting
if (!ps->redirected || ON == ps->o.stoppaint_force)
pixman_region32_clear(&ps->all_damage);
if (pixman_region32_not_empty(&ps->all_damage)) {
region_t all_damage_orig, *region_real = NULL;
pixman_region32_init(&all_damage_orig);
// keep a copy of non-resized all_damage for region_real
if (ps->o.resize_damage > 0) {
copy_region(&all_damage_orig, &ps->all_damage);
resize_region(ps, &ps->all_damage, ps->o.resize_damage);
region_real = &all_damage_orig;
}
static int paint = 0;
paint_all(ps, &ps->all_damage, region_real, t);
pixman_region32_clear(&ps->all_damage);
pixman_region32_fini(&all_damage_orig);
paint++;
if (ps->o.benchmark && paint >= ps->o.benchmark)
exit(0);
XSync(ps->dpy, False);
}
if (ps->idling)
ps->fade_time = 0L;
}
} }
/** /**
@ -5799,8 +5678,7 @@ session_run(session_t *ps) {
static void static void
reset_enable(int __attribute__((unused)) signum) { reset_enable(int __attribute__((unused)) signum) {
session_t * const ps = ps_g; session_t * const ps = ps_g;
ev_break(ps->loop, EVBREAK_ALL);
ps->reset = true;
} }
/** /**
@ -5826,7 +5704,8 @@ main(int argc, char **argv) {
// Main loop // Main loop
session_t *ps_old = ps_g; session_t *ps_old = ps_g;
while (1) { bool quit = false;
while (!quit) {
ps_g = session_init(ps_old, argc, argv); ps_g = session_init(ps_old, argc, argv);
if (!ps_g) { if (!ps_g) {
printf_errf("(): Failed to create new session."); printf_errf("(): Failed to create new session.");
@ -5834,6 +5713,7 @@ main(int argc, char **argv) {
} }
session_run(ps_g); session_run(ps_g);
ps_old = ps_g; ps_old = ps_g;
quit = ps_g->quit;
session_destroy(ps_g); session_destroy(ps_g);
} }

View File

@ -491,11 +491,6 @@ ensure_glx_context(session_t *ps) {
} }
#endif #endif
static inline time_ms_t
timeout_get_newrun(const timeout_t *ptmout) {
return ptmout->firstrun + (max_l((ptmout->lastrun + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + (time_ms_t) (ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE)) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval;
}
/** /**
* Get the Xinerama screen a window is on. * Get the Xinerama screen a window is on.
* *

View File

@ -10,6 +10,27 @@
#include "dbus.h" #include "dbus.h"
static DBusHandlerResult
cdbus_process(DBusConnection *conn, DBusMessage *m, void *);
static dbus_bool_t
cdbus_callback_add_timeout(DBusTimeout *timeout, void *data);
static void
cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data);
static void
cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data);
static dbus_bool_t
cdbus_callback_add_watch(DBusWatch *watch, void *data);
static void
cdbus_callback_remove_watch(DBusWatch *watch, void *data);
static void
cdbus_callback_watch_toggled(DBusWatch *watch, void *data);
/** /**
* Initialize D-Bus connection. * Initialize D-Bus connection.
*/ */
@ -84,7 +105,7 @@ cdbus_init(session_t *ps) {
dbus_error_free(&err); dbus_error_free(&err);
return false; return false;
} }
dbus_connection_add_filter(ps->dbus_conn, cdbus_process, ps, NULL);
return true; return true;
} }
@ -117,6 +138,20 @@ cdbus_destroy(session_t *ps) {
*/ */
///@{ ///@{
typedef struct ev_dbus_timer {
ev_timer w;
DBusTimeout *t;
} ev_dbus_timer;
/**
* Callback for handling a D-Bus timeout.
*/
static void
cdbus_callback_handle_timeout(EV_P_ ev_timer *w, int revents) {
ev_dbus_timer *t = (void *)w;
dbus_timeout_handle(t->t);
}
/** /**
* Callback for adding D-Bus timeout. * Callback for adding D-Bus timeout.
*/ */
@ -124,12 +159,16 @@ static dbus_bool_t
cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) { cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) {
session_t *ps = data; session_t *ps = data;
timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout), ev_dbus_timer *t = calloc(1, sizeof *t);
cdbus_callback_handle_timeout, timeout); double i = dbus_timeout_get_interval(timeout) / 1000.0;
if (ptmout) ev_timer_init(&t->w, cdbus_callback_handle_timeout, i, i);
dbus_timeout_set_data(timeout, ptmout, NULL); t->t = timeout;
dbus_timeout_set_data(timeout, t, NULL);
return (bool) ptmout; if (dbus_timeout_get_enabled(timeout))
ev_timer_start(ps->loop, &t->w);
return true;
} }
/** /**
@ -139,10 +178,10 @@ static void
cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) { cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) {
session_t *ps = data; session_t *ps = data;
timeout_t *ptmout = dbus_timeout_get_data(timeout); ev_dbus_timer *t = dbus_timeout_get_data(timeout);
assert(ptmout); assert(t);
if (ptmout) ev_timer_stop(ps->loop, &t->w);
timeout_drop(ps, ptmout); free(t);
} }
/** /**
@ -150,52 +189,77 @@ cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) {
*/ */
static void static void
cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) { cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) {
timeout_t *ptmout = dbus_timeout_get_data(timeout); session_t *ps = data;
ev_dbus_timer *t = dbus_timeout_get_data(timeout);
assert(ptmout); assert(t);
if (ptmout) { ev_timer_stop(ps->loop, &t->w);
ptmout->enabled = dbus_timeout_get_enabled(timeout); if (dbus_timeout_get_enabled(timeout)) {
// Refresh interval as libdbus doc says: "Whenever a timeout is toggled, double i = dbus_timeout_get_interval(timeout) / 1000.0;
// its interval may change." ev_timer_set(&t->w, i, i);
ptmout->interval = dbus_timeout_get_interval(timeout); ev_timer_start(ps->loop, &t->w);
} }
} }
/**
* Callback for handling a D-Bus timeout.
*/
static bool
cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout) {
assert(ptmout && ptmout->data);
if (ptmout && ptmout->data)
return dbus_timeout_handle(ptmout->data);
return false;
}
///@} ///@}
/** @name DBusWatch handling /** @name DBusWatch handling
*/ */
///@{ ///@{
typedef struct ev_dbus_io {
ev_io w;
session_t *ps;
DBusWatch *dw;
} ev_dbus_io;
void cdbus_io_callback(EV_P_ ev_io *w, int revents) {
ev_dbus_io *dw = (void *)w;
DBusWatchFlags flags = 0;
if (revents & EV_READ)
flags |= DBUS_WATCH_READABLE;
if (revents & EV_WRITE)
flags |= DBUS_WATCH_WRITABLE;
dbus_watch_handle(dw->dw, flags);
while (dbus_connection_dispatch(dw->ps->dbus_conn) != DBUS_DISPATCH_COMPLETE);
}
/**
* Determine the poll condition of a DBusWatch.
*/
static inline int
cdbus_get_watch_cond(DBusWatch *watch) {
const unsigned flags = dbus_watch_get_flags(watch);
int condition = 0;
if (flags & DBUS_WATCH_READABLE)
condition |= EV_READ;
if (flags & DBUS_WATCH_WRITABLE)
condition |= EV_WRITE;
return condition;
}
/** /**
* Callback for adding D-Bus watch. * Callback for adding D-Bus watch.
*/ */
static dbus_bool_t static dbus_bool_t
cdbus_callback_add_watch(DBusWatch *watch, void *data) { cdbus_callback_add_watch(DBusWatch *watch, void *data) {
// Leave disabled watches alone
if (!dbus_watch_get_enabled(watch))
return TRUE;
session_t *ps = data; session_t *ps = data;
// Insert the file descriptor ev_dbus_io *w = calloc(1, sizeof *w);
fds_insert(ps, dbus_watch_get_unix_fd(watch), w->dw = watch;
w->ps = ps;
ev_io_init(&w->w, cdbus_io_callback, dbus_watch_get_unix_fd(watch),
cdbus_get_watch_cond(watch)); cdbus_get_watch_cond(watch));
// Leave disabled watches alone
if (dbus_watch_get_enabled(watch))
ev_io_start(ps->loop, &w->w);
dbus_watch_set_data(watch, w, NULL);
// Always return true // Always return true
return TRUE; return true;
} }
/** /**
@ -204,9 +268,9 @@ cdbus_callback_add_watch(DBusWatch *watch, void *data) {
static void static void
cdbus_callback_remove_watch(DBusWatch *watch, void *data) { cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
session_t *ps = data; session_t *ps = data;
ev_dbus_io *w = dbus_watch_get_data(watch);
fds_drop(ps, dbus_watch_get_unix_fd(watch), ev_io_stop(ps->loop, &w->w);
cdbus_get_watch_cond(watch)); free(w);
} }
/** /**
@ -214,12 +278,12 @@ cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
*/ */
static void static void
cdbus_callback_watch_toggled(DBusWatch *watch, void *data) { cdbus_callback_watch_toggled(DBusWatch *watch, void *data) {
if (dbus_watch_get_enabled(watch)) { session_t *ps = data;
cdbus_callback_add_watch(watch, data); ev_io *w = dbus_watch_get_data(watch);
} if (dbus_watch_get_enabled(watch))
else { ev_io_start(ps->loop, w);
cdbus_callback_remove_watch(watch, data); else
} ev_io_stop(ps->loop, w);
} }
///@} ///@}
@ -534,110 +598,10 @@ cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
return true; return true;
} }
void
cdbus_loop(session_t *ps) {
dbus_connection_read_write(ps->dbus_conn, 0);
DBusMessage *msg = NULL;
while ((msg = dbus_connection_pop_message(ps->dbus_conn)))
cdbus_process(ps, msg);
}
/** @name Message processing /** @name Message processing
*/ */
///@{ ///@{
/**
* Process a message from D-Bus.
*/
static void
cdbus_process(session_t *ps, DBusMessage *msg) {
bool success = false;
#define cdbus_m_ismethod(method) \
dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method)
if (cdbus_m_ismethod("reset")) {
ps->reset = true;
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
success = true;
}
else if (cdbus_m_ismethod("repaint")) {
force_repaint(ps);
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
success = true;
}
else if (cdbus_m_ismethod("list_win")) {
success = cdbus_process_list_win(ps, msg);
}
else if (cdbus_m_ismethod("win_get")) {
success = cdbus_process_win_get(ps, msg);
}
else if (cdbus_m_ismethod("win_set")) {
success = cdbus_process_win_set(ps, msg);
}
else if (cdbus_m_ismethod("find_win")) {
success = cdbus_process_find_win(ps, msg);
}
else if (cdbus_m_ismethod("opts_get")) {
success = cdbus_process_opts_get(ps, msg);
}
else if (cdbus_m_ismethod("opts_set")) {
success = cdbus_process_opts_set(ps, msg);
}
#undef cdbus_m_ismethod
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Introspectable", "Introspect")) {
success = cdbus_process_introspect(ps, msg);
}
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Peer", "Ping")) {
cdbus_reply(ps, msg, NULL, NULL);
success = true;
}
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Peer", "GetMachineId")) {
char *uuid = dbus_get_local_machine_id();
if (uuid) {
cdbus_reply_string(ps, msg, uuid);
dbus_free(uuid);
success = true;
}
}
else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired")
|| dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) {
success = true;
}
else {
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) {
printf_errf("(): Error message of path \"%s\" "
"interface \"%s\", member \"%s\", error \"%s\"",
dbus_message_get_path(msg), dbus_message_get_interface(msg),
dbus_message_get_member(msg), dbus_message_get_error_name(msg));
}
else {
printf_errf("(): Illegal message of type \"%s\", path \"%s\" "
"interface \"%s\", member \"%s\"",
cdbus_repr_msgtype(msg), dbus_message_get_path(msg),
dbus_message_get_interface(msg), dbus_message_get_member(msg));
}
if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg)
&& !dbus_message_get_no_reply(msg))
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
success = true;
}
// If the message could not be processed, and an reply is expected, return
// an empty reply.
if (!success && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg)
&& !dbus_message_get_no_reply(msg))
cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S);
// Free the message
dbus_message_unref(msg);
}
/** /**
* Process a list_win D-Bus request. * Process a list_win D-Bus request.
*/ */
@ -993,6 +957,9 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
return true; return true;
} }
// XXX Remove this after header clean up
void queue_redraw(session_t *ps);
/** /**
* Process a opts_set D-Bus request. * Process a opts_set D-Bus request.
*/ */
@ -1055,7 +1022,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
return false; return false;
if (ps->o.unredir_if_possible != val) { if (ps->o.unredir_if_possible != val) {
ps->o.unredir_if_possible = val; ps->o.unredir_if_possible = val;
ps->ev_received = true; queue_redraw(ps);
} }
goto cdbus_process_opts_set_success; goto cdbus_process_opts_set_success;
} }
@ -1171,6 +1138,100 @@ cdbus_process_introspect(session_t *ps, DBusMessage *msg) {
} }
///@} ///@}
/**
* Process a message from D-Bus.
*/
static DBusHandlerResult
cdbus_process(DBusConnection *c, DBusMessage *msg, void *ud) {
session_t *ps = ud;
bool handled = false;
#define cdbus_m_ismethod(method) \
dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method)
if (cdbus_m_ismethod("reset")) {
ps->reset = true;
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
handled = true;
}
else if (cdbus_m_ismethod("repaint")) {
force_repaint(ps);
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
handled = true;
}
else if (cdbus_m_ismethod("list_win")) {
handled = cdbus_process_list_win(ps, msg);
}
else if (cdbus_m_ismethod("win_get")) {
handled = cdbus_process_win_get(ps, msg);
}
else if (cdbus_m_ismethod("win_set")) {
handled = cdbus_process_win_set(ps, msg);
}
else if (cdbus_m_ismethod("find_win")) {
handled = cdbus_process_find_win(ps, msg);
}
else if (cdbus_m_ismethod("opts_get")) {
handled = cdbus_process_opts_get(ps, msg);
}
else if (cdbus_m_ismethod("opts_set")) {
handled = cdbus_process_opts_set(ps, msg);
}
#undef cdbus_m_ismethod
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Introspectable", "Introspect")) {
handled = cdbus_process_introspect(ps, msg);
}
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Peer", "Ping")) {
cdbus_reply(ps, msg, NULL, NULL);
handled = true;
}
else if (dbus_message_is_method_call(msg,
"org.freedesktop.DBus.Peer", "GetMachineId")) {
char *uuid = dbus_get_local_machine_id();
if (uuid) {
cdbus_reply_string(ps, msg, uuid);
dbus_free(uuid);
handled = true;
}
}
else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired")
|| dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) {
handled = true;
}
else {
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) {
printf_errf("(): Error message of path \"%s\" "
"interface \"%s\", member \"%s\", error \"%s\"",
dbus_message_get_path(msg), dbus_message_get_interface(msg),
dbus_message_get_member(msg), dbus_message_get_error_name(msg));
}
else {
printf_errf("(): Illegal message of type \"%s\", path \"%s\" "
"interface \"%s\", member \"%s\"",
cdbus_repr_msgtype(msg), dbus_message_get_path(msg),
dbus_message_get_interface(msg), dbus_message_get_member(msg));
}
if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg)
&& !dbus_message_get_no_reply(msg))
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
handled = true;
}
// If the message could not be processed, and an reply is expected, return
// an empty reply.
if (!handled && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg)
&& !dbus_message_get_no_reply(msg)) {
cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S);
handled = true;
}
return handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/** @name Core callbacks /** @name Core callbacks
*/ */
///@{ ///@{

View File

@ -42,42 +42,6 @@ typedef uint16_t cdbus_enum_t;
#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 #define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16
#define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING #define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING
static dbus_bool_t
cdbus_callback_add_timeout(DBusTimeout *timeout, void *data);
static void
cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data);
static void
cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data);
static bool
cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout);
/**
* Determine the poll condition of a DBusWatch.
*/
static inline short
cdbus_get_watch_cond(DBusWatch *watch) {
const unsigned flags = dbus_watch_get_flags(watch);
short condition = POLLERR | POLLHUP;
if (flags & DBUS_WATCH_READABLE)
condition |= POLLIN;
if (flags & DBUS_WATCH_WRITABLE)
condition |= POLLOUT;
return condition;
}
static dbus_bool_t
cdbus_callback_add_watch(DBusWatch *watch, void *data);
static void
cdbus_callback_remove_watch(DBusWatch *watch, void *data);
static void
cdbus_callback_watch_toggled(DBusWatch *watch, void *data);
static bool static bool
cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data);
@ -221,32 +185,4 @@ cdbus_repr_msgtype(DBusMessage *msg) {
return dbus_message_type_to_string(dbus_message_get_type(msg)); return dbus_message_type_to_string(dbus_message_get_type(msg));
} }
/** @name Message processing
*/
///@{
static void
cdbus_process(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_list_win(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_win_get(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_win_set(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_find_win(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_opts_get(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_opts_set(session_t *ps, DBusMessage *msg);
static bool
cdbus_process_introspect(session_t *ps, DBusMessage *msg);
///@} ///@}