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:
parent
e58de49d7a
commit
2a4fed50f4
2
Makefile
2
Makefile
|
@ -10,7 +10,7 @@ APPDIR ?= $(PREFIX)/share/applications
|
|||
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
|
||||
LIBS = -lm -lrt
|
||||
LIBS = -lm -lrt -lev
|
||||
INCS =
|
||||
|
||||
OBJS = compton.o config.o win.o x.o
|
||||
|
|
118
src/common.h
118
src/common.h
|
@ -94,6 +94,7 @@
|
|||
#ifdef CONFIG_XINERAMA
|
||||
#include <xcb/xinerama.h>
|
||||
#endif
|
||||
#include <ev.h>
|
||||
#include <pixman.h>
|
||||
|
||||
// libdbus
|
||||
|
@ -180,7 +181,6 @@
|
|||
#define TIME_MS_MAX LONG_MAX
|
||||
#define FADE_DELTA_TOLERANCE 0.2
|
||||
#define SWOPTI_TOLERANCE 3000
|
||||
#define TIMEOUT_RUN_TOLERANCE 0.05
|
||||
#define WIN_GET_LEADER_MAX_RECURSION 20
|
||||
|
||||
#define SEC_WRAP (15L * 24L * 60L * 60L)
|
||||
|
@ -504,9 +504,10 @@ typedef struct _latom {
|
|||
|
||||
#define REG_DATA_INIT { NULL, 0 }
|
||||
|
||||
struct _timeout_t;
|
||||
|
||||
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.
|
||||
typedef struct options_t {
|
||||
|
@ -764,6 +765,10 @@ typedef struct {
|
|||
|
||||
/// Structure containing all necessary data for a compton session.
|
||||
typedef struct session {
|
||||
/// ev_io for X connection
|
||||
ev_io xiow;
|
||||
/// libev mainloop
|
||||
struct ev_loop *loop;
|
||||
// === Display related ===
|
||||
/// Display in use.
|
||||
Display *dpy;
|
||||
|
@ -813,25 +818,27 @@ typedef struct session {
|
|||
// === Operation related ===
|
||||
/// Program options.
|
||||
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.
|
||||
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.
|
||||
bool tmout_unredir_hit;
|
||||
/// Whether we have received an event in this cycle.
|
||||
bool ev_received;
|
||||
/// Whether we need to redraw the screen
|
||||
bool redraw_needed;
|
||||
/// Whether the program is idling. I.e. no fading, no potential window
|
||||
/// changes.
|
||||
bool idling;
|
||||
bool fade_running;
|
||||
/// Program start time.
|
||||
struct timeval time_start;
|
||||
/// 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];
|
||||
/// Reset program after next paint.
|
||||
bool reset;
|
||||
/// If compton should quit
|
||||
bool quit;
|
||||
|
||||
// === Expose event related ===
|
||||
/// Pointer to an array of <code>XRectangle</code>-s of exposed region.
|
||||
|
@ -1206,17 +1215,6 @@ struct options_tmp {
|
|||
double menu_opacity;
|
||||
};
|
||||
|
||||
/// Structure for a recorded timeout.
|
||||
typedef struct _timeout_t {
|
||||
bool enabled;
|
||||
void *data;
|
||||
bool (*callback)(session_t *ps, struct _timeout_t *ptmout);
|
||||
time_ms_t interval;
|
||||
time_ms_t firstrun;
|
||||
time_ms_t lastrun;
|
||||
struct _timeout_t *next;
|
||||
} timeout_t;
|
||||
|
||||
/// Enumeration for window event hints.
|
||||
typedef enum {
|
||||
WIN_EVMODE_UNKNOWN,
|
||||
|
@ -1453,7 +1451,7 @@ print_timestamp(session_t *ps) {
|
|||
if (gettimeofday(&tm, NULL)) return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
|
|
632
src/compton.c
632
src/compton.c
|
@ -15,6 +15,8 @@
|
|||
#include <xcb/render.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#include "compton.h"
|
||||
#ifdef CONFIG_OPENGL
|
||||
#include "opengl.h"
|
||||
|
@ -38,9 +40,6 @@ update_refresh_rate(session_t *ps);
|
|||
static bool
|
||||
swopti_init(session_t *ps);
|
||||
|
||||
static void
|
||||
swopti_handle_timeout(session_t *ps, struct timeval *ptv);
|
||||
|
||||
static void
|
||||
cxinerama_upd_scrs(session_t *ps);
|
||||
|
||||
|
@ -113,6 +112,24 @@ restack_win(session_t *ps, win *w, Window new_above);
|
|||
static void
|
||||
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 ===
|
||||
|
||||
/// 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
|
||||
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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
#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 ===
|
||||
|
||||
/**
|
||||
|
@ -322,13 +312,13 @@ fds_poll(session_t *ps, struct timeval *ptv) {
|
|||
*
|
||||
* In milliseconds.
|
||||
*/
|
||||
static int
|
||||
static double
|
||||
fade_timeout(session_t *ps) {
|
||||
int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time;
|
||||
|
||||
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) {
|
||||
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
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
// 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);
|
||||
// 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->o.unredir_if_possible_delay || ps->tmout_unredir_hit)
|
||||
redir_stop(ps);
|
||||
else if (!ps->tmout_unredir->enabled) {
|
||||
timeout_reset(ps, ps->tmout_unredir);
|
||||
ps->tmout_unredir->enabled = true;
|
||||
else if (!ev_is_active(&ps->unredir_timer->w)) {
|
||||
ev_timer_set(&ps->unredir_timer->w,
|
||||
ps->o.unredir_if_possible_delay / 1000.0, 0);
|
||||
ev_timer_start(ps->loop, &ps->unredir_timer->w);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ps->tmout_unredir->enabled = false;
|
||||
ev_timer_stop(ps->loop, &ps->unredir_timer->w);
|
||||
redir_start(ps);
|
||||
}
|
||||
|
||||
|
@ -2466,7 +2456,7 @@ expose_root(session_t *ps, const rect_t *rects, int nrects) {
|
|||
void
|
||||
force_repaint(session_t *ps) {
|
||||
assert(pixman_region32_not_empty(&ps->screen_reg));
|
||||
ps->ev_received = true;
|
||||
queue_redraw(ps);
|
||||
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) {
|
||||
w->shadow_force = val;
|
||||
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) {
|
||||
w->fade_force = val;
|
||||
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) {
|
||||
w->focused_force = val;
|
||||
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) {
|
||||
w->invert_color_force = val;
|
||||
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;
|
||||
for (win *w = ps->list; w; w = w->next)
|
||||
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
|
||||
|
||||
// XXX redraw needs to be more fine grained
|
||||
queue_redraw(ps);
|
||||
|
||||
switch (ev->response_type) {
|
||||
case FocusIn:
|
||||
ev_focus_in(ps, (xcb_focus_in_event_t *)ev);
|
||||
|
@ -4215,31 +4208,22 @@ swopti_init(session_t *ps) {
|
|||
* @param ps current session
|
||||
* @param[in,out] ptv pointer to the timeout
|
||||
*/
|
||||
static void
|
||||
swopti_handle_timeout(session_t *ps, struct timeval *ptv) {
|
||||
if (!ptv)
|
||||
return;
|
||||
|
||||
static double
|
||||
swopti_handle_timeout(session_t *ps) {
|
||||
// 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 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)
|
||||
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
|
||||
// an offset, to avoid certain blocking conditions.
|
||||
if (offset < SWOPTI_TOLERANCE
|
||||
|| offset > ps->refresh_intv - SWOPTI_TOLERANCE)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
// Add an offset so we wait until the next refresh after timeout
|
||||
ptv->tv_usec += ps->refresh_intv - offset;
|
||||
if (ptv->tv_usec > US_PER_SEC) {
|
||||
ptv->tv_usec -= US_PER_SEC;
|
||||
++ptv->tv_sec;
|
||||
}
|
||||
return (ps->refresh_intv - offset) / 1e6;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -4836,99 +4676,156 @@ redir_stop(session_t *ps) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unredirection timeout callback.
|
||||
*/
|
||||
static bool
|
||||
tmout_unredir_callback(session_t *ps, timeout_t *tmout) {
|
||||
ps->tmout_unredir_hit = true;
|
||||
tmout->enabled = false;
|
||||
|
||||
return true;
|
||||
// Handle queued events before we go to sleep
|
||||
static void
|
||||
handle_queued_x_events(EV_P_ ev_prepare *w, int revents) {
|
||||
ev_session_prepare *sw = (void *)w;
|
||||
session_t *ps = sw->ps;
|
||||
xcb_generic_event_t *ev;
|
||||
xcb_connection_t *c = XGetXCBConnection(ps->dpy);
|
||||
while ((ev = xcb_poll_for_queued_event(c))) {
|
||||
ev_handle(ps, ev);
|
||||
free(ev);
|
||||
};
|
||||
XFlush(ps->dpy);
|
||||
xcb_flush(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loop.
|
||||
* Unredirection timeout callback.
|
||||
*/
|
||||
static bool
|
||||
mainloop(session_t *ps) {
|
||||
// Don't miss timeouts even when we have a LOT of other events!
|
||||
timeout_run(ps);
|
||||
static void
|
||||
tmout_unredir_callback(EV_P_ ev_timer *w, int revents) {
|
||||
ev_session_timer *sw = (void *)w;
|
||||
sw->ps->tmout_unredir_hit = true;
|
||||
queue_redraw(sw->ps);
|
||||
}
|
||||
|
||||
// Process existing events
|
||||
// Sometimes poll() returns 1 but no events are actually read,
|
||||
// causing XNextEvent() to block, I have no idea what's wrong, so we
|
||||
// check for the number of events here.
|
||||
static void
|
||||
fade_timer_callback(EV_P_ ev_timer *w, int revents) {
|
||||
ev_session_timer *sw = (void *)w;
|
||||
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_generic_event_t *ev = xcb_poll_for_event(c);
|
||||
if (ev) {
|
||||
ev_handle(ps, ev);
|
||||
ps->ev_received = true;
|
||||
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
|
||||
|
@ -5073,20 +4970,14 @@ session_init(session_t *ps_old, int argc, char **argv) {
|
|||
.track_leader = false,
|
||||
},
|
||||
|
||||
.pfds_read = NULL,
|
||||
.pfds_write = NULL,
|
||||
.pfds_except = NULL,
|
||||
.nfds_max = 0,
|
||||
.tmout_lst = NULL,
|
||||
|
||||
.time_start = { 0, 0 },
|
||||
.redirected = false,
|
||||
.alpha_picts = NULL,
|
||||
.idling = false,
|
||||
.fade_running = false,
|
||||
.fade_time = 0L,
|
||||
.ignore_head = NULL,
|
||||
.ignore_tail = NULL,
|
||||
.reset = false,
|
||||
.quit = false,
|
||||
|
||||
.expose_rects = NULL,
|
||||
.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
|
||||
session_t *ps = malloc(sizeof(session_t));
|
||||
*ps = s_def;
|
||||
ps->loop = EV_DEFAULT;
|
||||
pixman_region32_init(&ps->screen_reg);
|
||||
pixman_region32_init(&ps->all_damage);
|
||||
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);
|
||||
}
|
||||
|
||||
fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN);
|
||||
ps->tmout_unredir = timeout_insert(ps, ps->o.unredir_if_possible_delay,
|
||||
tmout_unredir_callback, NULL);
|
||||
ps->tmout_unredir->enabled = false;
|
||||
ev_io_init(&ps->xiow, x_event_callback, ConnectionNumber(ps->dpy), EV_READ);
|
||||
ev_io_start(ps->loop, &ps->xiow);
|
||||
ps->unredir_timer = calloc(1, sizeof(ev_session_timer));
|
||||
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);
|
||||
|
||||
|
@ -5647,9 +5573,6 @@ session_destroy(session_t *ps) {
|
|||
free(ps->o.blur_kerns[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_xinerama_info(ps);
|
||||
free(ps->pictfmts);
|
||||
|
@ -5686,6 +5609,7 @@ session_destroy(session_t *ps) {
|
|||
|
||||
// Flush all events
|
||||
XSync(ps->dpy, True);
|
||||
ev_io_stop(ps->loop, &ps->xiow);
|
||||
|
||||
#ifdef DEBUG_XRC
|
||||
// Report about resource leakage
|
||||
|
@ -5693,8 +5617,21 @@ session_destroy(session_t *ps) {
|
|||
#endif
|
||||
|
||||
// Free timeouts
|
||||
ps->tmout_unredir = NULL;
|
||||
timeout_clear(ps);
|
||||
ev_timer_stop(ps->loop, &ps->unredir_timer->w);
|
||||
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)
|
||||
ps_g = NULL;
|
||||
|
@ -5727,68 +5664,10 @@ session_run(session_t *ps) {
|
|||
if (ps->redirected)
|
||||
paint_all(ps, NULL, NULL, t);
|
||||
|
||||
// Initialize idling
|
||||
ps->idling = false;
|
||||
if (ps->o.benchmark)
|
||||
ev_idle_start(ps->loop, &ps->draw_idle->w);
|
||||
|
||||
// Main loop
|
||||
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;
|
||||
}
|
||||
ev_run(ps->loop, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5799,8 +5678,7 @@ session_run(session_t *ps) {
|
|||
static void
|
||||
reset_enable(int __attribute__((unused)) signum) {
|
||||
session_t * const ps = ps_g;
|
||||
|
||||
ps->reset = true;
|
||||
ev_break(ps->loop, EVBREAK_ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5826,7 +5704,8 @@ main(int argc, char **argv) {
|
|||
|
||||
// Main loop
|
||||
session_t *ps_old = ps_g;
|
||||
while (1) {
|
||||
bool quit = false;
|
||||
while (!quit) {
|
||||
ps_g = session_init(ps_old, argc, argv);
|
||||
if (!ps_g) {
|
||||
printf_errf("(): Failed to create new session.");
|
||||
|
@ -5834,6 +5713,7 @@ main(int argc, char **argv) {
|
|||
}
|
||||
session_run(ps_g);
|
||||
ps_old = ps_g;
|
||||
quit = ps_g->quit;
|
||||
session_destroy(ps_g);
|
||||
}
|
||||
|
||||
|
|
|
@ -491,11 +491,6 @@ ensure_glx_context(session_t *ps) {
|
|||
}
|
||||
#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.
|
||||
*
|
||||
|
|
355
src/dbus.c
355
src/dbus.c
|
@ -10,6 +10,27 @@
|
|||
|
||||
#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.
|
||||
*/
|
||||
|
@ -84,7 +105,7 @@ cdbus_init(session_t *ps) {
|
|||
dbus_error_free(&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus_connection_add_filter(ps->dbus_conn, cdbus_process, ps, NULL);
|
||||
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.
|
||||
*/
|
||||
|
@ -124,12 +159,16 @@ static dbus_bool_t
|
|||
cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) {
|
||||
session_t *ps = data;
|
||||
|
||||
timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout),
|
||||
cdbus_callback_handle_timeout, timeout);
|
||||
if (ptmout)
|
||||
dbus_timeout_set_data(timeout, ptmout, NULL);
|
||||
ev_dbus_timer *t = calloc(1, sizeof *t);
|
||||
double i = dbus_timeout_get_interval(timeout) / 1000.0;
|
||||
ev_timer_init(&t->w, cdbus_callback_handle_timeout, i, i);
|
||||
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) {
|
||||
session_t *ps = data;
|
||||
|
||||
timeout_t *ptmout = dbus_timeout_get_data(timeout);
|
||||
assert(ptmout);
|
||||
if (ptmout)
|
||||
timeout_drop(ps, ptmout);
|
||||
ev_dbus_timer *t = dbus_timeout_get_data(timeout);
|
||||
assert(t);
|
||||
ev_timer_stop(ps->loop, &t->w);
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,52 +189,77 @@ cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) {
|
|||
*/
|
||||
static void
|
||||
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);
|
||||
if (ptmout) {
|
||||
ptmout->enabled = dbus_timeout_get_enabled(timeout);
|
||||
// Refresh interval as libdbus doc says: "Whenever a timeout is toggled,
|
||||
// its interval may change."
|
||||
ptmout->interval = dbus_timeout_get_interval(timeout);
|
||||
assert(t);
|
||||
ev_timer_stop(ps->loop, &t->w);
|
||||
if (dbus_timeout_get_enabled(timeout)) {
|
||||
double i = dbus_timeout_get_interval(timeout) / 1000.0;
|
||||
ev_timer_set(&t->w, i, i);
|
||||
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
|
||||
*/
|
||||
///@{
|
||||
|
||||
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.
|
||||
*/
|
||||
static dbus_bool_t
|
||||
cdbus_callback_add_watch(DBusWatch *watch, void *data) {
|
||||
// Leave disabled watches alone
|
||||
if (!dbus_watch_get_enabled(watch))
|
||||
return TRUE;
|
||||
|
||||
session_t *ps = data;
|
||||
|
||||
// Insert the file descriptor
|
||||
fds_insert(ps, dbus_watch_get_unix_fd(watch),
|
||||
cdbus_get_watch_cond(watch));
|
||||
ev_dbus_io *w = calloc(1, sizeof *w);
|
||||
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));
|
||||
|
||||
// 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
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,9 +268,9 @@ cdbus_callback_add_watch(DBusWatch *watch, void *data) {
|
|||
static void
|
||||
cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
|
||||
session_t *ps = data;
|
||||
|
||||
fds_drop(ps, dbus_watch_get_unix_fd(watch),
|
||||
cdbus_get_watch_cond(watch));
|
||||
ev_dbus_io *w = dbus_watch_get_data(watch);
|
||||
ev_io_stop(ps->loop, &w->w);
|
||||
free(w);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,12 +278,12 @@ cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
|
|||
*/
|
||||
static void
|
||||
cdbus_callback_watch_toggled(DBusWatch *watch, void *data) {
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
cdbus_callback_add_watch(watch, data);
|
||||
}
|
||||
else {
|
||||
cdbus_callback_remove_watch(watch, data);
|
||||
}
|
||||
session_t *ps = data;
|
||||
ev_io *w = dbus_watch_get_data(watch);
|
||||
if (dbus_watch_get_enabled(watch))
|
||||
ev_io_start(ps->loop, w);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -993,6 +957,9 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// XXX Remove this after header clean up
|
||||
void queue_redraw(session_t *ps);
|
||||
|
||||
/**
|
||||
* Process a opts_set D-Bus request.
|
||||
*/
|
||||
|
@ -1055,7 +1022,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
|||
return false;
|
||||
if (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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
///@{
|
||||
|
|
64
src/dbus.h
64
src/dbus.h
|
@ -42,42 +42,6 @@ typedef uint16_t cdbus_enum_t;
|
|||
#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16
|
||||
#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
|
||||
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));
|
||||
}
|
||||
|
||||
/** @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);
|
||||
|
||||
///@}
|
||||
|
|
Loading…
Reference in New Issue