Files
picom/src/dbus.c
Maxim Solovyov 986b3c1cb3 core: drop xinerama
there are two x extensions for working with monitors (especially
multiple): xinerama and randr. xinerama is old, feature-poor and in
general isn't used anymore compared to the randr: new, feature-rich and
widely-used. for some reason we were using both of them, so let's drop
xinerama to keep things simple, clean and small. and to be modern.

the drop was done in three steps:
* first step was to replace all the xinerama-based code with the
  randr-based one and to replace or remove all the xinerama mentions;
* second step was to replace the xinerama's terminology with the
  randr's one. xinerama was referring only to the word "screen", while
  randr refers to multiple words and i think the word "monitor" is the
  most suitable for us and, hopefully, clear both to a contributor and
  to an end user;
* third step was to refactor the new randr-based code if needed and to
  address related todo's (mostly about moving related functions
  elsewhere).

all the steps were done well except addressing a leftover todo about
moving the win_update_monitor function to the x.c which wasn't done.

the xinerama-shadow-crop option was renamed to crop-shadow-to-monitor,
but it's previous name is still accepted, has effect and the
deprecation message is printed to preserve backwards-compatibility.
2023-01-29 10:51:12 +03:00

1704 lines
50 KiB
C

// SPDX-License-Identifier: MIT
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE-mit for more information.
*
*/
#include <X11/Xlib.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <xcb/xcb.h>
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "list.h"
#include "log.h"
#include "string_utils.h"
#include "types.h"
#include "uthash_extra.h"
#include "utils.h"
#include "win.h"
#include "dbus.h"
struct cdbus_data {
/// DBus connection.
DBusConnection *dbus_conn;
/// DBus service name.
char *dbus_service;
};
// 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 uint32_t cdbus_enum_t;
#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT32
#define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT32_AS_STRING
#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 "Failed to parse argument %d: %s"
#define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window"
#define CDBUS_ERROR_BADWIN_S "Requested window %#010x not found."
#define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target"
#define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found."
#define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden"
#define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied."
#define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom"
#define CDBUS_ERROR_CUSTOM_S "%s"
#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__))
#define PICOM_WINDOW_INTERFACE "picom.Window"
#define PICOM_COMPOSITOR_INTERFACE "picom.Compositor"
static DBusHandlerResult cdbus_process(DBusConnection *conn, DBusMessage *m, void *);
static DBusHandlerResult cdbus_process_windows(DBusConnection *c, DBusMessage *msg, void *ud);
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.
*/
bool cdbus_init(session_t *ps, const char *uniq) {
auto cd = cmalloc(struct cdbus_data);
cd->dbus_service = NULL;
// Set ps->dbus_data here because add_watch functions need it
ps->dbus_data = cd;
DBusError err = {};
// Initialize
dbus_error_init(&err);
// Connect to D-Bus
// Use dbus_bus_get_private() so we can fully recycle it ourselves
cd->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
log_error("D-Bus connection failed (%s).", err.message);
dbus_error_free(&err);
goto fail;
}
if (!cd->dbus_conn) {
log_error("D-Bus connection failed for unknown reason.");
goto fail;
}
// Avoid exiting on disconnect
dbus_connection_set_exit_on_disconnect(cd->dbus_conn, false);
// Request service name
{
// Build service name
size_t service_len = strlen(CDBUS_SERVICE_NAME) + strlen(uniq) + 2;
char *service = ccalloc(service_len, char);
snprintf(service, service_len, "%s.%s", CDBUS_SERVICE_NAME, uniq);
// Make a valid dbus name by converting non alphanumeric characters to
// underscore
char *tmp = service + strlen(CDBUS_SERVICE_NAME) + 1;
while (*tmp) {
if (!isalnum((unsigned char)*tmp)) {
*tmp = '_';
}
tmp++;
}
cd->dbus_service = service;
// Request for the name
int ret = dbus_bus_request_name(cd->dbus_conn, service,
DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
if (dbus_error_is_set(&err)) {
log_error("Failed to obtain D-Bus name (%s).", err.message);
dbus_error_free(&err);
goto fail;
}
if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret &&
DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) {
log_error("Failed to become the primary owner of requested D-Bus "
"name (%d).",
ret);
goto fail;
}
}
// Add watch handlers
if (!dbus_connection_set_watch_functions(cd->dbus_conn, cdbus_callback_add_watch,
cdbus_callback_remove_watch,
cdbus_callback_watch_toggled, ps, NULL)) {
log_error("Failed to add D-Bus watch functions.");
goto fail;
}
// Add timeout handlers
if (!dbus_connection_set_timeout_functions(
cd->dbus_conn, cdbus_callback_add_timeout, cdbus_callback_remove_timeout,
cdbus_callback_timeout_toggled, ps, NULL)) {
log_error("Failed to add D-Bus timeout functions.");
goto fail;
}
// Add match
dbus_bus_add_match(cd->dbus_conn,
"type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err);
if (dbus_error_is_set(&err)) {
log_error("Failed to add D-Bus match.");
dbus_error_free(&err);
goto fail;
}
dbus_connection_register_object_path(
cd->dbus_conn, CDBUS_OBJECT_NAME,
(DBusObjectPathVTable[]){{NULL, cdbus_process}}, ps);
dbus_connection_register_fallback(
cd->dbus_conn, CDBUS_OBJECT_NAME "/windows",
(DBusObjectPathVTable[]){{NULL, cdbus_process_windows}}, ps);
return true;
fail:
ps->dbus_data = NULL;
free(cd->dbus_service);
free(cd);
return false;
}
/**
* Destroy D-Bus connection.
*/
void cdbus_destroy(session_t *ps) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
// Release DBus name firstly
if (cd->dbus_service) {
DBusError err = {};
dbus_error_init(&err);
dbus_bus_release_name(cd->dbus_conn, cd->dbus_service, &err);
if (dbus_error_is_set(&err)) {
log_error("Failed to release DBus name (%s).", err.message);
dbus_error_free(&err);
}
free(cd->dbus_service);
}
// Close and unref the connection
dbus_connection_close(cd->dbus_conn);
dbus_connection_unref(cd->dbus_conn);
}
free(cd);
}
/** @name DBusTimeout handling
*/
///@{
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 attr_unused, ev_timer *w, int revents attr_unused) {
ev_dbus_timer *t = (void *)w;
dbus_timeout_handle(t->t);
}
/**
* Callback for adding D-Bus timeout.
*/
static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) {
session_t *ps = data;
auto t = ccalloc(1, ev_dbus_timer);
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);
if (dbus_timeout_get_enabled(timeout))
ev_timer_start(ps->loop, &t->w);
return true;
}
/**
* Callback for removing D-Bus timeout.
*/
static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) {
session_t *ps = data;
ev_dbus_timer *t = dbus_timeout_get_data(timeout);
assert(t);
ev_timer_stop(ps->loop, &t->w);
free(t);
}
/**
* Callback for toggling a D-Bus timeout.
*/
static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) {
session_t *ps = data;
ev_dbus_timer *t = dbus_timeout_get_data(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);
}
}
///@}
/** @name DBusWatch handling
*/
///@{
typedef struct ev_dbus_io {
ev_io w;
struct cdbus_data *cd;
DBusWatch *dw;
} ev_dbus_io;
void cdbus_io_callback(EV_P attr_unused, 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->cd->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) {
session_t *ps = data;
auto w = ccalloc(1, ev_dbus_io);
w->dw = watch;
w->cd = ps->dbus_data;
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;
}
/**
* Callback for removing D-Bus watch.
*/
static void cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
session_t *ps = data;
ev_dbus_io *w = dbus_watch_get_data(watch);
ev_io_stop(ps->loop, &w->w);
free(w);
}
/**
* Callback for toggling D-Bus watch status.
*/
static void cdbus_callback_watch_toggled(DBusWatch *watch, void *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);
}
///@}
/** @name Message argument appending callbacks
*/
///@{
/**
* Callback to append a bool argument to a message.
*/
static bool cdbus_apdarg_bool(session_t *ps attr_unused, 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)) {
log_error("Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append an int32 argument to a message.
*/
static bool cdbus_apdarg_int32(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, DBUS_TYPE_INT32, data, DBUS_TYPE_INVALID)) {
log_error("Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append an uint32 argument to a message.
*/
static bool
cdbus_apdarg_uint32(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, data, DBUS_TYPE_INVALID)) {
log_error("Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append a double argument to a message.
*/
static bool
cdbus_apdarg_double(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, DBUS_TYPE_DOUBLE, data, DBUS_TYPE_INVALID)) {
log_error("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 attr_unused, DBusMessage *msg, const void *data) {
assert(data);
cdbus_window_t val = *(const xcb_window_t *)data;
if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, DBUS_TYPE_INVALID)) {
log_error("Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append a Window argument to a message as a variant.
*/
static bool
cdbus_append_wid_variant(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
assert(data);
cdbus_window_t val = *(const xcb_window_t *)data;
DBusMessageIter it, it2;
dbus_message_iter_init_append(msg, &it);
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT,
CDBUS_TYPE_WINDOW_STR, &it2)) {
return false;
}
if (!dbus_message_iter_append_basic(&it2, CDBUS_TYPE_WINDOW, &val)) {
log_error("Failed to append argument.");
return false;
}
if (!dbus_message_iter_close_container(&it, &it2)) {
return false;
}
return true;
}
/**
* Callback to append a bool argument to a message as a variant.
*/
static bool
cdbus_append_bool_variant(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
assert(data);
dbus_bool_t val = *(const bool *)data;
DBusMessageIter it, it2;
dbus_message_iter_init_append(msg, &it);
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT,
DBUS_TYPE_BOOLEAN_AS_STRING, &it2)) {
return false;
}
if (!dbus_message_iter_append_basic(&it2, DBUS_TYPE_BOOLEAN, &val)) {
log_error("Failed to append argument.");
return false;
}
if (!dbus_message_iter_close_container(&it, &it2)) {
return false;
}
return true;
}
/**
* Callback to append an cdbus_enum_t argument to a message.
*/
static bool cdbus_apdarg_enum(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
assert(data);
if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, DBUS_TYPE_INVALID)) {
log_error("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 attr_unused, 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)) {
log_error("Failed to append argument.");
return false;
}
return true;
}
/**
* Callback to append a string argument to a message as a variant.
*/
static bool
cdbus_append_string_variant(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
const char *str = *(const char **)data;
if (!str) {
str = "";
}
DBusMessageIter it, it2;
dbus_message_iter_init_append(msg, &it);
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING, &it2)) {
return false;
}
if (!dbus_message_iter_append_basic(&it2, DBUS_TYPE_STRING, &str)) {
log_error("Failed to append argument.");
return false;
}
if (!dbus_message_iter_close_container(&it, &it2)) {
return false;
}
return true;
}
static bool cdbus_append_empty_dict(session_t *ps attr_unused, DBusMessage *msg,
const void *data attr_unused) {
DBusMessageIter it, it2;
dbus_message_iter_init_append(msg, &it);
if (!dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &it2)) {
return false;
}
if (!dbus_message_iter_close_container(&it, &it2)) {
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 attr_unused) {
// Get the number of wids we are to include
unsigned count = 0;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
++count;
}
if (!count) {
// Nothing to append
return true;
}
// Allocate memory for an array of window IDs
auto arr = ccalloc(count, cdbus_window_t);
// Build the array
cdbus_window_t *pcur = arr;
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
*pcur = w->id;
++pcur;
}
assert(pcur == arr + count);
// Append arguments
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr,
count, DBUS_TYPE_INVALID)) {
log_error("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 *interface, const char *name,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data) {
struct cdbus_data *cd = ps->dbus_data;
DBusMessage *msg = NULL;
// Create a signal
msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, interface, name);
if (!msg) {
log_error("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(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus signal.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(cd->dbus_conn);
// Free the message
dbus_message_unref(msg);
return true;
}
/**
* Send a signal with a Window ID as argument.
*/
static inline bool
cdbus_signal_wid(session_t *ps, const char *interface, const char *name, xcb_window_t wid) {
return cdbus_signal(ps, interface, name, cdbus_apdarg_wid, &wid);
}
/**
* 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) {
struct cdbus_data *cd = ps->dbus_data;
DBusMessage *msg = NULL;
// Create a reply
msg = dbus_message_new_method_return(srcmsg);
if (!msg) {
log_error("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(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(cd->dbus_conn);
// Free the message
dbus_message_unref(msg);
return true;
}
/**
* 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 an int32 argument.
*/
static inline bool cdbus_reply_int32(session_t *ps, DBusMessage *srcmsg, int32_t val) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &val);
}
/**
* Send a reply with an int32 argument, cast from a long.
*/
static inline bool cdbus_reply_int32l(session_t *ps, DBusMessage *srcmsg, long val) {
int32_t tmp = (int32_t)val;
return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &tmp);
}
/**
* Send a reply with an uint32 argument.
*/
static inline bool cdbus_reply_uint32(session_t *ps, DBusMessage *srcmsg, uint32_t val) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_uint32, &val);
}
/**
* Send a reply with a double argument.
*/
static inline bool cdbus_reply_double(session_t *ps, DBusMessage *srcmsg, double val) {
return cdbus_reply(ps, srcmsg, cdbus_apdarg_double, &val);
}
/**
* Send a reply with a wid argument.
*/
static inline bool cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, xcb_window_t 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);
}
/**
* 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) {
struct cdbus_data *cd = ps->dbus_data;
if (!msg) {
log_error("Failed to create D-Bus reply.");
return false;
}
// Send the message and flush the connection
if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(cd->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)) {
log_error("Message has no argument.");
return false;
}
{
const int oldcount = count;
while (count) {
if (!dbus_message_iter_next(&iter)) {
log_error("Failed to find argument %d.", oldcount);
return false;
}
--count;
}
}
if (type != dbus_message_iter_get_arg_type(&iter)) {
log_error("Argument has incorrect type.");
return false;
}
dbus_message_iter_get_basic(&iter, pdest);
return true;
}
/** @name Message processing
*/
///@{
/**
* 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_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_t wid) {
const char *target = NULL;
const char *interface = NULL;
DBusError err = {};
if (!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &target, DBUS_TYPE_INVALID)) {
log_error("Failed to parse argument of \"Get\" (%s).", err.message);
dbus_error_free(&err);
return false;
}
if (strcmp(interface, PICOM_WINDOW_INTERFACE)) {
return false;
}
auto w = find_managed_win(ps, wid);
if (!w) {
log_error("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, member, apdarg_func) \
if (!strcmp(#tgt, target)) { \
cdbus_reply(ps, msg, apdarg_func, &w->member); \
return true; \
}
if (!strcmp("Mapped", target)) {
cdbus_reply(ps, msg, cdbus_append_bool_variant,
(bool[]){win_is_mapped_in_x(w)});
return true;
}
if (!strcmp(target, "Id")) {
cdbus_reply(ps, msg, cdbus_append_wid_variant, &w->base.id);
return true;
}
// next
if (!strcmp("Next", target)) {
cdbus_window_t next_id = 0;
if (!list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)) {
next_id = list_entry(w->base.stack_neighbour.next, struct win,
stack_neighbour)
->id;
}
cdbus_reply(ps, msg, cdbus_append_wid_variant, &next_id);
return true;
}
cdbus_m_win_get_do(ClientWin, client_win, cdbus_append_wid_variant);
cdbus_m_win_get_do(Leader, leader, cdbus_append_wid_variant);
cdbus_m_win_get_do(Name, name, cdbus_append_string_variant);
if (!strcmp("Type", target)) {
cdbus_reply(ps, msg, cdbus_append_string_variant, &WINTYPES[w->window_type]);
return true;
}
if (!strcmp("RawFocused", target)) {
cdbus_reply(ps, msg, cdbus_append_bool_variant,
(bool[]){win_is_focused_raw(ps, w)});
return true;
}
#undef cdbus_m_win_get_do
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
}
/**
* Process a win_get D-Bus request.
*/
static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_window_t wid = XCB_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)) {
log_error("Failed to parse argument of \"win_get\" (%s).", err.message);
dbus_error_free(&err);
return false;
}
auto w = find_managed_win(ps, wid);
if (!w) {
log_error("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(#tgt, target)) { \
apdarg_func(ps, msg, w->tgt); \
return true; \
}
if (!strcmp(target, "id")) {
cdbus_reply_wid(ps, msg, w->base.id);
return true;
}
// next
if (!strcmp("next", target)) {
cdbus_reply_wid(
ps, msg,
(list_node_is_last(&ps->window_stack, &w->base.stack_neighbour)
? 0
: list_entry(w->base.stack_neighbour.next, struct win, stack_neighbour)
->id));
return true;
}
// map_state
if (!strcmp("map_state", target)) {
cdbus_reply_bool(ps, msg, w->a.map_state);
return true;
}
cdbus_m_win_get_do(mode, cdbus_reply_enum);
cdbus_m_win_get_do(client_win, cdbus_reply_wid);
cdbus_m_win_get_do(ever_damaged, 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);
if (!strcmp("focused_raw", target)) {
cdbus_reply_bool(ps, msg, win_is_focused_raw(ps, w));
return true;
}
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
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);
cdbus_m_win_get_do(name, cdbus_reply_string);
cdbus_m_win_get_do(class_instance, cdbus_reply_string);
cdbus_m_win_get_do(class_general, cdbus_reply_string);
cdbus_m_win_get_do(role, cdbus_reply_string);
cdbus_m_win_get_do(opacity, cdbus_reply_double);
cdbus_m_win_get_do(opacity_target, cdbus_reply_double);
cdbus_m_win_get_do(has_opacity_prop, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32);
cdbus_m_win_get_do(opacity_is_set, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_set, cdbus_reply_double);
cdbus_m_win_get_do(frame_opacity, cdbus_reply_double);
if (!strcmp("left_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.left);
return true;
}
if (!strcmp("right_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.right);
return true;
}
if (!strcmp("top_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.top);
return true;
}
if (!strcmp("bottom_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.bottom);
return true;
}
cdbus_m_win_get_do(shadow, cdbus_reply_bool);
cdbus_m_win_get_do(invert_color, cdbus_reply_bool);
cdbus_m_win_get_do(blur_background, cdbus_reply_bool);
#undef cdbus_m_win_get_do
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
}
/**
* Process a win_set D-Bus request.
*/
static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
cdbus_window_t wid = XCB_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)) {
log_error("(): Failed to parse argument of \"win_set\" (%s).", err.message);
dbus_error_free(&err);
return false;
}
auto w = find_managed_win(ps, wid);
if (!w) {
log_error("Window %#010x not found.", wid);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
return 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("fade_force", target)) {
cdbus_enum_t val = UNSET;
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
return false;
win_set_fade_force(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
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
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;
xcb_window_t wid = XCB_NONE;
// Find window by client window
if (!strcmp("client", target)) {
cdbus_window_t client = XCB_NONE;
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client))
return false;
auto w = find_toplevel(ps, client);
if (w) {
wid = w->base.id;
}
}
// Find focused window
else if (!strcmp("focused", target)) {
if (ps->active_win && ps->active_win->state != WSTATE_UNMAPPED) {
wid = ps->active_win->base.id;
}
} else {
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
}
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(#tgt, target)) { \
apdarg_func(ps, msg, ps->o.tgt); \
return true; \
}
#define cdbus_m_opts_get_stub(tgt, apdarg_func, ret) \
if (!strcmp(#tgt, target)) { \
apdarg_func(ps, msg, ret); \
return true; \
}
// version
if (!strcmp("version", target)) {
cdbus_reply_string(ps, msg, PICOM_VERSION);
return true;
}
// pid
if (!strcmp("pid", target)) {
cdbus_reply_int32(ps, msg, getpid());
return true;
}
// display
if (!strcmp("display", target)) {
cdbus_reply_string(ps, msg, DisplayString(ps->dpy));
return true;
}
cdbus_m_opts_get_stub(config_file, cdbus_reply_string, "Unknown");
cdbus_m_opts_get_do(write_pid_path, 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(detect_rounded_corners, cdbus_reply_bool);
cdbus_m_opts_get_stub(paint_on_overlay, cdbus_reply_bool, ps->overlay != XCB_NONE);
// paint_on_overlay_id: Get ID of the X composite overlay window
if (!strcmp("paint_on_overlay_id", target)) {
cdbus_reply_uint32(ps, msg, ps->overlay);
return true;
}
cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool);
cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32l);
cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum);
cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum);
cdbus_m_opts_get_do(logpath, cdbus_reply_string);
cdbus_m_opts_get_stub(refresh_rate, cdbus_reply_int32, 0);
cdbus_m_opts_get_stub(sw_opti, cdbus_reply_bool, false);
cdbus_m_opts_get_do(vsync, cdbus_reply_bool);
if (!strcmp("backend", target)) {
assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]);
return true;
}
cdbus_m_opts_get_do(shadow_red, cdbus_reply_double);
cdbus_m_opts_get_do(shadow_green, cdbus_reply_double);
cdbus_m_opts_get_do(shadow_blue, cdbus_reply_double);
cdbus_m_opts_get_do(shadow_radius, cdbus_reply_int32);
cdbus_m_opts_get_do(shadow_offset_x, cdbus_reply_int32);
cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32);
cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double);
cdbus_m_opts_get_do(crop_shadow_to_monitor, cdbus_reply_bool);
cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32);
cdbus_m_opts_get_do(fade_in_step, cdbus_reply_double);
cdbus_m_opts_get_do(fade_out_step, cdbus_reply_double);
cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool);
cdbus_m_opts_get_do(blur_method, cdbus_reply_bool);
cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool);
cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool);
cdbus_m_opts_get_do(inactive_dim, cdbus_reply_double);
cdbus_m_opts_get_do(inactive_dim_fixed, cdbus_reply_bool);
cdbus_m_opts_get_do(max_brightness, cdbus_reply_double);
cdbus_m_opts_get_do(use_ewmh_active_win, cdbus_reply_bool);
cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool);
cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool);
cdbus_m_opts_get_do(use_damage, cdbus_reply_bool);
#ifdef CONFIG_OPENGL
cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool);
cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool);
#endif
#undef cdbus_m_opts_get_do
#undef cdbus_m_opts_get_stub
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
}
// XXX Remove this after header clean up
void queue_redraw(session_t *ps);
/**
* 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(#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; \
}
// fade_delta
if (!strcmp("fade_delta", target)) {
int32_t val = 0;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) {
return false;
}
if (val <= 0) {
return false;
}
ps->o.fade_delta = max2(val, 1);
goto cdbus_process_opts_set_success;
}
// fade_in_step
if (!strcmp("fade_in_step", target)) {
double val = 0.0;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
return false;
ps->o.fade_in_step = normalize_d(val);
goto cdbus_process_opts_set_success;
}
// fade_out_step
if (!strcmp("fade_out_step", target)) {
double val = 0.0;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
return false;
ps->o.fade_out_step = normalize_d(val);
goto cdbus_process_opts_set_success;
}
// no_fading_openclose
if (!strcmp("no_fading_openclose", target)) {
dbus_bool_t val = FALSE;
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
return false;
opts_set_no_fading_openclose(ps, 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;
queue_redraw(ps);
}
goto cdbus_process_opts_set_success;
}
// clear_shadow
if (!strcmp("clear_shadow", target)) {
goto cdbus_process_opts_set_success;
}
// track_focus
if (!strcmp("track_focus", target)) {
goto cdbus_process_opts_set_success;
}
// redirected_force
if (!strcmp("redirected_force", target)) {
cdbus_enum_t val = UNSET;
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val))
return false;
ps->o.redirected_force = val;
force_repaint(ps);
goto cdbus_process_opts_set_success;
}
// stoppaint_force
cdbus_m_opts_set_do(stoppaint_force, CDBUS_TYPE_ENUM, cdbus_enum_t);
#undef cdbus_m_opts_set_do
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
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) {
static const 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='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' direction='out' type='s' />\n"
" </method>\n"
" </interface>\n"
" <interface name='org.freedesktop.DBus.Peer'>\n"
" <method name='Ping' />\n"
" <method name='GetMachineId'>\n"
" <arg name='machine_uuid' direction='out' type='s' />\n"
" </method>\n"
" </interface>\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"
" <signal name='win_focusin'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='win_focusout'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <method name='reset' />\n"
" <method name='repaint' />\n"
" </interface>\n"
" <interface name='" PICOM_COMPOSITOR_INTERFACE "'>\n"
" <signal name='WinAdded'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='WinDestroyed'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='WinMapped'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" <signal name='WinUnmapped'>\n"
" <arg name='wid' type='" CDBUS_TYPE_WINDOW_STR "'/>\n"
" </signal>\n"
" </interface>\n"
" <node name='windows' />\n"
"</node>\n";
cdbus_reply_string(ps, msg, str_introspect);
return true;
}
///@}
/**
* Process an D-Bus Introspect request, for /windows.
*/
static bool cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *msg) {
static const 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>\n"
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' direction='out' type='s' />\n"
" </method>\n"
" </interface>\n";
char *ret = NULL;
mstrextend(&ret, str_introspect);
HASH_ITER2(ps->windows, w) {
assert(!w->destroyed);
if (!w->managed) {
continue;
}
char *tmp = NULL;
if (asprintf(&tmp, "<node name='%#010x'/>\n", w->id) < 0) {
log_fatal("Failed to allocate memory.");
abort();
}
mstrextend(&ret, tmp);
free(tmp);
}
mstrextend(&ret, "</node>");
bool success = cdbus_reply_string(ps, msg, ret);
free(ret);
return success;
}
static bool cdbus_process_window_introspect(session_t *ps, DBusMessage *msg) {
// clang-format off
static const 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>\n"
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' direction='out' type='s' />\n"
" </method>\n"
" </interface>\n"
" <interface name='org.freedesktop.DBus.Properties'>\n"
" <method name='Get'>\n"
" <arg type='s' name='interface_name' direction='in'/>\n"
" <arg type='s' name='property_name' direction='in'/>\n"
" <arg type='v' name='value' direction='out'/>\n"
" </method>\n"
" <method name='GetAll'>\n"
" <arg type='s' name='interface_name' direction='in'/>\n"
" <arg type='a{sv}' name='properties' direction='out'/>\n"
" </method>\n"
" <method name='Set'>\n"
" <arg type='s' name='interface_name' direction='in'/>\n"
" <arg type='s' name='property_name' direction='in'/>\n"
" <arg type='v' name='value' direction='in'/>\n"
" </method>\n"
" <signal name='PropertiesChanged'>\n"
" <arg type='s' name='interface_name'/>\n"
" <arg type='a{sv}' name='changed_properties'/>\n"
" <arg type='as' name='invalidated_properties'/>\n"
" </signal>\n"
" </interface>\n"
" <interface name='" PICOM_WINDOW_INTERFACE "'>\n"
" <property type='" CDBUS_TYPE_WINDOW_STR "' name='Leader' access='read'/>\n"
" <property type='" CDBUS_TYPE_WINDOW_STR "' name='ClientWin' access='read'/>\n"
" <property type='" CDBUS_TYPE_WINDOW_STR "' name='Id' access='read'/>\n"
" <property type='" CDBUS_TYPE_WINDOW_STR "' name='Next' access='read'/>\n"
" <property type='b' name='RawFocused' access='read'/>\n"
" <property type='b' name='Mapped' access='read'/>\n"
" <property type='s' name='Name' access='read'/>\n"
" <property type='s' name='Type' access='read'/>\n"
" </interface>\n"
"</node>\n";
// clang-format on
return cdbus_reply_string(ps, msg, str_introspect);
}
/**
* Process a message from D-Bus.
*/
static DBusHandlerResult
cdbus_process(DBusConnection *c attr_unused, 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")) {
log_info("picom is resetting...");
ev_break(ps->loop, EVBREAK_ALL);
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)) {
log_error(
"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 {
log_error("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;
}
/**
* Process a message from D-Bus, for /windows path.
*/
static DBusHandlerResult
cdbus_process_windows(DBusConnection *c attr_unused, DBusMessage *msg, void *ud) {
session_t *ps = ud;
bool handled = false;
const char *path = dbus_message_get_path(msg);
const char *last_segment = strrchr(path, '/');
if (last_segment == NULL) {
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);
return DBUS_HANDLER_RESULT_HANDLED;
}
bool is_root = strncmp(last_segment, "/windows", 8) == 0;
if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable",
"Introspect")) {
if (is_root) {
handled = cdbus_process_windows_root_introspect(ps, msg);
} else {
handled = cdbus_process_window_introspect(ps, msg);
}
goto finished;
}
if (!is_root) {
auto wid = (cdbus_window_t)strtol(last_segment + 1, NULL, 0);
if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Properties",
"GetAll")) {
handled = cdbus_reply(ps, msg, cdbus_append_empty_dict, NULL);
goto finished;
}
if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Properties", "Get")) {
handled = cdbus_process_window_property_get(ps, msg, wid);
goto finished;
}
}
if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") ||
dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) {
return DBUS_HANDLER_RESULT_HANDLED;
}
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) {
log_error("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 {
log_error("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)) {
handled = cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
}
finished:
if (!handled && dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
!dbus_message_get_no_reply(msg)) {
handled =
cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S);
}
return handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/** @name Core callbacks
*/
///@{
void cdbus_ev_win_added(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_added", w->id);
cdbus_signal_wid(ps, PICOM_COMPOSITOR_INTERFACE, "WinAdded", w->id);
}
}
void cdbus_ev_win_destroyed(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_destroyed", w->id);
cdbus_signal_wid(ps, PICOM_COMPOSITOR_INTERFACE, "WinDestroyed", w->id);
}
}
void cdbus_ev_win_mapped(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_mapped", w->id);
cdbus_signal_wid(ps, PICOM_COMPOSITOR_INTERFACE, "WinMapped", w->id);
}
}
void cdbus_ev_win_unmapped(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_unmapped", w->id);
cdbus_signal_wid(ps, PICOM_COMPOSITOR_INTERFACE, "WinUnmapped", w->id);
}
}
void cdbus_ev_win_focusout(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_focusout", w->id);
}
}
void cdbus_ev_win_focusin(session_t *ps, struct win *w) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
cdbus_signal_wid(ps, CDBUS_INTERFACE_NAME, "win_focusin", w->id);
}
}
//!@}