Feature #80: D-Bus support

- Add D-Bus support. Currently 7 methods are available: "reset" (same as
  SIGUSR1), "list_win" (list the windows compton manages), "win_get"
  (get a property of the window), "win_set" (set a property of the
  window), "find_win" (find window based on client window / focus),
  "opts_get" (get the value of a compton option), and "opts_set" (set
  the value of a compton option), together with 4 signals: "win_added",
  "win_destroyed", "win_mapped", "win_unmapped".

- D-Bus support depends on libdbus.

- As there are many items and my time is tight, no much tests are done.
  Bugs to be expected.

- Create a new header file `common.h` that contains shared content.

- Fix some bugs in timeout handling.

- Update file headers in all source files.

- Re-enable --unredir-if-possible on multi-screen set-ups, as the user
  could turn if off manually anyway.

- Check if the window is mapped in `repair_win()`.

- Add ps->track_atom_lst and its handlers, to prepare for the new
  condition format.

- Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a
  very limited number of targets only. New ones will be added gradually.

- Known issue 2: Accidental drop of D-Bus connection is not handled.

- Known issue 3: Introspection does not reveal all available methods,
  because some methods have unpredictable prototypes. Still hesitating
  about what to do...

- Known issue 4: Error handling is not finished yet. Compton does not
  always reply with the correct error message (but it does print out the
  correct error message, usually).
This commit is contained in:
Richard Grenville 2013-01-19 20:20:27 +08:00
parent e60fe72dcb
commit 58c0ecec40
8 changed files with 2715 additions and 1280 deletions

View File

@ -11,6 +11,8 @@ PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr
LIBS = -lm -lrt
INCS =
OBJS = compton.o
# === Configuration flags ===
CFG =
@ -46,10 +48,11 @@ ifeq "$(NO_VSYNC_OPENGL)" ""
endif
# ==== D-Bus ====
# ifeq "$(NO_DBUS)" ""
# CFG += -DCONFIG_DBUS
# PACKAGES += dbus-1
# endif
ifeq "$(NO_DBUS)" ""
CFG += -DCONFIG_DBUS
PACKAGES += dbus-1
OBJS += dbus.o
endif
# === Version string ===
COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1 --date=short --pretty=format:%cd)
@ -63,14 +66,13 @@ LIBS += $(shell pkg-config --libs $(PACKAGES))
INCS += $(shell pkg-config --cflags $(PACKAGES))
CFLAGS += -Wall -std=c99
OBJS = compton.o
MANPAGES = man/compton.1 man/compton-trans.1
MANPAGES_HTML = $(addsuffix .html,$(MANPAGES))
# === Recipes ===
.DEFAULT_GOAL := compton
%.o: src/%.c src/%.h
%.o: src/%.c src/%.h src/common.h
$(CC) $(CFLAGS) $(INCS) -c src/$*.c
compton: $(OBJS)

View File

@ -11,17 +11,18 @@ partially doing this out of a desire to learn Xlib.
## Changes from xcompmgr:
* __inactive window transparency__ (specified with `-i`)
* __inactive window transparency / dimming__
* __titlebar/frame transparency__ (specified with `-e`)
* menu transparency (thanks to Dana)
* shadows are now enabled for argb windows, e.g. terminals with transparency
* removed serverside shadows (and simple compositing) to clean the code,
the only option that remains is clientside shadows
* configuration files (specified with `--config`)
* colored shadows (with `--shadow-[red/green/blue] value`)
* configuration files (see the man page for more details)
* colored shadows (`--shadow-[red/green/blue]`)
* a new fade system
* vsync (still under development)
* several more options
* VSync support (not always working)
* Blur of background of transparent windows, window color inversion (bad in performance)
* Some more options...
## Fixes from the original xcompmgr:
@ -51,10 +52,11 @@ __R__ for runtime
* xproto / x11proto (B)
* bash (R)
* xprop,xwininfo / x11-utils (R)
* libpcre (B,R) (Will probably be made optional soon)
* libconfig (B,R) (Will probably be made optional soon)
* libdrm (B) (Will probably be made optional soon)
* libGL (B,R) (Will probably be made optional soon)
* libpcre (B,R) (Can be disabled with `NO_REGEX_PCRE` at compile time)
* libconfig (B,R) (Can be disabled with `NO_LIBCONFIG` at compile time)
* libdrm (B) (Can be disabled with `NO_VSYNC_DRM` at compile time)
* libGL (B,R) (Can be disabled with `NO_VSYNC_OPENGL` at compile time)
* libdbus (B,R) (Can be disabled with `NO_DBUS` at compile time)
* asciidoc (B)
### How to build
@ -64,7 +66,7 @@ To build, make sure you have the dependencies above:
``` bash
# Make the main program
$ make
# Make the newer man pages
# Make the man pages
$ make docs
# Install
$ make install

45
dbus-examples/cdbus-driver.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh
# === Get connection parameters ===
dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _)
if [ -z "$dpy" ]; then
echo "Cannot find display."
exit 1
fi
service="com.github.chjj.compton.${dpy}"
interface='com.github.chjj.compton'
object='/com/github/chjj/compton'
type_win='uint32'
type_enum='uint16'
# === DBus methods ===
# List all window ID compton manages (except destroyed ones)
dbus-send --print-reply --dest="$service" "$object" "${interface}.list_win"
# Get window ID of currently focused window
focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | sed -n 's/^[[:space:]]*'${type_win}'\s*\([[:digit:]]*\).*/\1/p')
if [ -n "$focused" ]; then
# Get invert_color_force property of the window
dbus-send --print-reply --dest="$service" "$object" "${interface}.win_get" "${type_win}:${focused}" string:invert_color_force
# Set the window to have inverted color
dbus-send --print-reply --dest="$service" "$object" "${interface}.win_set" "${type_win}:${focused}" string:invert_color_force "${type_enum}:1"
else
echo "Cannot find focused window."
fi
# Set the clear_shadow setting to true
dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:clear_shadow boolean:true
# Get the clear_shadow setting
dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_get" string:clear_shadow
# Reset compton
sleep 3
dbus-send --print-reply --dest="$service" "$object" "${interface}.reset"

1370
src/common.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011, Christopher Jeffrey
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE for more information.
*
*/
@ -837,7 +837,7 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) {
// Check if it's a mapped client window
if (WIN_EVMODE_CLIENT == mode
|| ((w = find_toplevel(ps, wid)) && IsViewable == w->a.map_state)) {
if (ps->o.frame_opacity || ps->o.track_wdata
if (ps->o.frame_opacity || ps->o.track_wdata || ps->track_atom_lst
|| ps->o.detect_client_opacity)
evmask |= PropertyChangeMask;
}
@ -1361,8 +1361,7 @@ paint_preprocess(session_t *ps, win *list) {
// Disable unredirection for multi-screen setups
if (WMODE_SOLID == w->mode
&& (!w->frame_opacity || !win_has_frame(w))
&& win_is_fullscreen(ps, w)
&& ScreenCount(ps->dpy) <= 1)
&& win_is_fullscreen(ps, w))
ps->unredir_possible = true;
}
@ -1848,6 +1847,9 @@ add_damage(session_t *ps, XserverRegion damage) {
static void
repair_win(session_t *ps, win *w) {
if (IsViewable != w->a.map_state)
return;
XserverRegion parts;
if (!w->damaged) {
@ -1986,6 +1988,13 @@ map_win(session_t *ps, Window id) {
if (w->need_configure) {
configure_win(ps, &w->queue_configure);
}
#ifdef CONFIG_DBUS
// Send D-Bus signal
if (ps->o.dbus) {
cdbus_ev_win_mapped(ps, w);
}
#endif
}
static void
@ -2042,6 +2051,13 @@ unmap_win(session_t *ps, Window id) {
// don't care about properties anymore
win_ev_stop(ps, w);
#ifdef CONFIG_DBUS
// Send D-Bus signal
if (ps->o.dbus) {
cdbus_ev_win_unmapped(ps, w);
}
#endif
}
static opacity_t
@ -2583,8 +2599,8 @@ add_win(session_t *ps, Window id, Window prev) {
// Fill structure
new->id = id;
set_ignore_next(ps);
set_ignore_next(ps);
if (!XGetWindowAttributes(ps->dpy, id, &new->a)) {
// Failed to get window attributes. Which probably means, the window
// is gone already.
@ -2608,7 +2624,14 @@ add_win(session_t *ps, Window id, Window prev) {
new->next = *p;
*p = new;
if (map_state == IsViewable) {
#ifdef CONFIG_DBUS
// Send D-Bus signal
if (ps->o.dbus) {
cdbus_ev_win_added(ps, new);
}
#endif
if (IsViewable == map_state) {
map_win(ps, id);
}
@ -2811,6 +2834,13 @@ destroy_win(session_t *ps, Window id) {
// Fading out the window
w->flags |= WFLAG_OPCT_CHANGE;
set_fade_callback(ps, w, destroy_callback, false);
#ifdef CONFIG_DBUS
// Send D-Bus signal
if (ps->o.dbus) {
cdbus_ev_win_destroyed(ps, w);
}
#endif
}
}
@ -3252,6 +3282,58 @@ win_get_class(session_t *ps, win *w) {
return true;
}
#ifdef CONFIG_DBUS
/** @name DBus hooks
*/
///@{
/**
* Set w->shadow_force of a window.
*/
void
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);
}
}
/**
* Set w->focused_force of a window.
*/
void
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);
}
}
/**
* Set w->invert_color_force of a window.
*/
void
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);
}
}
/**
* Force a full-screen repaint.
*/
void
force_repaint(session_t *ps) {
XserverRegion reg = None;
if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) {
ps->ev_received = true;
add_damage(ps, reg);
}
}
//!@}
#endif
#ifdef DEBUG_EVENTS
static int
ev_serial(XEvent *ev) {
@ -3647,6 +3729,17 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
}
}
// Check for other atoms we are tracking
for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
if (platom->atom == ev->atom) {
win *w = find_win(ps, ev->window);
if (!w)
w = find_toplevel(ps, ev->window);
if (w)
win_on_wdata_change(ps, w);
break;
}
}
}
inline static void
@ -5129,8 +5222,7 @@ timeout_get_poll_time(session_t *ps) {
// Traverse throught the timeout linked list
for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) {
if (ptmout->enabled) {
// Truncate the last run time to the closest interval
time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval;
time_ms_t newrun = timeout_get_newrun(ptmout);
if (newrun <= now) {
wait = 0;
break;
@ -5149,7 +5241,7 @@ timeout_get_poll_time(session_t *ps) {
/**
* Insert a new timeout.
*/
static timeout_t *
timeout_t *
timeout_insert(session_t *ps, time_ms_t interval,
bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) {
const static timeout_t tmout_def = {
@ -5184,7 +5276,7 @@ timeout_insert(session_t *ps, time_ms_t interval,
* @return true if we have found the timeout and removed it, false
* otherwise
*/
static bool
bool
timeout_drop(session_t *ps, timeout_t *prm) {
timeout_t **pplast = &ps->tmout_lst;
@ -5224,12 +5316,14 @@ 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 = ptmout->next) {
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 = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval;
time_ms_t newrun = timeout_get_newrun(ptmout);
if (newrun <= max) {
ret = true;
timeout_invoke(ps, ptmout);
@ -5243,7 +5337,7 @@ timeout_run(session_t *ps) {
/**
* Invoke a timeout.
*/
static void
void
timeout_invoke(session_t *ps, timeout_t *ptmout) {
const time_ms_t now = get_time_ms();
ptmout->lastrun = now;
@ -5301,6 +5395,12 @@ mainloop(session_t *ps) {
return true;
}
#ifdef CONFIG_DBUS
if (ps->o.dbus) {
cdbus_loop(ps);
}
#endif
if (ps->reset)
return false;
@ -5531,7 +5631,13 @@ session_init(session_t *ps_old, int argc, char **argv) {
.atom_ewmh_active_win = None,
.atom_compton_shadow = None,
.atom_win_type = None,
.atoms_wintypes = { 0 }
.atoms_wintypes = { 0 },
.track_atom_lst = NULL,
#ifdef CONFIG_DBUS
.dbus_conn = NULL,
.dbus_service = NULL,
#endif
};
// Allocate a session and copy default values into it
@ -5740,6 +5846,19 @@ session_init(session_t *ps_old, int argc, char **argv) {
XUngrabServer(ps->dpy);
// Initialize DBus
if (ps->o.dbus) {
#ifdef CONFIG_DBUS
cdbus_init(ps);
if (!ps->dbus_conn) {
cdbus_destroy(ps);
ps->o.dbus = false;
}
#else
printf_errfq(1, "(): DBus support not compiled in!");
#endif
}
// Fork to background, if asked
if (ps->o.fork_after_register) {
if (!fork_after(ps)) {
@ -5770,6 +5889,14 @@ session_destroy(session_t *ps) {
// Stop listening to events on root window
XSelectInput(ps->dpy, ps->root, 0);
#ifdef CONFIG_DBUS
// Kill DBus connection
if (ps->o.dbus)
cdbus_destroy(ps);
free(ps->dbus_service);
#endif
// Free window linked list
{
win *next = NULL;

File diff suppressed because it is too large Load Diff

920
src/dbus.c Normal file
View File

@ -0,0 +1,920 @@
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE for more information.
*
*/
#include "dbus.h"
/**
* Initialize D-Bus connection.
*/
bool
cdbus_init(session_t *ps) {
DBusError err = { };
// Initialize
dbus_error_init(&err);
// Connect to D-Bus
// Use dbus_bus_get_private() so we can fully recycle it ourselves
ps->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): D-Bus connection failed (%s).", err.message);
dbus_error_free(&err);
return false;
}
if (!ps->dbus_conn) {
printf_errf("(): D-Bus connection failed for unknown reason.");
return false;
}
// Avoid exiting on disconnect
dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false);
// Request service name
{
// Get display name
char *display = DisplayString(ps->dpy);
if (!display)
display = "unknown";
display = mstrcpy(display);
// Convert all special characters in display name to underscore
{
char *pdisp = display;
while (*pdisp) {
if (!isalnum(*pdisp))
*pdisp = '_';
++pdisp;
}
}
// Build service name
char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", display);
ps->dbus_service = service;
free(display);
display = NULL;
// Request for the name
int ret = dbus_bus_request_name(ps->dbus_conn, service,
DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): Failed to obtain D-Bus name (%s).", err.message);
dbus_error_free(&err);
}
if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret
&& DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) {
printf_errf("(): Failed to become the primary owner of requested "
"D-Bus name (%d).", ret);
}
}
// Add watch handlers
if (!dbus_connection_set_watch_functions(ps->dbus_conn,
cdbus_callback_add_watch, cdbus_callback_remove_watch,
cdbus_callback_watch_toggled, ps, NULL)) {
printf_errf("(): Failed to add D-Bus watch functions.");
return false;
}
// Add timeout handlers
if (!dbus_connection_set_timeout_functions(ps->dbus_conn,
cdbus_callback_add_timeout, cdbus_callback_remove_timeout,
cdbus_callback_timeout_toggled, ps, NULL)) {
printf_errf("(): Failed to add D-Bus timeout functions.");
return false;
}
// Add match
dbus_bus_add_match(ps->dbus_conn,
"type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): Failed to add D-Bus match.");
dbus_error_free(&err);
return false;
}
return true;
}
/**
* Destroy D-Bus connection.
*/
void
cdbus_destroy(session_t *ps) {
if (ps->dbus_conn) {
// Release DBus name firstly
if (ps->dbus_service) {
DBusError err = { };
dbus_error_init(&err);
dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): Failed to release DBus name (%s).",
err.message);
dbus_error_free(&err);
}
}
// Close and unref the connection
dbus_connection_close(ps->dbus_conn);
dbus_connection_unref(ps->dbus_conn);
}
}
/** @name DBusTimeout handling
*/
///@{
/**
* Callback for adding D-Bus timeout.
*/
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);
return (bool) ptmout;
}
/**
* Callback for removing D-Bus timeout.
*/
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);
}
/**
* Callback for toggling a D-Bus timeout.
*/
static void
cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) {
timeout_t *ptmout = 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);
}
}
/**
* 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
*/
///@{
/**
* 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));
// Always return true
return TRUE;
}
/**
* Callback for removing D-Bus watch.
*/
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));
}
/**
* Callback for toggling D-Bus watch status.
*/
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);
}
}
///@}
/** @name Message argument appending callbacks
*/
///@{
/**
* Callback to append a bool argument to a message.
*/
static bool
cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) {
assert(data);
dbus_bool_t val = *(const bool *) data;
if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append a Window argument to a message.
*/
static bool
cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) {
assert(data);
cdbus_window_t val = *(const Window *)data;
if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append an cdbus_enum_t argument to a message.
*/
static bool
cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data) {
assert(data);
if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append a string argument to a message.
*/
static bool
cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) {
const char *str = data;
if (!str)
str = "";
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append all window IDs to a message.
*/
static bool
cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) {
// Get the number of wids we are to include
unsigned count = 0;
for (win *w = ps->list; w; w = w->next) {
if (!w->destroyed)
++count;
}
// Allocate memory for an array of window IDs
cdbus_window_t *arr = malloc(sizeof(cdbus_window_t) * count);
if (!arr) {
printf_errf("(): Failed to allocate memory for window ID array.");
return false;
}
// Build the array
{
cdbus_window_t *pcur = arr;
for (win *w = ps->list; w; w = w->next) {
if (!w->destroyed) {
*pcur = w->id;
++pcur;
assert(pcur <= arr + count);
}
}
assert(pcur == arr + count);
}
// Append arguments
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW,
&arr, count, DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
free(arr);
return false;
}
free(arr);
return true;
}
///@}
/**
* Send a D-Bus signal.
*
* @param ps current session
* @param name signal name
* @param func a function that modifies the built message, to, for example,
* add an argument
* @param data data pointer to pass to the function
*/
static bool
cdbus_signal(session_t *ps, const char *name,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data) {
DBusMessage* msg = NULL;
// Create a signal
msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME,
name);
if (!msg) {
printf_errf("(): Failed to create D-Bus signal.");
return false;
}
// Append arguments onto message
if (func && !func(ps, msg, data)) {
dbus_message_unref(msg);
return false;
}
// Send the message and flush the connection
if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) {
printf_errf("(): Failed to send D-Bus signal.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
// Free the message
dbus_message_unref(msg);
return true;
}
/**
* Send a D-Bus reply.
*
* @param ps current session
* @param srcmsg original message
* @param func a function that modifies the built message, to, for example,
* add an argument
* @param data data pointer to pass to the function
*/
static bool
cdbus_reply(session_t *ps, DBusMessage *srcmsg,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data) {
DBusMessage* msg = NULL;
// Create a reply
msg = dbus_message_new_method_return(srcmsg);
if (!msg) {
printf_errf("(): Failed to create D-Bus reply.");
return false;
}
// Append arguments onto message
if (func && !func(ps, msg, data)) {
dbus_message_unref(msg);
return false;
}
// Send the message and flush the connection
if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) {
printf_errf("(): Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
// Free the message
dbus_message_unref(msg);
return true;
}
/**
* Send a D-Bus error reply.
*
* @param ps current session
* @param msg the new error DBusMessage
*/
static bool
cdbus_reply_errm(session_t *ps, DBusMessage *msg) {
if (!msg) {
printf_errf("(): Failed to create D-Bus reply.");
return false;
}
// Send the message and flush the connection
if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) {
printf_errf("(): Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
// Free the message
dbus_message_unref(msg);
return true;
}
/**
* Get n-th argument of a D-Bus message.
*
* @param count the position of the argument to get, starting from 0
* @param type libdbus type number of the type
* @param pdest pointer to the target
* @return true if successful, false otherwise.
*/
static bool
cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
assert(count >= 0);
DBusMessageIter iter = { };
if (!dbus_message_iter_init(msg, &iter)) {
printf_errf("(): Message has no argument.");
return false;
}
{
const int oldcount = count;
while (count) {
if (!dbus_message_iter_next(&iter)) {
printf_errf("(): Failed to find argument %d.", oldcount);
return false;
}
--count;
}
}
if (type != dbus_message_iter_get_arg_type(&iter)) {
printf_errf("(): Argument has incorrect type.");
return false;
}
dbus_message_iter_get_basic(&iter, 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("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_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.
*/
static bool
cdbus_process_list_win(session_t *ps, DBusMessage *msg) {
cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL);
return true;
}
/**
* Process a win_get D-Bus request.
*/
static bool
cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_window_t wid = None;
const char *target = NULL;
DBusError err = { };
if (!dbus_message_get_args(msg, &err,
CDBUS_TYPE_WINDOW, &wid,
DBUS_TYPE_STRING, &target,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to parse argument of \"win_get\" (%s).",
err.message);
dbus_error_free(&err);
return false;
}
win *w = find_win(ps, wid);
if (!w) {
printf_errf("(): Window %#010x not found.", wid);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
return true;
}
#define cdbus_m_win_get_do(tgt, apdarg_func) \
if (!strcmp(MSTR(tgt), target)) { \
apdarg_func(ps, msg, w->tgt); \
return true; \
}
cdbus_m_win_get_do(client_win, cdbus_reply_wid);
cdbus_m_win_get_do(damaged, cdbus_reply_bool);
cdbus_m_win_get_do(destroyed, cdbus_reply_bool);
cdbus_m_win_get_do(window_type, cdbus_reply_enum);
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
cdbus_m_win_get_do(leader, cdbus_reply_wid);
cdbus_m_win_get_do(focused_real, cdbus_reply_bool);
cdbus_m_win_get_do(shadow_force, cdbus_reply_enum);
cdbus_m_win_get_do(focused_force, cdbus_reply_enum);
cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum);
if (!strcmp("map_state", target)) {
cdbus_reply_bool(ps, msg, w->a.map_state);
return true;
}
#undef cdbus_m_win_get_do
printf_errf("(): No matching target found.");
return false;
}
/**
* Process a win_set D-Bus request.
*/
static bool
cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
cdbus_window_t wid = None;
const char *target = NULL;
DBusError err = { };
if (!dbus_message_get_args(msg, &err,
CDBUS_TYPE_WINDOW, &wid,
DBUS_TYPE_STRING, &target,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to parse argument of \"win_set\" (%s).",
err.message);
dbus_error_free(&err);
return false;
}
win *w = find_win(ps, wid);
if (!w) {
printf_errf("(): Window %#010x not found.", wid);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
return true;
}
ps->ev_received = true;
#define cdbus_m_win_set_do(tgt, type, real_type) \
if (!strcmp(MSTR(tgt), target)) { \
real_type val; \
if (!cdbus_msg_get_arg(msg, 2, type, &val)) \
return false; \
w->tgt = val; \
goto cdbus_process_win_set_success; \
}
if (!strcmp("shadow_force", target)) {
cdbus_enum_t val = UNSET;
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
return false;
win_set_shadow_force(ps, w, val);
goto cdbus_process_win_set_success;
}
if (!strcmp("focused_force", target)) {
cdbus_enum_t val = UNSET;
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
return false;
win_set_focused_force(ps, w, val);
goto cdbus_process_win_set_success;
}
if (!strcmp("invert_color_force", target)) {
cdbus_enum_t val = UNSET;
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
return false;
win_set_invert_color_force(ps, w, val);
goto cdbus_process_win_set_success;
}
#undef cdbus_m_win_set_do
printf_errf("(): No matching target found.");
return false;
cdbus_process_win_set_success:
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
return true;
}
/**
* Process a find_win D-Bus request.
*/
static bool
cdbus_process_find_win(session_t *ps, DBusMessage *msg) {
const char *target = NULL;
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
return false;
Window wid = None;
// Find window by client window
if (!strcmp("client", target)) {
cdbus_window_t client = None;
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client))
return false;
win *w = find_toplevel(ps, client);
if (w)
wid = w->id;
}
// Find focused window
else if (!strcmp("focused", target)) {
win *w = find_focused(ps);
if (w)
wid = w->id;
}
else {
printf_errf("(): No matching target found.");
return false;
}
cdbus_reply_wid(ps, msg, wid);
return true;
}
/**
* Process a opts_get D-Bus request.
*/
static bool
cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
const char *target = NULL;
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
return false;
#define cdbus_m_opts_get_do(tgt, apdarg_func) \
if (!strcmp(MSTR(tgt), target)) { \
apdarg_func(ps, msg, ps->o.tgt); \
return true; \
}
cdbus_m_opts_get_do(display, cdbus_reply_string);
cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool);
cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool);
cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool);
cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool);
cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool);
cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool);
cdbus_m_opts_get_do(logpath, cdbus_reply_string);
cdbus_m_opts_get_do(synchronize, cdbus_reply_bool);
cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool);
#undef cdbus_m_opts_get_do
printf_errf("(): No matching target found.");
return false;
}
/**
* Process a opts_set D-Bus request.
*/
static bool
cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
const char *target = NULL;
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
return false;
#define cdbus_m_opts_set_do(tgt, type, real_type) \
if (!strcmp(MSTR(tgt), target)) { \
real_type val; \
if (!cdbus_msg_get_arg(msg, 1, type, &val)) \
return false; \
ps->o.tgt = val; \
goto cdbus_process_opts_set_success; \
}
// unredir_if_possible
if (!strcmp("unredir_if_possible", target)) {
dbus_bool_t val = FALSE;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
return false;
if (ps->o.unredir_if_possible != val) {
ps->o.unredir_if_possible = val;
ps->ev_received = true;
}
goto cdbus_process_opts_set_success;
}
// clear_shadow
if (!strcmp("clear_shadow", target)) {
dbus_bool_t val = FALSE;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
return false;
if (ps->o.clear_shadow != val) {
ps->o.clear_shadow = val;
force_repaint(ps);
}
goto cdbus_process_opts_set_success;
}
#undef cdbus_m_opts_set_do
printf_errf("(): No matching target found.");
return false;
cdbus_process_opts_set_success:
if (!dbus_message_get_no_reply(msg))
cdbus_reply_bool(ps, msg, true);
return true;
}
/**
* Process an Introspect D-Bus request.
*/
static bool
cdbus_process_introspect(session_t *ps, DBusMessage *msg) {
const static char *str_introspect =
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node name='" CDBUS_OBJECT_NAME "'>\n"
" <interface name='" CDBUS_INTERFACE_NAME "'>\n"
" <signal name='win_added'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='win_destroyed'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='win_mapped'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='win_unmapped'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <method name='reset' />\n"
" </interface>\n"
"</node>\n";
cdbus_reply_string(ps, msg, str_introspect);
return true;
}
///@}
/** @name Core callbacks
*/
///@{
void
cdbus_ev_win_added(session_t *ps, win *w) {
if (ps->dbus_conn)
cdbus_signal_wid(ps, "win_added", w->id);
}
void
cdbus_ev_win_destroyed(session_t *ps, win *w) {
if (ps->dbus_conn)
cdbus_signal_wid(ps, "win_destroyed", w->id);
}
void
cdbus_ev_win_mapped(session_t *ps, win *w) {
if (ps->dbus_conn)
cdbus_signal_wid(ps, "win_mapped", w->id);
}
void
cdbus_ev_win_unmapped(session_t *ps, win *w) {
if (ps->dbus_conn)
cdbus_signal_wid(ps, "win_unmapped", w->id);
}
//!@}

213
src/dbus.h Normal file
View File

@ -0,0 +1,213 @@
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE for more information.
*
*/
#include "common.h"
#include <ctype.h>
#define CDBUS_SERVICE_NAME "com.github.chjj.compton"
#define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME
#define CDBUS_OBJECT_NAME "/com/github/chjj/compton"
#define CDBUS_ERROR_PREFIX CDBUS_INTERFACE_NAME ".error"
#define CDBUS_ERROR_UNKNOWN CDBUS_ERROR_PREFIX ".unknown"
#define CDBUS_ERROR_UNKNOWN_S "Well, I don't know what happened. Do you?"
#define CDBUS_ERROR_BADMSG CDBUS_ERROR_PREFIX ".bad_message"
#define CDBUS_ERROR_BADMSG_S "Unrecognized command. Beware compton " \
"cannot make you a sandwich."
#define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument"
#define CDBUS_ERROR_BADARG_S "Something wrong in arguments?"
#define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window"
#define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found."
#define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden"
#define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied."
// Window type
typedef uint32_t cdbus_window_t;
#define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32
#define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING
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);
static bool
cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data);
static bool
cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data);
static bool
cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data);
static bool
cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data);
/** @name DBus signal sending
*/
///@{
static bool
cdbus_signal(session_t *ps, const char *name,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data);
/**
* Send a signal with no argument.
*/
static inline bool
cdbus_signal_noarg(session_t *ps, const char *name) {
return cdbus_signal(ps, name, NULL, NULL);
}
/**
* Send a signal with a Window ID as argument.
*/
static inline bool
cdbus_signal_wid(session_t *ps, const char *name, Window wid) {
return cdbus_signal(ps, name, cdbus_apdarg_wid, &wid);
}
///@}
/** @name DBus reply sending
*/
///@{
static bool
cdbus_reply(session_t *ps, DBusMessage *srcmsg,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data);
static bool
cdbus_reply_errm(session_t *ps, DBusMessage *msg);
#define cdbus_reply_err(ps, srcmsg, err_name, err_format, ...) \
cdbus_reply_errm((ps), dbus_message_new_error_printf((srcmsg), (err_name), (err_format), ## __VA_ARGS__))
/**
* Send a reply with no argument.
*/
static inline bool
cdbus_reply_noarg(session_t *ps, DBusMessage *srcmsg) {
return cdbus_reply(ps, srcmsg, NULL, NULL);
}
/**
* Send a reply with a bool argument.
*/
static inline bool
cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval);
}
/**
* Send a reply with a wid argument.
*/
static inline bool
cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, Window wid) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid);
}
/**
* Send a reply with a string argument.
*/
static inline bool
cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str);
}
/**
* Send a reply with a enum argument.
*/
static inline bool
cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval);
}
///@}
static bool
cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest);
/**
* Return a string representation of a D-Bus message type.
*/
static inline const char *
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);
///@}