1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-02-17 15:56:21 -05:00

Merge pull request #1309 from yshui/inspect-monitor

Improvement of `picom-inspect`
This commit is contained in:
Yuxuan Shui 2024-08-09 23:24:14 +01:00 committed by GitHub
commit 2b3745b396
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 675 additions and 665 deletions

View file

@ -5,7 +5,7 @@
* Universal window rules (#1284). One option to rule them all! Added new configuration option `rules` to replace all existing rule options, and to provide more flexibility on top of that. See [picom(1)](https://picom.app/#_window_rules) for more details.
* `@include` directives in config file now also search in `$XDG_CONFIG_HOME/picom/include` and `$XDG_CONFIG_DIRS/picom/include`, in addition to relative to the config file's parent directory.
* Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170)
* New `picom-inspect` tool, which lets you test out your picom rules. Sample output:
* New `picom-inspect` tool, which lets you test out your picom rules. `man picom-inspect(1)` for more details. Sample output:
```
...

View file

@ -19,12 +19,16 @@ DESCRIPTION
OPTIONS
-------
*picom-inspect* accept the exact same set of options as *picom*. Naturally, most of those options will not be relevant.
*picom-inspect* accepts all options that *picom* does. Naturally, most of those options will not be relevant.
These are some of the options you might find useful (See *picom*(1) for descriptions of what they do):
*--config*, *--log-level*, *--log-file*, all the options related to rules.
*picom-inspect* also accepts some extra options: :::
*--monitor*:: Keep *picom-inspect* running in a loop, and dump information every time something changed about a window.
NOTES
-----
*picom-inspect* is prototype right now. If you find any bug, for example, if rules are matched differently compared to *picom*, please submit bug reports to:

638
src/c2.c

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,9 @@
#include <stddef.h>
#include <xcb/xproto.h>
typedef struct _c2_lptr c2_lptr_t;
#include "utils/list.h"
typedef struct c2_condition c2_condition;
typedef struct session session_t;
struct c2_state;
/// Per-window state used for c2 condition matching.
@ -19,19 +21,20 @@ struct c2_window_state {
};
struct atom;
struct win;
struct list_node;
typedef void (*c2_userdata_free)(void *);
c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
struct c2_condition *c2_parse(struct list_node *list, const char *pattern, void *data);
/// Parse a condition that has a prefix. The prefix is parsed by `parse_prefix`. If
/// `free_value` is not NULL, it will be called to free the value returned by
/// `parse_prefix` when error occurs.
c2_lptr_t *
c2_parse_with_prefix(c2_lptr_t **pcondlst, const char *pattern,
c2_condition *
c2_parse_with_prefix(struct list_node *list, const char *pattern,
void *(*parse_prefix)(const char *input, const char **end, void *),
void (*free_value)(void *), void *user_data);
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp, c2_userdata_free f);
void c2_free_condition(c2_condition *lp, c2_userdata_free f);
/// Create a new c2_state object. This is used for maintaining the internal state
/// used for c2 condition matching. This state object holds a reference to the
@ -50,30 +53,46 @@ void c2_window_state_update(struct c2_state *state, struct c2_window_state *wind
xcb_connection_t *c, xcb_window_t client_win,
xcb_window_t frame_win);
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(struct c2_state *state, const struct win *w,
const struct list_node *conditions, void **pdata);
bool c2_match_one(const struct c2_state *state, const struct win *w,
const c2_condition *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);
bool c2_list_foreach(const c2_lptr_t *list, c2_list_foreach_cb_t cb, void *data);
bool c2_list_postprocess(struct c2_state *state, xcb_connection_t *c, struct list_node *list);
/// Return user data stored in a condition.
void *c2_list_get_data(const c2_lptr_t *condlist);
void *c2_condition_get_data(const c2_condition *condition);
/// Set user data stored in a condition. Return the old user data.
void *c2_list_set_data(c2_lptr_t *condlist, void *data);
/// Convert a c2_lptr_t to string. The returned string is only valid until the
void *c2_condition_set_data(c2_condition *condlist, void *data);
/// Convert a c2_condition to string. The returned string is only valid until the
/// next call to this function, and should not be freed.
const char *c2_lptr_to_str(const c2_lptr_t *);
void c2_condlist_insert(c2_lptr_t **pcondlst, c2_lptr_t *pnew);
const char *c2_condition_to_str(const c2_condition *);
c2_condition *c2_condition_list_next(struct list_node *list, c2_condition *condition);
c2_condition *c2_condition_list_prev(struct list_node *list, c2_condition *condition);
c2_condition *c2_condition_list_entry(struct list_node *list);
/// Create a new condition list with a single condition that is always true.
c2_lptr_t *c2_new_true(void);
c2_condition *c2_new_true(struct list_node *list);
#define c2_condition_list_foreach(list, i) \
for (c2_condition *i = \
list_is_empty((list)) ? NULL : c2_condition_list_entry((list)->next); \
i; i = c2_condition_list_next(list, i))
#define c2_condition_list_foreach_rev(list, i) \
for (c2_condition *i = \
list_is_empty((list)) ? NULL : c2_condition_list_entry((list)->prev); \
i; i = c2_condition_list_prev(list, i))
#define c2_condition_list_foreach_safe(list, i, n) \
for (c2_condition *i = \
list_is_empty((list)) ? NULL : c2_condition_list_entry((list)->next), \
*n = c2_condition_list_next(list, i); \
i; i = n, n = c2_condition_list_next(list, i))
/**
* Destroy a condition list.
*/
static inline void c2_list_free(c2_lptr_t **pcondlst, c2_userdata_free f) {
while ((*pcondlst = c2_free_lptr(*pcondlst, f))) {
static inline void c2_list_free(struct list_node *list, c2_userdata_free f) {
c2_condition_list_foreach_safe(list, i, ni) {
c2_free_condition(i, f);
}
*pcondlst = NULL;
list_init_head(list);
}

View file

@ -724,6 +724,21 @@ bool parse_config(options_t *opt, const char *config_file) {
// clang-format on
list_init_head(&opt->included_config_files);
list_init_head(&opt->unredir_if_possible_blacklist);
list_init_head(&opt->paint_blacklist);
list_init_head(&opt->shadow_blacklist);
list_init_head(&opt->shadow_clip_list);
list_init_head(&opt->fade_blacklist);
list_init_head(&opt->blur_background_blacklist);
list_init_head(&opt->invert_color_list);
list_init_head(&opt->window_shader_fg_rules);
list_init_head(&opt->opacity_rules);
list_init_head(&opt->rounded_corners_blacklist);
list_init_head(&opt->corner_radius_rules);
list_init_head(&opt->focus_blacklist);
list_init_head(&opt->transparent_clipping_blacklist);
list_init_head(&opt->rules);
opt->all_scripts = dynarr_new(struct script *, 4);
return parse_config_libconfig(opt, config_file);
}

View file

@ -59,8 +59,6 @@ typedef struct win_option {
bool clip_shadow_above;
} win_option_t;
typedef struct _c2_lptr c2_lptr_t;
enum vblank_scheduler_type {
/// X Present extension based vblank events
VBLANK_SCHEDULER_PRESENT,
@ -241,6 +239,9 @@ typedef struct options {
bool print_diagnostics;
/// Render to a separate window instead of taking over the screen
bool debug_mode;
/// For picom-inspect only, dump windows in a loop
bool inspect_monitor;
xcb_window_t inspect_win;
// === General ===
/// Use the legacy backends?
bool use_legacy_backends;
@ -273,7 +274,7 @@ typedef struct options {
bool unredir_if_possible;
/// List of conditions of windows to ignore as a full-screen window
/// when determining if a window could be unredirected.
c2_lptr_t *unredir_if_possible_blacklist;
struct list_node unredir_if_possible_blacklist;
/// Delay before unredirecting screen, in milliseconds.
int unredir_if_possible_delay;
/// Forced redirection setting through D-Bus.
@ -289,7 +290,7 @@ typedef struct options {
/// Window to constantly repaint in benchmark mode. 0 for full-screen.
xcb_window_t benchmark_wid;
/// A list of conditions of windows not to paint.
c2_lptr_t *paint_blacklist;
struct list_node paint_blacklist;
/// Whether to show all X errors.
bool show_all_xerrors;
/// Whether to avoid acquiring X Selection.
@ -318,13 +319,13 @@ typedef struct options {
int shadow_offset_x, shadow_offset_y;
double shadow_opacity;
/// Shadow blacklist. A linked list of conditions.
c2_lptr_t *shadow_blacklist;
struct list_node shadow_blacklist;
/// Whether bounding-shaped window should be ignored.
bool shadow_ignore_shaped;
/// Whether to crop shadow to the very X RandR monitor.
bool crop_shadow_to_monitor;
/// Don't draw shadow over these windows. A linked list of conditions.
c2_lptr_t *shadow_clip_list;
struct list_node shadow_clip_list;
bool shadow_enable;
// === Fading ===
@ -339,7 +340,7 @@ typedef struct options {
/// Whether to disable fading on ARGB managed destroyed windows.
bool no_fading_destroyed_argb;
/// Fading blacklist. A linked list of conditions.
c2_lptr_t *fade_blacklist;
struct list_node fade_blacklist;
bool fading_enable;
// === Opacity ===
@ -374,7 +375,7 @@ typedef struct options {
/// to window opacity.
bool blur_background_fixed;
/// Background blur blacklist. A linked list of conditions.
c2_lptr_t *blur_background_blacklist;
struct list_node blur_background_blacklist;
/// Blur convolution kernel.
struct conv **blur_kerns;
/// Number of convolution kernels
@ -382,24 +383,24 @@ typedef struct options {
/// Custom fragment shader for painting windows
char *window_shader_fg;
/// Rules to change custom fragment shader for painting windows.
c2_lptr_t *window_shader_fg_rules;
struct list_node window_shader_fg_rules;
/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable.
double inactive_dim;
/// Whether to use fixed inactive dim opacity, instead of deciding
/// based on window opacity.
bool inactive_dim_fixed;
/// Conditions of windows to have inverted colors.
c2_lptr_t *invert_color_list;
struct list_node invert_color_list;
/// Rules to change window opacity.
c2_lptr_t *opacity_rules;
struct list_node opacity_rules;
/// Limit window brightness
double max_brightness;
// Radius of rounded window corners
int corner_radius;
/// Rounded corners blacklist. A linked list of conditions.
c2_lptr_t *rounded_corners_blacklist;
struct list_node rounded_corners_blacklist;
/// Rounded corner rules. A linked list of conditions.
c2_lptr_t *corner_radius_rules;
struct list_node corner_radius_rules;
// === Focus related ===
/// Whether to try to detect WM windows and mark them as focused.
@ -409,7 +410,7 @@ typedef struct options {
/// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window.
bool use_ewmh_active_win;
/// A list of windows always to be considered focused.
c2_lptr_t *focus_blacklist;
struct list_node focus_blacklist;
/// Whether to do window grouping with <code>WM_TRANSIENT_FOR</code>.
bool detect_transient;
/// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>.
@ -427,7 +428,7 @@ typedef struct options {
bool transparent_clipping;
/// A list of conditions of windows to which transparent clipping
/// should not apply
c2_lptr_t *transparent_clipping_blacklist;
struct list_node transparent_clipping_blacklist;
bool dithered_present;
// === Animation ===
@ -435,7 +436,7 @@ typedef struct options {
/// Array of all the scripts used in `animations`. This is a dynarr.
struct script **all_scripts;
c2_lptr_t *rules;
struct list_node rules;
bool has_both_style_of_rules;
} options_t;

View file

@ -124,7 +124,7 @@ FILE *open_config_file(const char *cpath, char **ppath) {
/**
* Parse a condition list in configuration file.
*/
bool must_use parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) {
bool must_use parse_cfg_condlst(struct list_node *list, const config_t *pcfg, const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting == NULL) {
return true;
@ -133,15 +133,14 @@ bool must_use parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, cons
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--) {
if (!c2_parse(pcondlst,
config_setting_get_string_elem(setting, i), NULL)) {
if (!c2_parse(list, config_setting_get_string_elem(setting, i), NULL)) {
return false;
}
}
}
// Treat it as a single pattern if it's a string
else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
if (!c2_parse(pcondlst, config_setting_get_string(setting), NULL)) {
if (!c2_parse(list, config_setting_get_string(setting), NULL)) {
return false;
}
}
@ -152,7 +151,7 @@ bool must_use parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, cons
* Parse a window corner radius rule list in configuration file.
*/
static inline bool
parse_cfg_condlst_with_prefix(c2_lptr_t **condlst, const config_t *pcfg, const char *name,
parse_cfg_condlst_with_prefix(struct list_node *list, const config_t *pcfg, const char *name,
void *(*parse_prefix)(const char *, const char **, void *),
void (*free_value)(void *), void *user_data) {
config_setting_t *setting = config_lookup(pcfg, name);
@ -164,7 +163,7 @@ parse_cfg_condlst_with_prefix(c2_lptr_t **condlst, const config_t *pcfg, const c
int i = config_setting_length(setting);
while (i--) {
if (!c2_parse_with_prefix(
condlst, config_setting_get_string_elem(setting, i),
list, config_setting_get_string_elem(setting, i),
parse_prefix, free_value, user_data)) {
return false;
}
@ -172,7 +171,7 @@ parse_cfg_condlst_with_prefix(c2_lptr_t **condlst, const config_t *pcfg, const c
}
// Treat it as a single pattern if it's a string
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
if (!c2_parse_with_prefix(condlst, config_setting_get_string(setting),
if (!c2_parse_with_prefix(list, config_setting_get_string(setting),
parse_prefix, free_value, user_data)) {
return false;
}
@ -576,7 +575,8 @@ static const struct {
{"transparent-clipping", offsetof(struct window_maybe_options, transparent_clipping)},
};
static c2_lptr_t *parse_rule(config_setting_t *setting, struct script ***out_scripts) {
static c2_condition *
parse_rule(struct list_node *rules, config_setting_t *setting, struct script ***out_scripts) {
if (!config_setting_is_group(setting)) {
log_error("Invalid rule at line %d. It must be a group.",
config_setting_source_line(setting));
@ -585,9 +585,9 @@ static c2_lptr_t *parse_rule(config_setting_t *setting, struct script ***out_scr
int ival;
double fval;
const char *sval;
c2_lptr_t *rule = NULL;
c2_condition *rule = NULL;
if (config_setting_lookup_string(setting, "match", &sval)) {
rule = c2_parse(NULL, sval, NULL);
rule = c2_parse(rules, sval, NULL);
if (!rule) {
log_error("Failed to parse rule at line %d.",
config_setting_source_line(setting));
@ -595,12 +595,12 @@ static c2_lptr_t *parse_rule(config_setting_t *setting, struct script ***out_scr
}
} else {
// If no match condition is specified, it matches all windows
rule = c2_new_true();
rule = c2_new_true(rules);
}
auto wopts = cmalloc(struct window_maybe_options);
*wopts = WIN_MAYBE_OPTIONS_DEFAULT;
c2_list_set_data(rule, wopts);
c2_condition_set_data(rule, wopts);
for (size_t i = 0; i < ARR_SIZE(all_window_options); i++) {
if (config_setting_lookup_bool(setting, all_window_options[i].name, &ival)) {
@ -630,8 +630,8 @@ static c2_lptr_t *parse_rule(config_setting_t *setting, struct script ***out_scr
return rule;
}
static void
parse_rules(config_setting_t *setting, struct script ***out_scripts, c2_lptr_t **rules) {
static void parse_rules(struct list_node *rules, config_setting_t *setting,
struct script ***out_scripts) {
if (!config_setting_is_list(setting)) {
log_error("Invalid value for \"rules\" at line %d. It must be a list.",
config_setting_source_line(setting));
@ -640,10 +640,7 @@ parse_rules(config_setting_t *setting, struct script ***out_scripts, c2_lptr_t *
const auto length = (unsigned int)config_setting_length(setting);
for (unsigned int i = 0; i < length; i++) {
auto sub = config_setting_get_elem(setting, i);
auto rule = parse_rule(sub, out_scripts);
if (rule != NULL) {
c2_condlist_insert(rules, rule);
}
parse_rule(rules, sub, out_scripts);
}
}
@ -766,7 +763,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
config_setting_t *rules = config_lookup(&cfg, "rules");
if (rules) {
parse_rules(rules, &opt->all_scripts, &opt->rules);
parse_rules(&opt->rules, rules, &opt->all_scripts);
}
// --dbus
@ -795,7 +792,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
// -i (inactive_opacity)
if (config_lookup_float(&cfg, "inactive-opacity", &dval)) {
opt->inactive_opacity = normalize_d(dval);
if (opt->rules) {
if (!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("inactive-opacity");
opt->has_both_style_of_rules = true;
}
@ -803,7 +800,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
// --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval)) {
opt->active_opacity = normalize_d(dval);
if (opt->rules) {
if (!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("active-opacity");
opt->has_both_style_of_rules = true;
}
@ -859,30 +856,31 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
}
// --inactive-opacity-override
if (lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override) &&
opt->rules != NULL) {
!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("inactive-opacity-override");
opt->has_both_style_of_rules = true;
}
// --inactive-dim
if (config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim) && opt->rules != NULL) {
if (config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim) &&
!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("inactive-dim");
opt->has_both_style_of_rules = true;
}
// --mark-wmwin-focused
if (lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused) &&
opt->rules != NULL) {
!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("mark-wmwin-focused");
opt->has_both_style_of_rules = true;
}
// --mark-ovredir-focused
if (lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opt->mark_ovredir_focused) &&
opt->rules != NULL) {
!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("mark-ovredir-focused");
opt->has_both_style_of_rules = true;
}
// --shadow-ignore-shaped
if (lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &opt->shadow_ignore_shaped) &&
opt->rules != NULL) {
!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules("shadow-ignore-shaped");
opt->has_both_style_of_rules = true;
}
@ -965,7 +963,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
// --dithered_present
lcfg_lookup_bool(&cfg, "dithered-present", &opt->dithered_present);
if (opt->rules != NULL) {
if (!list_is_empty(&opt->rules)) {
static const char *rule_list[] = {
"transparent-clipping-exclude",
"shadow-exclude",
@ -987,18 +985,18 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
opt->has_both_style_of_rules = true;
}
}
} else if (!parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist,
} else if (!parse_cfg_condlst(&opt->transparent_clipping_blacklist, &cfg,
"transparent-clipping-exclude") ||
!parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude") ||
!parse_cfg_condlst(&cfg, &opt->shadow_clip_list, "clip-shadow-above") ||
!parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude") ||
!parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude") ||
!parse_cfg_condlst(&cfg, &opt->invert_color_list, "invert-color-include") ||
!parse_cfg_condlst(&cfg, &opt->blur_background_blacklist,
!parse_cfg_condlst(&opt->shadow_blacklist, &cfg, "shadow-exclude") ||
!parse_cfg_condlst(&opt->shadow_clip_list, &cfg, "clip-shadow-above") ||
!parse_cfg_condlst(&opt->fade_blacklist, &cfg, "fade-exclude") ||
!parse_cfg_condlst(&opt->focus_blacklist, &cfg, "focus-exclude") ||
!parse_cfg_condlst(&opt->invert_color_list, &cfg, "invert-color-include") ||
!parse_cfg_condlst(&opt->blur_background_blacklist, &cfg,
"blur-background-exclude") ||
!parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist,
!parse_cfg_condlst(&opt->unredir_if_possible_blacklist, &cfg,
"unredir-if-possible-exclude") ||
!parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist,
!parse_cfg_condlst(&opt->rounded_corners_blacklist, &cfg,
"rounded-corners-exclude") ||
!parse_cfg_condlst_with_prefix(
&opt->corner_radius_rules, &cfg, "corner-radius-rules",
@ -1139,7 +1137,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
// Wintype settings
// XXX ! Refactor all the wintype_* arrays into a struct
if (opt->rules == NULL) {
if (list_is_empty(&opt->rules)) {
for (wintype_t i = 0; i < NUM_WINTYPES; ++i) {
parse_wintype_config(&cfg, WINTYPES[i].name, &opt->wintype_option[i],
&opt->wintype_option_mask[i]);

View file

@ -11,92 +11,18 @@
#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 "utils/console.h"
#include "utils/dynarr.h"
#include "utils/str.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,119 +79,54 @@ 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) {
struct c2_match_state *state = data;
static bool c2_match_and_log(const struct list_node *list, const struct c2_state *state,
const struct win *w, bool print_value) {
void *rule_data = NULL;
printf(" %s ... ", c2_lptr_to_str(cond));
bool matched = c2_match_one(state->state, state->w, cond, rule_data);
printf("%s", matched ? "\033[1;32mmatched\033[0m" : "not matched");
if (state->print_value && matched) {
printf("/%lu", (unsigned long)(intptr_t)rule_data);
state->print_value = false;
c2_condition_list_foreach((struct list_node *)list, i) {
printf(" %s ... ", c2_condition_to_str(i));
bool matched = c2_match_one(state, w, i, rule_data);
printf("%s", matched ? "\033[1;32mmatched\033[0m" : "not matched");
if (print_value && matched) {
printf("/%lu", (unsigned long)(intptr_t)rule_data);
print_value = false;
}
printf("\n");
}
printf("\n");
return false;
}
#define BOLD(str) "\033[1m" str "\033[0m"
void inspect_dump_window(const struct c2_state *state, const struct options *opts,
const struct win *w) {
if (list_is_empty(&opts->rules)) {
printf("Checking " BOLD("transparent-clipping-exclude") ":\n");
c2_match_and_log(&opts->transparent_clipping_blacklist, state, w, false);
printf("Checking " BOLD("shadow-exclude") ":\n");
c2_match_and_log(&opts->shadow_blacklist, state, w, false);
printf("Checking " BOLD("fade-exclude") ":\n");
c2_match_and_log(&opts->fade_blacklist, state, w, false);
printf("Checking " BOLD("clip-shadow-above") ":\n");
c2_match_and_log(&opts->shadow_clip_list, state, w, true);
printf("Checking " BOLD("focus-exclude") ":\n");
c2_match_and_log(&opts->focus_blacklist, state, w, false);
printf("Checking " BOLD("invert-color-include") ":\n");
c2_match_and_log(&opts->invert_color_list, state, w, false);
printf("Checking " BOLD("blur-background-exclude") ":\n");
c2_match_and_log(&opts->blur_background_blacklist, state, w, false);
printf("Checking " BOLD("unredir-if-possible-exclude") ":\n");
c2_match_and_log(&opts->unredir_if_possible_blacklist, state, w, false);
printf("Checking " BOLD("rounded-corners-exclude") ":\n");
c2_match_and_log(&opts->rounded_corners_blacklist, state, w, false);
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;
printf("Checking " BOLD("opacity-rule") ":\n");
c2_match_and_log(&opts->opacity_rules, state, w, true);
printf("Checking " BOLD("corner-radius-rule") ":\n");
c2_match_and_log(&opts->corner_radius_rules, state, w, true);
}
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);
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,
&match_state);
printf("Checking " BOLD("shadow-exclude") ":\n");
c2_list_foreach(options.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);
printf("Checking " BOLD("clip-shadow-above") ":\n");
c2_list_foreach(options.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);
printf("Checking " BOLD("invert-color-include") ":\n");
c2_list_foreach(options.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);
printf("Checking " BOLD("unredir-if-possible-exclude") ":\n");
c2_list_foreach(options.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);
match_state.print_value = true;
printf("Checking " BOLD("opacity-rule") ":\n");
c2_list_foreach(options.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);
printf("\nHere are some rule(s) that match this window:\n");
if (w->name != NULL) {
@ -292,21 +153,88 @@ 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;
}
void inspect_dump_window_maybe_options(struct window_maybe_options wopts) {
bool nothing = true;
printf(" Applying:\n");
if (wopts.shadow != TRI_UNKNOWN) {
printf(" shadow = %s\n", wopts.shadow == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.fade != TRI_UNKNOWN) {
printf(" fade = %s\n", wopts.fade == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.blur_background != TRI_UNKNOWN) {
printf(" blur_background = %s\n",
wopts.blur_background == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.invert_color != TRI_UNKNOWN) {
printf(" invert_color = %s\n",
wopts.invert_color == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.clip_shadow_above != TRI_UNKNOWN) {
printf(" clip_shadow_above = %s\n",
wopts.clip_shadow_above == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.transparent_clipping != TRI_UNKNOWN) {
printf(" transparent_clipping = %s\n",
wopts.transparent_clipping == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.full_shadow != TRI_UNKNOWN) {
printf(" full_shadow = %s\n",
wopts.full_shadow == TRI_TRUE ? "true" : "false");
nothing = false;
}
if (wopts.unredir != WINDOW_UNREDIR_INVALID) {
const char *str = NULL;
switch (wopts.unredir) {
case WINDOW_UNREDIR_WHEN_POSSIBLE_ELSE_TERMINATE: str = "true"; break;
case WINDOW_UNREDIR_TERMINATE: str = "false"; break;
case WINDOW_UNREDIR_FORCED: str = "\"forced\""; break;
case WINDOW_UNREDIR_PASSIVE: str = "\"passive\""; break;
case WINDOW_UNREDIR_WHEN_POSSIBLE: str = "\"preferred\""; break;
default: unreachable();
}
printf(" unredir = %s\n", str);
nothing = false;
}
if (!safe_isnan(wopts.opacity)) {
printf(" opacity = %f\n", wopts.opacity);
nothing = false;
}
if (!safe_isnan(wopts.dim)) {
printf(" dim = %f\n", wopts.dim);
nothing = false;
}
if (wopts.corner_radius >= 0) {
printf(" corner_radius = %d\n", wopts.corner_radius);
nothing = false;
}
char **animation_triggers = dynarr_new(char *, 0);
for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
if (wopts.animations[i].script != NULL) {
char *name = NULL;
casprintf(&name, "\"%s\"", animation_trigger_names[i]);
dynarr_push(animation_triggers, name);
}
}
if (dynarr_len(animation_triggers) > 0) {
char *animation_triggers_str = dynarr_join(animation_triggers, ", ");
printf(" animations = { triggers = [%s]; }\n", animation_triggers_str);
free(animation_triggers_str);
nothing = false;
} else {
dynarr_free_pod(animation_triggers);
}
if (nothing) {
printf(" (nothing)\n");
}
}

View file

@ -3,5 +3,14 @@
#pragma once
#include <xcb/xcb.h>
#include "wm/win.h"
struct x_connection;
struct c2_state;
struct options;
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);
void inspect_dump_window_maybe_options(struct window_maybe_options wopts);

View file

@ -17,6 +17,7 @@
#include "compiler.h"
#include "log.h"
#include "utils/console.h"
#include "utils/misc.h"
thread_local struct log *tls_logger;
@ -274,7 +275,6 @@ static void file_logger_destroy(struct log_target *tgt) {
free(tgt);
}
#define ANSI(x) "\033[" x "m"
static const char *terminal_colorize_begin(enum log_level level) {
switch (level) {
case LOG_LEVEL_TRACE: return ANSI("30;2");

View file

@ -59,6 +59,7 @@ struct picom_option {
int has_arg;
struct picom_arg arg;
const char *help;
const char *argv0;
};
static bool set_flag(const struct picom_option * /*opt*/, const struct picom_arg *arg,
@ -70,7 +71,7 @@ static bool set_flag(const struct picom_option * /*opt*/, const struct picom_arg
static bool set_rule_flag(const struct picom_option *arg_opt, const struct picom_arg *arg,
const char * /*arg_str*/, void *output) {
auto opt = (struct options *)output;
if (opt->rules != NULL) {
if (!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules(arg_opt->long_name);
opt->has_both_style_of_rules = true;
return true;
@ -114,7 +115,7 @@ static bool store_float(const struct picom_option *opt, const struct picom_arg *
static bool store_rule_float(const struct picom_option *arg_opt, const struct picom_arg *arg,
const char *arg_str, void *output) {
auto opt = (struct options *)output;
if (opt->rules != NULL) {
if (!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules(arg_opt->long_name);
opt->has_both_style_of_rules = true;
return true;
@ -147,12 +148,12 @@ static bool store_rules(const struct picom_option *arg_opt, const struct picom_a
const char *arg_str, void *output) {
const struct picom_rules_parser *parser = arg->user_data;
struct options *opt = (struct options *)output;
if (opt->rules != NULL) {
if (!list_is_empty(&opt->rules)) {
log_warn_both_style_of_rules(arg_opt->long_name);
opt->has_both_style_of_rules = true;
return true;
}
auto rules = (c2_lptr_t **)(output + arg->offset);
auto rules = (struct list_node *)(output + arg->offset);
if (!parser->parse_prefix) {
return c2_parse(rules, arg_str, NULL) != NULL;
}
@ -452,6 +453,8 @@ static const struct picom_option picom_options[] = {
"window is fullscreen based only on its size and coordinates."},
[804] = {"realtime" , ENABLE(use_realtime_scheduling) , "Enable realtime scheduling. This might reduce latency, but might also cause "
"other issues. Disable this if you see the compositor being killed."},
[805] = {"monitor" , ENABLE(inspect_monitor) , "For picom-inspect, run in a loop and dump information every time something "
"changed about a window.", "picom-inspect"},
// Flags that takes an argument
['r'] = {"shadow-radius" , INTEGER(shadow_radius, 0, INT_MAX) , "The blur radius for shadows. (default 12)"},
@ -634,12 +637,19 @@ static void usage(const char *argv0, int ret) {
line_wrap = window_size.ws_col;
}
const char *basename = strrchr(argv0, '/') ? strrchr(argv0, '/') + 1 : argv0;
size_t help_indent = 0;
for (size_t i = 0; i < ARR_SIZE(picom_options); i++) {
if (picom_options[i].help == NULL) {
// Hide options with no help message.
continue;
}
if (picom_options[i].argv0 != NULL &&
strcmp(picom_options[i].argv0, basename) != 0) {
// Hide options that are not for this program.
continue;
}
auto option_len = strlen(picom_options[i].long_name) + 2 + 4;
if (picom_options[i].arg.name) {
option_len += strlen(picom_options[i].arg.name) + 1;
@ -654,6 +664,11 @@ static void usage(const char *argv0, int ret) {
if (picom_options[i].help == NULL) {
continue;
}
if (picom_options[i].argv0 != NULL &&
strcmp(picom_options[i].argv0, basename) != 0) {
// Hide options that are not for this program.
continue;
}
size_t option_len = 8;
fprintf(f, " ");
if ((i > 'a' && i < 'z') || (i > 'A' && i < 'Z')) {
@ -841,7 +856,7 @@ static bool sanitize_options(struct options *opt) {
dynarr_clear(opt->all_scripts, script_ptr_deinit);
}
if (opt->window_shader_fg || opt->window_shader_fg_rules) {
if (opt->window_shader_fg || !list_is_empty(&opt->window_shader_fg_rules)) {
log_warn("The new shader interface is not supported by the "
"legacy glx backend. You may want to use "
"--glx-fshader-win instead.");
@ -943,10 +958,15 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) {
int o = 0, longopt_idx = -1;
bool failed = false;
optind = 1;
const char *basename = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
if (o == '?' || o == ':' || picom_options[o].arg.handler == NULL) {
usage(argv[0], 1);
failed = true;
} else if (picom_options[o].argv0 != NULL &&
strcmp(picom_options[o].argv0, basename) != 0) {
log_error("Invalid option %s", argv[optind - 1]);
failed = true;
} else if (!picom_options[o].arg.handler(
&picom_options[o], &picom_options[o].arg, optarg, opt)) {
failed = true;
@ -1008,27 +1028,27 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) {
void options_postprocess_c2_lists(struct c2_state *state, struct x_connection *c,
struct options *option) {
if (option->rules) {
if (!c2_list_postprocess(state, c->c, option->rules)) {
if (!list_is_empty(&option->rules)) {
if (!c2_list_postprocess(state, c->c, &option->rules)) {
log_error("Post-processing of rules failed, some of your rules "
"might not work");
}
return;
}
if (!(c2_list_postprocess(state, c->c, option->unredir_if_possible_blacklist) &&
c2_list_postprocess(state, c->c, option->paint_blacklist) &&
c2_list_postprocess(state, c->c, option->shadow_blacklist) &&
c2_list_postprocess(state, c->c, option->shadow_clip_list) &&
c2_list_postprocess(state, c->c, option->fade_blacklist) &&
c2_list_postprocess(state, c->c, option->blur_background_blacklist) &&
c2_list_postprocess(state, c->c, option->invert_color_list) &&
c2_list_postprocess(state, c->c, option->window_shader_fg_rules) &&
c2_list_postprocess(state, c->c, option->opacity_rules) &&
c2_list_postprocess(state, c->c, option->rounded_corners_blacklist) &&
c2_list_postprocess(state, c->c, option->corner_radius_rules) &&
c2_list_postprocess(state, c->c, option->focus_blacklist) &&
c2_list_postprocess(state, c->c, option->transparent_clipping_blacklist))) {
if (!(c2_list_postprocess(state, c->c, &option->unredir_if_possible_blacklist) &&
c2_list_postprocess(state, c->c, &option->paint_blacklist) &&
c2_list_postprocess(state, c->c, &option->shadow_blacklist) &&
c2_list_postprocess(state, c->c, &option->shadow_clip_list) &&
c2_list_postprocess(state, c->c, &option->fade_blacklist) &&
c2_list_postprocess(state, c->c, &option->blur_background_blacklist) &&
c2_list_postprocess(state, c->c, &option->invert_color_list) &&
c2_list_postprocess(state, c->c, &option->window_shader_fg_rules) &&
c2_list_postprocess(state, c->c, &option->opacity_rules) &&
c2_list_postprocess(state, c->c, &option->rounded_corners_blacklist) &&
c2_list_postprocess(state, c->c, &option->corner_radius_rules) &&
c2_list_postprocess(state, c->c, &option->focus_blacklist) &&
c2_list_postprocess(state, c->c, &option->transparent_clipping_blacklist))) {
log_error("Post-processing of conditionals failed, some of your "
"rules might not work");
}

View file

@ -1986,10 +1986,6 @@ err:
return true;
}
static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data) {
return load_shader_source(data, c2_list_get_data(cond));
}
static struct window_options win_options_from_config(const struct options *opts) {
struct window_options ret = {
.blur_background = opts->blur_method != BLUR_METHOD_NONE,
@ -2193,6 +2189,17 @@ 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;
if (!ps->o.inspect_monitor) {
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) {
@ -2227,9 +2234,11 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
options_postprocess_c2_lists(ps->c2_state, &ps->c, &ps->o);
// Load shader source file specified in the shader rules
if (c2_list_foreach(ps->o.window_shader_fg_rules, load_shader_source_for_condition, ps)) {
log_error("Failed to load shader source file for some of the window "
"shader rules");
c2_condition_list_foreach(&ps->o.window_shader_fg_rules, i) {
if (!load_shader_source(ps, c2_condition_get_data(i))) {
log_error("Failed to load shader source file for some of the "
"window shader rules");
}
}
if (load_shader_source(ps, ps->o.window_shader_fg)) {
log_error("Failed to load window shader source file");
@ -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)) {

6
src/utils/console.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
/// Generate ANSI escape code
#define ANSI(x) "\033[" x "m"
/// Create a string that will print `str` in bold when output to terminal
#define BOLD(str) "\033[1m" str "\033[0m"

View file

@ -24,10 +24,12 @@
#include "compiler.h"
#include "config.h"
#include "dbus.h"
#include "inspect.h"
#include "log.h"
#include "picom.h"
#include "region.h"
#include "render.h"
#include "utils/console.h"
#include "utils/misc.h"
#include "x.h"
@ -109,7 +111,7 @@ static bool win_is_focused(session_t *ps, struct win *w) {
(ps->o.mark_wmwin_focused && is_wmwin) ||
(ps->o.mark_ovredir_focused && wm_ref_client_of(w->tree_ref) == NULL && !is_wmwin) ||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
c2_match(ps->c2_state, w, ps->o.focus_blacklist, NULL))) {
c2_match(ps->c2_state, w, &ps->o.focus_blacklist, NULL))) {
return true;
}
return false;
@ -846,7 +848,7 @@ static void win_determine_shadow(session_t *ps, struct win *w) {
if (!ps->o.wintype_option[index_of_lowest_one(w->window_types)].shadow) {
log_debug("Shadow disabled by wintypes");
w->options.shadow = TRI_FALSE;
} else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) {
} else if (c2_match(ps->c2_state, w, &ps->o.shadow_blacklist, NULL)) {
log_debug("Shadow disabled by shadow-exclude");
w->options.shadow = TRI_FALSE;
} else if (ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) {
@ -892,7 +894,7 @@ bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms
static void win_determine_clip_shadow_above(session_t *ps, struct win *w) {
bool should_crop =
(ps->o.wintype_option[index_of_lowest_one(w->window_types)].clip_shadow_above ||
c2_match(ps->c2_state, w, ps->o.shadow_clip_list, NULL));
c2_match(ps->c2_state, w, &ps->o.shadow_clip_list, NULL));
w->options.clip_shadow_above = should_crop ? TRI_TRUE : TRI_UNKNOWN;
}
@ -905,7 +907,7 @@ static void win_determine_invert_color(session_t *ps, struct win *w) {
return;
}
if (c2_match(ps->c2_state, w, ps->o.invert_color_list, NULL)) {
if (c2_match(ps->c2_state, w, &ps->o.invert_color_list, NULL)) {
w->options.invert_color = TRI_TRUE;
}
}
@ -925,7 +927,7 @@ static void win_determine_blur_background(session_t *ps, struct win *w) {
if (!ps->o.wintype_option[index_of_lowest_one(w->window_types)].blur_background) {
log_debug("Blur background disabled by wintypes");
w->options.blur_background = TRI_FALSE;
} else if (c2_match(ps->c2_state, w, ps->o.blur_background_blacklist, NULL)) {
} else if (c2_match(ps->c2_state, w, &ps->o.blur_background_blacklist, NULL)) {
log_debug("Blur background disabled by blur-background-exclude");
w->options.blur_background = TRI_FALSE;
}
@ -937,13 +939,14 @@ static void win_determine_blur_background(session_t *ps, struct win *w) {
*/
static void win_determine_rounded_corners(session_t *ps, struct win *w) {
void *radius_override = NULL;
bool blacklisted = c2_match(ps->c2_state, w, ps->o.rounded_corners_blacklist, NULL);
bool blacklisted = c2_match(ps->c2_state, w, &ps->o.rounded_corners_blacklist, NULL);
if (blacklisted) {
w->options.corner_radius = 0;
return;
}
bool matched = c2_match(ps->c2_state, w, ps->o.corner_radius_rules, &radius_override);
bool matched =
c2_match(ps->c2_state, w, &ps->o.corner_radius_rules, &radius_override);
if (matched) {
log_debug("Window %#010x (%s) matched corner rule! %d", win_id(w),
w->name, (int)(long)radius_override);
@ -978,7 +981,7 @@ static void win_determine_fg_shader(session_t *ps, struct win *w) {
void *val = NULL;
w->options.shader = NULL;
if (c2_match(ps->c2_state, w, ps->o.window_shader_fg_rules, &val)) {
if (c2_match(ps->c2_state, w, &ps->o.window_shader_fg_rules, &val)) {
struct shader_info *shader = NULL;
HASH_FIND_STR(ps->shaders, val, shader);
w->options.shader = shader;
@ -995,28 +998,32 @@ void win_update_opacity_rule(session_t *ps, struct win *w) {
double opacity = NAN;
void *val = NULL;
if (c2_match(ps->c2_state, w, ps->o.opacity_rules, &val)) {
if (c2_match(ps->c2_state, w, &ps->o.opacity_rules, &val)) {
opacity = ((double)(long)val) / 100.0;
}
w->options.opacity = opacity;
}
struct win_update_rule_params {
struct win *w;
struct session *ps;
struct window_maybe_options options;
};
static bool win_update_rule(const c2_lptr_t *rule, void *args) {
auto params = (struct win_update_rule_params *)args;
static bool
win_update_rule(struct session *ps, struct win *w, const c2_condition *rule, bool inspect) {
void *pdata = NULL;
if (!c2_match_one(params->ps->c2_state, params->w, rule, &pdata)) {
if (inspect) {
printf(" %s ... ", c2_condition_to_str(rule));
}
bool matched = c2_match_one(ps->c2_state, w, rule, &pdata);
if (inspect) {
printf("%s\n", matched ? ANSI("1;32") "matched\033[0m" : "not matched");
}
if (!matched) {
return false;
}
auto wopts_next = (struct window_maybe_options *)pdata;
params->options = win_maybe_options_fold(params->options, *wopts_next);
if (inspect) {
inspect_dump_window_maybe_options(*wopts_next);
}
w->options = win_maybe_options_fold(*wopts_next, w->options);
return false;
}
@ -1027,14 +1034,21 @@ static bool win_update_rule(const c2_lptr_t *rule, void *args) {
*/
void win_on_factor_change(session_t *ps, struct win *w) {
auto wid = win_client_id(w, /*fallback_to_self=*/true);
bool inspect = (ps->o.inspect_win != XCB_NONE && win_id(w) == ps->o.inspect_win) ||
ps->o.inspect_monitor;
log_debug("Window %#010x, client %#010x (%s) factor change", win_id(w), wid, w->name);
c2_window_state_update(ps->c2_state, &w->c2_state, ps->c.c, wid, win_id(w));
// Focus and is_fullscreen needs to be updated first, as other rules might depend
// on the focused state of the window
win_update_is_fullscreen(ps, w);
if (ps->o.inspect_monitor) {
printf("Window %#010x (Client %#010x):\n======\n\n", win_id(w),
win_client_id(w, /*fallback_to_self=*/true));
}
assert(w->window_types != 0);
if (ps->o.rules == NULL) {
if (list_is_empty(&ps->o.rules)) {
bool focused = win_is_focused(ps, w);
auto window_type = index_of_lowest_one(w->window_types);
// Universal rules take precedence over wintype_option and
@ -1057,11 +1071,11 @@ void win_on_factor_change(session_t *ps, struct win *w) {
w->options.fade = TRI_UNKNOWN;
w->options.transparent_clipping = TRI_UNKNOWN;
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
c2_match(ps->c2_state, w, ps->o.paint_blacklist, NULL)) {
c2_match(ps->c2_state, w, &ps->o.paint_blacklist, NULL)) {
w->options.paint = TRI_FALSE;
}
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL)) {
c2_match(ps->c2_state, w, &ps->o.unredir_if_possible_blacklist, NULL)) {
if (ps->o.wintype_option[window_type].redir_ignore) {
w->options.unredir = WINDOW_UNREDIR_PASSIVE;
} else {
@ -1077,23 +1091,23 @@ void win_on_factor_change(session_t *ps, struct win *w) {
w->options.unredir = WINDOW_UNREDIR_WHEN_POSSIBLE;
}
if (c2_match(ps->c2_state, w, ps->o.fade_blacklist, NULL)) {
if (c2_match(ps->c2_state, w, &ps->o.fade_blacklist, NULL)) {
w->options.fade = TRI_FALSE;
}
if (c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL)) {
if (c2_match(ps->c2_state, w, &ps->o.transparent_clipping_blacklist, NULL)) {
w->options.transparent_clipping = TRI_FALSE;
}
w->options.full_shadow =
tri_from_bool(ps->o.wintype_option[window_type].full_shadow);
} else {
struct win_update_rule_params params = {
.w = w,
.ps = ps,
.options = WIN_MAYBE_OPTIONS_DEFAULT,
};
w->options = WIN_MAYBE_OPTIONS_DEFAULT;
assert(w->state == WSTATE_MAPPED);
c2_list_foreach(ps->o.rules, win_update_rule, &params);
w->options = params.options;
if (inspect) {
printf("Checking " BOLD("window rules") ":\n");
}
c2_condition_list_foreach_rev(&ps->o.rules, i) {
win_update_rule(ps, w, i, inspect);
}
if (safe_isnan(w->options.opacity) && w->has_opacity_prop) {
w->options.opacity = ((double)w->opacity_prop) / OPAQUE;
}
@ -1115,6 +1129,14 @@ 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 (inspect) {
inspect_dump_window(ps->c2_state, &ps->o, w);
printf("\n");
if (!ps->o.inspect_monitor) {
quit(ps);
}
}
}
/**