// 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 #include #include #include "common.h" #include "compiler.h" #include "win.h" #include "string_utils.h" #include "log.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 uint16_t cdbus_enum_t; #define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 #define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_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__)) static DBusHandlerResult cdbus_process(DBusConnection *conn, DBusMessage *m, void *); static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data); static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data); static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data); static void cdbus_callback_remove_watch(DBusWatch *watch, void *data); static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data); /** * Initialize D-Bus connection. */ 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(*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_add_filter(cd->dbus_conn, cdbus_process, ps, NULL); 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); } } // 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_ ev_timer *w, int revents) { 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_ 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, 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, 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, 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, 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, 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 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)) { 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, 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 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 auto arr = ccalloc(count, cdbus_window_t); // 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)) { 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 *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, CDBUS_INTERFACE_NAME, 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 *name, xcb_window_t wid) { return cdbus_signal(ps, 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 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_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; } win *w = find_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(MSTR(tgt), target)) { \ apdarg_func(ps, msg, w->tgt); \ return true; \ } cdbus_m_win_get_do(id, cdbus_reply_wid); // next if (!strcmp("next", target)) { cdbus_reply_wid(ps, msg, (w->next ? w->next->id: 0)); 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(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); // focused_real if (!strcmp("focused_real", target)) { cdbus_reply_bool(ps, msg, win_is_focused_real(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_uint32); cdbus_m_win_get_do(opacity_tgt, cdbus_reply_uint32); 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_uint32); cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); if (!strcmp("left_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.left); return true; } if (!strcmp("right_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.right); return true; } if (!strcmp("top_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.top); return true; } if (!strcmp("bottom_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.bottom); return true; } cdbus_m_win_get_do(shadow, cdbus_reply_bool); cdbus_m_win_get_do(fade, 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; } win *w = find_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(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 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; 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 { 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(MSTR(tgt), target)) { \ apdarg_func(ps, msg, ps->o.tgt); \ return true; \ } #define cdbus_m_opts_get_stub(tgt, apdarg_func, ret) \ if (!strcmp(MSTR(tgt), target)) { \ apdarg_func(ps, msg, ret); \ return true; \ } // version if (!strcmp("version", target)) { cdbus_reply_string(ps, msg, COMPTON_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_do(config_file, cdbus_reply_string); 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(fork_after_register, 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_int32); 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(synchronize, cdbus_reply_bool, false); cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32); cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool); if (!strcmp("vsync", target)) { assert(ps->o.vsync < sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); cdbus_reply_string(ps, msg, VSYNC_STRS[ps->o.vsync]); return true; } 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_stub(dbe, cdbus_reply_bool, false); cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); 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_stub(clear_shadow, cdbus_reply_bool, true); cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool); cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); cdbus_m_opts_get_do(fade_out_step, cdbus_reply_int32); cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background, 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(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); #ifdef CONFIG_OPENGL cdbus_m_opts_get_stub(glx_use_copysubbuffermesa, cdbus_reply_bool, false); cdbus_m_opts_get_stub(glx_copy_from_front, cdbus_reply_bool, false); cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool); cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool); cdbus_m_opts_get_do(glx_swap_method, cdbus_reply_int32); #endif cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); cdbus_m_opts_get_do(track_wdata, cdbus_reply_bool); cdbus_m_opts_get_do(track_leader, cdbus_reply_bool); #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(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; \ } // fade_delta if (!strcmp("fade_delta", target)) { int32_t val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) return false; ps->o.fade_delta = max_i(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) * OPAQUE; 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) * OPAQUE; 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)) { dbus_bool_t val = FALSE; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) return false; // You could enable this option, but never turn if off if (val) { opts_init_track_focus(ps); } goto cdbus_process_opts_set_success; } // vsync if (!strcmp("vsync", target)) { const char * val = NULL; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) return false; vsync_deinit(ps); auto tmp_vsync = parse_vsync(val); if (tmp_vsync >= NUM_VSYNC) { log_error("Failed to parse vsync: invalid value %s.", val); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); return true; } auto old_vsync = ps->o.vsync; ps->o.vsync = tmp_vsync; if (!vsync_init(ps)) { // Trying to revert back to original vsync values log_error("Failed to initialize specified VSync method."); ps->o.vsync = old_vsync; if (!vsync_init(ps)) { log_error("Failed to revert back to original VSync method."); ps->o.vsync = VSYNC_NONE; } cdbus_reply_err(ps, msg, CDBUS_ERROR_CUSTOM, CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); } else goto cdbus_process_opts_set_success; return true; } // 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 = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; cdbus_reply_string(ps, msg, str_introspect); return true; } ///@} /** * Process a message from D-Bus. */ static DBusHandlerResult cdbus_process(DBusConnection *c, DBusMessage *msg, void *ud) { session_t *ps = ud; bool handled = false; #define cdbus_m_ismethod(method) \ dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method) if (cdbus_m_ismethod("reset")) { ps->reset = true; if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); handled = true; } else if (cdbus_m_ismethod("repaint")) { force_repaint(ps); if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); handled = true; } else if (cdbus_m_ismethod("list_win")) { handled = cdbus_process_list_win(ps, msg); } else if (cdbus_m_ismethod("win_get")) { handled = cdbus_process_win_get(ps, msg); } else if (cdbus_m_ismethod("win_set")) { handled = cdbus_process_win_set(ps, msg); } else if (cdbus_m_ismethod("find_win")) { handled = cdbus_process_find_win(ps, msg); } else if (cdbus_m_ismethod("opts_get")) { handled = cdbus_process_opts_get(ps, msg); } else if (cdbus_m_ismethod("opts_set")) { handled = cdbus_process_opts_set(ps, msg); } #undef cdbus_m_ismethod else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { handled = cdbus_process_introspect(ps, msg); } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer", "Ping")) { cdbus_reply(ps, msg, NULL, NULL); handled = true; } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer", "GetMachineId")) { char *uuid = dbus_get_local_machine_id(); if (uuid) { cdbus_reply_string(ps, msg, uuid); dbus_free(uuid); handled = true; } } else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { handled = true; } else { if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) { 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; } /** @name Core callbacks */ ///@{ void cdbus_ev_win_added(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_added", w->id); } void cdbus_ev_win_destroyed(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_destroyed", w->id); } void cdbus_ev_win_mapped(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_mapped", w->id); } void cdbus_ev_win_unmapped(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_unmapped", w->id); } void cdbus_ev_win_focusout(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_focusout", w->id); } void cdbus_ev_win_focusin(session_t *ps, win *w) { struct cdbus_data *cd = ps->dbus_data; if (cd->dbus_conn) cdbus_signal_wid(ps, "win_focusin", w->id); } //!@}