diff --git a/src/config.h b/src/config.h index aec3a13864..9a235a22fd 100644 --- a/src/config.h +++ b/src/config.h @@ -426,6 +426,7 @@ typedef struct options { struct script **all_scripts; c2_lptr_t *rules; + bool has_both_style_of_rules; } options_t; extern const char *const BACKEND_STRS[NUM_BKEND + 1]; @@ -504,4 +505,10 @@ static inline bool parse_vsync(const char *str) { /// Generate animation script for legacy fading options void generate_fading_config(struct options *opt); +static inline void log_warn_both_style_of_rules(const char *option_name) { + log_warn("Option \"%s\" is set along with \"rules\". \"rules\" will take " + "precedence, and \"%s\" will have no effect.", + option_name, option_name); +} + // vim: set noet sw=8 ts=8 : diff --git a/src/config_libconfig.c b/src/config_libconfig.c index fa422f7b12..e5ff7f4842 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -762,6 +762,11 @@ 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->rules); + } + // --dbus lcfg_lookup_bool(&cfg, "dbus", &opt->dbus); @@ -788,10 +793,18 @@ 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) { + log_warn_both_style_of_rules("inactive-opacity"); + opt->has_both_style_of_rules = true; + } } // --active_opacity if (config_lookup_float(&cfg, "active-opacity", &dval)) { opt->active_opacity = normalize_d(dval); + if (opt->rules) { + log_warn_both_style_of_rules("active-opacity"); + opt->has_both_style_of_rules = true; + } } // --corner-radius config_lookup_int(&cfg, "corner-radius", &opt->corner_radius); @@ -843,15 +856,34 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { goto out; } // --inactive-opacity-override - lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override); + if (lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override) && + opt->rules != NULL) { + log_warn_both_style_of_rules("inactive-opacity-override"); + opt->has_both_style_of_rules = true; + } // --inactive-dim - config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim); + if (config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim) && opt->rules != NULL) { + log_warn_both_style_of_rules("inactive-dim"); + opt->has_both_style_of_rules = true; + } // --mark-wmwin-focused - lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused); + if (lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused) && + opt->rules != NULL) { + log_warn_both_style_of_rules("mark-wmwin-focused"); + opt->has_both_style_of_rules = true; + } // --mark-ovredir-focused - lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opt->mark_ovredir_focused); + if (lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opt->mark_ovredir_focused) && + opt->rules != NULL) { + log_warn_both_style_of_rules("mark-ovredir-focused"); + opt->has_both_style_of_rules = true; + } // --shadow-ignore-shaped - lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &opt->shadow_ignore_shaped); + if (lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &opt->shadow_ignore_shaped) && + opt->rules != NULL) { + log_warn_both_style_of_rules("shadow-ignore-shaped"); + opt->has_both_style_of_rules = true; + } // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", &opt->detect_rounded_corners); // --crop-shadow-to-monitor @@ -931,24 +963,50 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { // --dithered_present lcfg_lookup_bool(&cfg, "dithered-present", &opt->dithered_present); - if (!parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist, - "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, "blur-background-exclude") || - !parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist, - "unredir-if-possible-exclude") || - !parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist, "rounded-corners-exclude") || - !parse_cfg_condlst_with_prefix(&opt->corner_radius_rules, &cfg, "corner-radius-rules", - parse_numeric_prefix, NULL, (int[]){0, INT_MAX}) || - !parse_cfg_condlst_with_prefix(&opt->opacity_rules, &cfg, "opacity-rule", - parse_numeric_prefix, NULL, (int[]){0, 100}) || - !parse_cfg_condlst_with_prefix( - &opt->window_shader_fg_rules, &cfg, "window-shader-fg-rule", - parse_window_shader_prefix, free, (void *)config_get_include_dir(&cfg))) { + if (opt->rules != NULL) { + static const char *rule_list[] = { + "transparent-clipping-exclude", + "shadow-exclude", + "clip-shadow-above", + "fade-exclude", + "focus-exclude", + "invert-color-include", + "blur-background-exclude", + "unredir-if-possible-exclude", + "rounded-corners-exclude", + "corner-radius-rules", + "opacity-rule", + "window-shader-fg-rule", + "wintypes", + }; + for (size_t i = 0; i < sizeof(rule_list) / sizeof(rule_list[0]); i++) { + if (config_lookup(&cfg, rule_list[i])) { + log_warn_both_style_of_rules(rule_list[i]); + opt->has_both_style_of_rules = true; + } + } + } else if (!parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist, + "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, + "blur-background-exclude") || + !parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist, + "unredir-if-possible-exclude") || + !parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist, + "rounded-corners-exclude") || + !parse_cfg_condlst_with_prefix( + &opt->corner_radius_rules, &cfg, "corner-radius-rules", + parse_numeric_prefix, NULL, (int[]){0, INT_MAX}) || + !parse_cfg_condlst_with_prefix(&opt->opacity_rules, &cfg, "opacity-rule", + parse_numeric_prefix, NULL, (int[]){0, 100}) || + !parse_cfg_condlst_with_prefix(&opt->window_shader_fg_rules, &cfg, + "window-shader-fg-rule", + parse_window_shader_prefix, free, + (void *)config_get_include_dir(&cfg))) { goto out; } @@ -1079,25 +1137,22 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { // Wintype settings // XXX ! Refactor all the wintype_* arrays into a struct - 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]); + if (opt->rules == NULL) { + 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]); + } + // Compatibility with the old name for notification windows. + parse_wintype_config(&cfg, "notify", + &opt->wintype_option[WINTYPE_NOTIFICATION], + &opt->wintype_option_mask[WINTYPE_NOTIFICATION]); } - // Compatibility with the old name for notification windows. - parse_wintype_config(&cfg, "notify", &opt->wintype_option[WINTYPE_NOTIFICATION], - &opt->wintype_option_mask[WINTYPE_NOTIFICATION]); - config_setting_t *animations = config_lookup(&cfg, "animations"); if (animations) { parse_animations(opt->animations, animations, &opt->all_scripts); } - config_setting_t *rules = config_lookup(&cfg, "rules"); - if (rules) { - parse_rules(rules, &opt->rules); - } - opt->config_file_path = path; path = NULL; succeeded = true; diff --git a/src/options.c b/src/options.c index 120bd248f4..41030a1068 100644 --- a/src/options.c +++ b/src/options.c @@ -67,6 +67,17 @@ static bool set_flag(const struct picom_option * /*opt*/, const struct picom_arg return true; } +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) { + log_warn_both_style_of_rules(arg_opt->long_name); + opt->has_both_style_of_rules = true; + return true; + } + *(bool *)(output + arg->offset) = true; + return true; +} static bool unset_flag(const struct picom_option * /*opt*/, const struct picom_arg *arg, const char * /*arg_str*/, void *output) { *(bool *)(output + arg->offset) = false; @@ -100,6 +111,17 @@ static bool store_float(const struct picom_option *opt, const struct picom_arg * return true; } +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) { + log_warn_both_style_of_rules(arg_opt->long_name); + opt->has_both_style_of_rules = true; + return true; + } + return store_float(arg_opt, arg, arg_str, output); +} + static bool store_int(const struct picom_option *opt, const struct picom_arg *arg, const char *arg_str, void *output) { const int *minmax = (const int *)arg->user_data; @@ -121,9 +143,15 @@ static bool store_string(const struct picom_option * /*opt*/, const struct picom return true; } -static bool store_rules(const struct picom_option * /*opt*/, const struct picom_arg *arg, +static bool store_rules(const struct picom_option *arg_opt, const struct picom_arg *arg, 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) { + 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); if (!parser->parse_prefix) { return c2_parse(rules, arg_str, NULL) != NULL; @@ -166,6 +194,13 @@ static bool say_deprecated(const struct picom_option *opt, const struct picom_ar .offset = OFFSET(member), .handler = set_flag, \ } +/// A true or false option that functions like a window rule. Which is superseded by the +/// `rules` option. +#define ENABLE_RULE(member) \ + no_argument, { \ + .offset = OFFSET(member), .handler = set_rule_flag, \ + } + #define DISABLE(member) \ no_argument, { \ .offset = OFFSET(member), .handler = unset_flag, \ @@ -201,6 +236,14 @@ static bool say_deprecated(const struct picom_option *opt, const struct picom_ar .user_data = (double[]){min, max}, \ } +/// A float option that functions like a window rule. Which is superseded by the `rules` +/// option. +#define FLOAT_RULE(member, min, max) \ + required_argument, { \ + .offset = OFFSET(member), .handler = store_rule_float, \ + .user_data = (double[]){min, max}, \ + } + #define INTEGER(member, min, max) \ required_argument, { \ .offset = OFFSET(member), .handler = store_int, \ @@ -313,7 +356,7 @@ static bool store_backend(const struct picom_option * /*opt*/, const struct pico } #define WINDOW_SHADER_RULE \ - { .parse_prefix = parse_window_shader_prefix_with_cwd, .free_value = free, } + {.parse_prefix = parse_window_shader_prefix_with_cwd, .free_value = free} #ifdef CONFIG_OPENGL #define BACKENDS "xrender, glx" @@ -336,16 +379,22 @@ static const struct picom_option picom_options[] = { [256] = {"config" , IGNORE(required_argument), "Path to the configuration file."}, [307] = {"plugins" , IGNORE(required_argument), "Plugins to load. Can be specified multiple times, each time with a single plugin."}, + // "Rule-like" options + [262] = {"mark-wmwin-focused" , ENABLE_RULE(mark_wmwin_focused) , "Try to detect WM windows and mark them as active."}, + [264] = {"mark-ovredir-focused" , ENABLE_RULE(mark_ovredir_focused) , "Mark windows that have no WM frame as active."}, + [266] = {"shadow-ignore-shaped" , ENABLE_RULE(shadow_ignore_shaped) , "Do not paint shadows on shaped windows. (Deprecated, use --shadow-exclude " + "\'bounding_shaped\' or --shadow-exclude \'bounding_shaped && " + "!rounded_corners\' instead.)"}, + [260] = {"inactive-opacity-override", ENABLE_RULE(inactive_opacity_override), "Inactive opacity set by -i overrides value of _NET_WM_WINDOW_OPACITY."}, + [297] = {"active-opacity" , FLOAT_RULE(active_opacity, 0, 1) , "Default opacity for active windows. (0.0 - 1.0)"}, + [261] = {"inactive-dim" , FLOAT_RULE(inactive_dim, 0, 1) , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"}, + ['i'] = {"inactive-opacity" , FLOAT_RULE(inactive_opacity, 0, 1) , "Opacity of inactive windows. (0.0 - 1.0)"}, + // Simple flags ['c'] = {"shadow" , ENABLE(shadow_enable) , "Enabled client-side shadows on windows."}, ['f'] = {"fading" , ENABLE(fading_enable) , "Fade windows in/out when opening/closing and when opacity changes, " "unless --no-fading-openclose is used."}, - [262] = {"mark-wmwin-focused" , ENABLE(mark_wmwin_focused) , "Try to detect WM windows and mark them as active."}, - [264] = {"mark-ovredir-focused" , ENABLE(mark_ovredir_focused) , "Mark windows that have no WM frame as active."}, [265] = {"no-fading-openclose" , ENABLE(no_fading_openclose) , "Do not fade on window open/close."}, - [266] = {"shadow-ignore-shaped" , ENABLE(shadow_ignore_shaped) , "Do not paint shadows on shaped windows. (Deprecated, use --shadow-exclude " - "\'bounding_shaped\' or --shadow-exclude \'bounding_shaped && " - "!rounded_corners\' instead.)"}, [268] = {"detect-client-opacity" , ENABLE(detect_client_opacity) , "Detect _NET_WM_WINDOW_OPACITY on client windows, useful for window " "managers not passing _NET_WM_WINDOW_OPACITY of client windows to frame"}, [270] = {"vsync" , ENABLE(vsync) , "Enable VSync"}, @@ -381,7 +430,6 @@ static const struct picom_option picom_options[] = { [324] = {"no-use-damage" , DISABLE(use_damage) , "Disable the use of damage information. This cause the whole screen to be" "redrawn every time, instead of the part of the screen that has actually " "changed. Potentially degrades the performance, but might fix some artifacts."}, - [260] = {"inactive-opacity-override", ENABLE(inactive_opacity_override), "Inactive opacity set by -i overrides value of _NET_WM_WINDOW_OPACITY."}, [267] = {"detect-rounded-corners" , ENABLE(detect_rounded_corners) , "Try to detect windows with rounded corners and don't consider them shaped " "windows. Affects --shadow-ignore-shaped, --unredir-if-possible, and " "possibly others. You need to turn this on manually if you want to match " @@ -413,16 +461,13 @@ static const struct picom_option picom_options[] = { ['I'] = {"fade-in-step" , FLOAT(fade_in_step, 0, 1) , "Opacity change between steps while fading in. (default 0.028)"}, ['O'] = {"fade-out-step" , FLOAT(fade_out_step, 0, 1) , "Opacity change between steps while fading out. (default 0.03)"}, ['D'] = {"fade-delta" , INTEGER(fade_delta, 1, INT_MAX) , "The time between steps in a fade in milliseconds. (default 10)"}, - ['i'] = {"inactive-opacity" , FLOAT(inactive_opacity, 0, 1) , "Opacity of inactive windows. (0.0 - 1.0)"}, ['e'] = {"frame-opacity" , FLOAT(frame_opacity, 0, 1) , "Opacity of window titlebars and borders. (0.0 - 1.0)"}, [257] = {"shadow-red" , FLOAT(shadow_red, 0, 1) , "Red color value of shadow (0.0 - 1.0, defaults to 0)."}, [258] = {"shadow-green" , FLOAT(shadow_green, 0, 1) , "Green color value of shadow (0.0 - 1.0, defaults to 0)."}, [259] = {"shadow-blue" , FLOAT(shadow_blue, 0, 1) , "Blue color value of shadow (0.0 - 1.0, defaults to 0)."}, - [261] = {"inactive-dim" , FLOAT(inactive_dim, 0, 1) , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"}, [283] = {"blur-background" , FIXED(blur_method, BLUR_METHOD_KERNEL) , "Blur background of semi-transparent / ARGB windows. May impact performance"}, [290] = {"backend" , DO(store_backend) , "Backend. Possible values are: " BACKENDS}, [293] = {"benchmark" , INTEGER(benchmark, 0, INT_MAX) , "Benchmark mode. Repeatedly paint until reaching the specified cycles."}, - [297] = {"active-opacity" , FLOAT(active_opacity, 0, 1) , "Default opacity for active windows. (0.0 - 1.0)"}, [302] = {"resize-damage" , INTEGER(resize_damage, INT_MIN, INT_MAX)}, // only used by legacy backends [309] = {"unredir-if-possible-delay" , INTEGER(unredir_if_possible_delay, 0, INT_MAX) , "Delay before unredirecting the window, in milliseconds. Defaults to 0."}, [310] = {"write-pid-path" , NAMED_STRING(write_pid_path, "PATH") , "Write process ID to a file."}, @@ -879,6 +924,15 @@ static bool sanitize_options(struct options *opt) { log_warn("Negative --resize-damage will not work correctly."); } + if (opt->has_both_style_of_rules) { + log_warn("You have set both \"rules\", as well as old-style rule options " + "in your configuration. The old-style rule options will have no " + "effect. It is recommended that you remove the old-style rule " + "options, and use only \"rules\" for all your window rules. If " + "you do genuinely need to use the old-style rule options, you " + "must not set \"rules\"."); + } + return true; }