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).
pull/2/head
Richard Grenville 12 years ago
parent a5dc829944
commit 3213231298

1370
common.h

File diff suppressed because it is too large Load Diff

@ -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
dbus.c

@ -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
dbus.h

@ -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);
///@}
Loading…
Cancel
Save