From 6ae1e7e7dd6b4427c4a7735c8b636dd2ebea7dcb Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 9 Aug 2024 03:15:46 +0100 Subject: [PATCH] inspect: merge inspect into picom I realized it's probably not a good idea to have a different code path handling window creation etc. just for picom-inspect. I found a not-so-hacky way of doing picom-inspect inside the compositor, also helps with code reuse. Signed-off-by: Yuxuan Shui --- src/c2.c | 9 +-- src/c2.h | 4 +- src/config.h | 1 + src/inspect.c | 188 +++++--------------------------------------------- src/inspect.h | 8 ++- src/picom.c | 14 ++-- src/wm/win.c | 8 +++ 7 files changed, 49 insertions(+), 183 deletions(-) diff --git a/src/c2.c b/src/c2.c index 0654e07c..4c161286 100644 --- a/src/c2.c +++ b/src/c2.c @@ -1801,7 +1801,7 @@ c2_match_once_leaf_string(struct atom *atoms, const struct win *w, const c2_l_t * For internal use. */ static inline bool -c2_match_once_leaf(struct c2_state *state, const struct win *w, const c2_l_t *leaf) { +c2_match_once_leaf(const struct c2_state *state, const struct win *w, const c2_l_t *leaf) { assert(leaf); const xcb_window_t wid = @@ -1845,7 +1845,8 @@ c2_match_once_leaf(struct c2_state *state, const struct win *w, const c2_l_t *le * * @return true if matched, false otherwise. */ -static bool c2_match_once(struct c2_state *state, const struct win *w, const c2_ptr_t cond) { +static bool +c2_match_once(const struct c2_state *state, const struct win *w, const c2_ptr_t cond) { bool result = false; if (cond.isbranch) { @@ -1931,8 +1932,8 @@ bool c2_match(struct c2_state *state, const struct win *w, const c2_lptr_t *cond } /// Match a window against the first condition in a condition linked list. -bool c2_match_one(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst, - void **pdata) { +bool c2_match_one(const struct c2_state *state, const struct win *w, + const c2_lptr_t *condlst, void **pdata) { if (!condlst) { return false; } diff --git a/src/c2.h b/src/c2.h index 5cdd33e3..aceebb8a 100644 --- a/src/c2.h +++ b/src/c2.h @@ -52,8 +52,8 @@ void c2_window_state_update(struct c2_state *state, struct c2_window_state *wind bool c2_match(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst, void **pdata); -bool c2_match_one(struct c2_state *state, const struct win *w, const c2_lptr_t *condlst, - void **pdata); +bool c2_match_one(const struct c2_state *state, const struct win *w, + const c2_lptr_t *condlst, void **pdata); bool c2_list_postprocess(struct c2_state *state, xcb_connection_t *c, c2_lptr_t *list); typedef bool (*c2_list_foreach_cb_t)(const c2_lptr_t *cond, void *data); diff --git a/src/config.h b/src/config.h index a7361b01..89568e76 100644 --- a/src/config.h +++ b/src/config.h @@ -241,6 +241,7 @@ typedef struct options { bool print_diagnostics; /// Render to a separate window instead of taking over the screen bool debug_mode; + xcb_window_t inspect_win; // === General === /// Use the legacy backends? bool use_legacy_backends; diff --git a/src/inspect.c b/src/inspect.c index d0df492e..1f71f9a9 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -11,92 +11,15 @@ #include "inspect.h" -#include "atom.h" -#include "backend/backend.h" #include "c2.h" #include "common.h" #include "config.h" #include "log.h" -#include "options.h" -#include "utils/misc.h" #include "wm/defs.h" #include "wm/win.h" #include "x.h" -static struct win * -setup_window(struct x_connection *c, struct atom *atoms, struct options *options, - struct wm *wm, struct c2_state *state, xcb_window_t target) { - // Pretend we are the compositor, and build up the window state - auto cursor = wm_find(wm, target); - if (cursor == NULL) { - log_fatal("Could not find window %#010x", target); - wm_free(wm); - return NULL; - } - - auto toplevel = wm_ref_toplevel_of(wm, cursor); - BUG_ON_NULL(toplevel); - struct win *w = ccalloc(1, struct win); - w->state = WSTATE_MAPPED; - w->tree_ref = toplevel; - log_debug("Toplevel is %#010x", wm_ref_win_id(toplevel)); - log_debug("Client is %#010x", win_client_id(w, true)); - win_update_wintype(c, atoms, w); - win_update_frame_extents(c, atoms, w, win_client_id(w, /*fallback_to_self=*/true), - options->frame_opacity); - // TODO(yshui) get leader - win_update_name(c, atoms, w); - win_update_class(c, atoms, w); - win_update_role(c, atoms, w); - - auto geometry_reply = XCB_AWAIT(xcb_get_geometry, c->c, win_id(w)); - w->g = (struct win_geometry){ - .x = geometry_reply->x, - .y = geometry_reply->y, - .width = geometry_reply->width, - .height = geometry_reply->height, - }; - free(geometry_reply); - - auto shape_info = xcb_get_extension_data(c->c, &xcb_shape_id); - win_on_win_size_change(w, options->shadow_offset_x, options->shadow_offset_y, - options->shadow_radius); - win_update_bounding_shape(c, w, shape_info->present, options->detect_rounded_corners); - win_update_prop_fullscreen(c, atoms, w); - - // Determine if the window is focused - xcb_window_t wid = XCB_NONE; - bool exists; - if (options->use_ewmh_active_win) { - wid_get_prop_window(c, c->screen_info->root, atoms->a_NET_ACTIVE_WINDOW, - &exists); - } else { - // Determine the currently focused window so we can apply appropriate - // opacity on it - xcb_get_input_focus_reply_t *reply = - xcb_get_input_focus_reply(c->c, xcb_get_input_focus(c->c), NULL); - - if (reply) { - wid = reply->focus; - free(reply); - } - } - if (wid == win_id(w) || wid == win_client_id(w, /*fallback_to_self=*/false)) { - w->is_focused = true; - } - - auto attributes_reply = XCB_AWAIT(xcb_get_window_attributes, c->c, win_id(w)); - w->a = *attributes_reply; - w->pictfmt = x_get_pictform_for_visual(c, w->a.visual); - free(attributes_reply); - - c2_window_state_init(state, &w->c2_state); - c2_window_state_update(state, &w->c2_state, c->c, - win_client_id(w, /*fallback_to_self=*/true), win_id(w)); - return w; -} - -xcb_window_t select_window(struct x_connection *c) { +xcb_window_t inspect_select_window(struct x_connection *c) { xcb_font_t font = x_new_id(c); xcb_cursor_t cursor = x_new_id(c); const char font_name[] = "cursor"; @@ -153,12 +76,12 @@ xcb_window_t select_window(struct x_connection *c) { } struct c2_match_state { - struct c2_state *state; - struct win *w; + const struct c2_state *state; + const struct win *w; bool print_value; }; -bool c2_match_once_and_log(const c2_lptr_t *cond, void *data) { +static bool c2_match_once_and_log(const c2_lptr_t *cond, void *data) { struct c2_match_state *state = data; void *rule_data = NULL; printf(" %s ... ", c2_lptr_to_str(cond)); @@ -174,98 +97,38 @@ bool c2_match_once_and_log(const c2_lptr_t *cond, void *data) { #define BOLD(str) "\033[1m" str "\033[0m" -int inspect_main(int argc, char **argv, const char *config_file) { - Display *dpy = XOpenDisplay(NULL); - if (!dpy) { - log_fatal("Can't open display"); - return 1; - } - struct x_connection c; - x_connection_init(&c, dpy); - - xcb_prefetch_extension_data(c.c, &xcb_shape_id); - - struct options options; - if (!parse_config(&options, config_file)) { - return 1; - } - - // Parse all of the rest command line options - options.backend = backend_find("dummy"); - if (!get_cfg(&options, argc, argv)) { - log_fatal("Failed to get configuration, usually mean you have specified " - "invalid options."); - return 1; - } - - auto atoms = init_atoms(c.c); - auto state = c2_state_new(atoms); - options_postprocess_c2_lists(state, &c, &options); - - struct wm *wm = wm_new(); - - wm_import_start(wm, &c, atoms, c.screen_info->root, NULL); - // Process events until the window tree is consistent - while (x_has_pending_requests(&c)) { - auto ev = x_poll_for_event(&c); - if (ev == NULL) { - continue; - } - switch (ev->response_type) { - case XCB_CREATE_NOTIFY:; - auto create = (xcb_create_notify_event_t *)ev; - auto parent = wm_find(wm, create->parent); - wm_import_start(wm, &c, atoms, - ((xcb_create_notify_event_t *)ev)->window, parent); - break; - case XCB_DESTROY_NOTIFY: - wm_destroy(wm, ((xcb_destroy_notify_event_t *)ev)->window); - break; - case XCB_REPARENT_NOTIFY:; - auto reparent = (xcb_reparent_notify_event_t *)ev; - wm_reparent(wm, reparent->window, reparent->parent); - break; - default: - // Ignore ConfigureNotify and CirculateNotify, because we don't - // use stacking order for window rules. - break; - } - free(ev); - } - - auto target = select_window(&c); - log_info("Target window: %#x", target); - auto w = setup_window(&c, atoms, &options, wm, state, target); +void inspect_dump_window(const struct c2_state *state, const struct options *opts, + const struct win *w) { struct c2_match_state match_state = { .state = state, .w = w, }; printf("Checking " BOLD("transparent-clipping-exclude") ":\n"); - c2_list_foreach(options.transparent_clipping_blacklist, c2_match_once_and_log, + c2_list_foreach(opts->transparent_clipping_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("shadow-exclude") ":\n"); - c2_list_foreach(options.shadow_blacklist, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->shadow_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("fade-exclude") ":\n"); - c2_list_foreach(options.fade_blacklist, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->fade_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("clip-shadow-above") ":\n"); - c2_list_foreach(options.shadow_clip_list, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->shadow_clip_list, c2_match_once_and_log, &match_state); printf("Checking " BOLD("focus-exclude") ":\n"); - c2_list_foreach(options.focus_blacklist, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->focus_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("invert-color-include") ":\n"); - c2_list_foreach(options.invert_color_list, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->invert_color_list, c2_match_once_and_log, &match_state); printf("Checking " BOLD("blur-background-exclude") ":\n"); - c2_list_foreach(options.blur_background_blacklist, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->blur_background_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("unredir-if-possible-exclude") ":\n"); - c2_list_foreach(options.unredir_if_possible_blacklist, c2_match_once_and_log, + c2_list_foreach(opts->unredir_if_possible_blacklist, c2_match_once_and_log, &match_state); printf("Checking " BOLD("rounded-corners-exclude") ":\n"); - c2_list_foreach(options.rounded_corners_blacklist, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->rounded_corners_blacklist, c2_match_once_and_log, &match_state); match_state.print_value = true; printf("Checking " BOLD("opacity-rule") ":\n"); - c2_list_foreach(options.opacity_rules, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->opacity_rules, c2_match_once_and_log, &match_state); printf("Checking " BOLD("corner-radius-rule") ":\n"); - c2_list_foreach(options.corner_radius_rules, c2_match_once_and_log, &match_state); + c2_list_foreach(opts->corner_radius_rules, c2_match_once_and_log, &match_state); printf("\nHere are some rule(s) that match this window:\n"); if (w->name != NULL) { @@ -292,21 +155,4 @@ int inspect_main(int argc, char **argv, const char *config_file) { printf(" bounding_shaped\n"); } printf(" border_width = %d\n", w->g.border_width); - - pixman_region32_fini(&w->bounding_shape); - free(w->name); - free(w->class_instance); - free(w->class_general); - free(w->role); - c2_window_state_destroy(state, &w->c2_state); - free(w); - - wm_free(wm); - - log_deinit_tls(); - c2_state_free(state); - destroy_atoms(atoms); - options_destroy(&options); - XCloseDisplay(c.dpy); - return 0; } diff --git a/src/inspect.h b/src/inspect.h index eb700737..32df68be 100644 --- a/src/inspect.h +++ b/src/inspect.h @@ -3,5 +3,11 @@ #pragma once #include - +struct x_connection; +struct c2_state; +struct options; +struct win; int inspect_main(int argc, char **argv, const char *config_file); +xcb_window_t inspect_select_window(struct x_connection *c); +void inspect_dump_window(const struct c2_state *state, const struct options *opts, + const struct win *w); diff --git a/src/picom.c b/src/picom.c index 2fb0e059..491a0d22 100644 --- a/src/picom.c +++ b/src/picom.c @@ -2193,6 +2193,15 @@ static session_t *session_init(int argc, char **argv, Display *dpy, return NULL; } + const char *basename = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; + + if (strcmp(basename, "picom-inspect") == 0) { + ps->o.backend = backend_find("dummy"); + ps->o.print_diagnostics = false; + ps->o.dbus = false; + ps->o.inspect_win = inspect_select_window(&ps->c); + } + ps->window_options_default = win_options_from_config(&ps->o); if (ps->o.window_shader_fg) { @@ -2734,11 +2743,6 @@ int PICOM_MAIN(int argc, char **argv) { return exit_code; } - char *exe_name = basename(argv[0]); - if (strcmp(exe_name, "picom-inspect") == 0) { - return inspect_main(argc, argv, config_file); - } - int pfds[2]; if (need_fork) { if (pipe2(pfds, O_CLOEXEC)) { diff --git a/src/wm/win.c b/src/wm/win.c index ff03ac73..084f3f88 100644 --- a/src/wm/win.c +++ b/src/wm/win.c @@ -24,6 +24,7 @@ #include "compiler.h" #include "config.h" #include "dbus.h" +#include "inspect.h" #include "log.h" #include "picom.h" #include "region.h" @@ -1115,6 +1116,13 @@ void win_on_factor_change(session_t *ps, struct win *w) { (win_client_id(w, /*fallback_to_self=*/false) == ps->debug_window))) { w->options.paint = TRI_FALSE; } + + if ((ps->o.inspect_win != XCB_NONE && win_id(w) == ps->o.inspect_win) || + ps->o.inspect_monitor) { + inspect_dump_window(ps->c2_state, &ps->o, w); + printf("\n"); + quit(ps); + } } /**