From 9502d7509d3b80103a910ec8d1290ba216aaba8b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 20 Apr 2024 13:51:59 +0100 Subject: [PATCH] config_libconfig: parse animation scripts Signed-off-by: Yuxuan Shui --- src/config.h | 37 +++++++++++ src/config_libconfig.c | 142 +++++++++++++++++++++++++++++++++++++++++ src/options.c | 7 ++ src/win.h | 27 ++++++++ src/win_defs.h | 11 ++++ 5 files changed, 224 insertions(+) diff --git a/src/config.h b/src/config.h index 68aef3b3..16d7ce3e 100644 --- a/src/config.h +++ b/src/config.h @@ -81,6 +81,38 @@ enum vblank_scheduler_type { LAST_VBLANK_SCHEDULER, }; +enum animation_trigger { + ANIMATION_TRIGGER_INVALID = -1, + /// When a hidden window is shown + ANIMATION_TRIGGER_SHOW = 0, + /// When a window is hidden + ANIMATION_TRIGGER_HIDE, + /// When window opacity is increased + ANIMATION_TRIGGER_INCREASE_OPACITY, + /// When window opacity is decreased + ANIMATION_TRIGGER_DECREASE_OPACITY, + /// When a new window opens + ANIMATION_TRIGGER_OPEN, + /// When a window is closed + ANIMATION_TRIGGER_CLOSE, + ANIMATION_TRIGGER_LAST = ANIMATION_TRIGGER_CLOSE, +}; + +static const char *animation_trigger_names[] attr_unused = { + [ANIMATION_TRIGGER_SHOW] = "show", + [ANIMATION_TRIGGER_HIDE] = "hide", + [ANIMATION_TRIGGER_INCREASE_OPACITY] = "increase-opacity", + [ANIMATION_TRIGGER_DECREASE_OPACITY] = "decrease-opacity", + [ANIMATION_TRIGGER_OPEN] = "open", + [ANIMATION_TRIGGER_CLOSE] = "close", +}; + +struct script; +struct win_script { + int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS]; + struct script *script; +}; + extern const char *vblank_scheduler_str[]; /// Internal, private options for debugging and development use. @@ -292,6 +324,11 @@ typedef struct options { c2_lptr_t *transparent_clipping_blacklist; bool dithered_present; + // === Animation === + struct win_script animations[ANIMATION_TRIGGER_LAST + 1]; + /// Array of all the scripts used in `animations`. + struct script **all_scripts; + int number_of_scripts; } options_t; extern const char *const BACKEND_STRS[NUM_BKEND + 1]; diff --git a/src/config_libconfig.c b/src/config_libconfig.c index df9c4961..008b0215 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -15,8 +15,10 @@ #include "config.h" #include "err.h" #include "log.h" +#include "script.h" #include "string_utils.h" #include "utils.h" +#include "win.h" #pragma GCC diagnostic error "-Wunused-parameter" @@ -220,6 +222,140 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_ } } +static enum animation_trigger parse_animation_trigger(const char *trigger) { + for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) { + if (strcasecmp(trigger, animation_trigger_names[i]) == 0) { + return i; + } + } + return ANIMATION_TRIGGER_INVALID; +} + +static struct script * +compile_win_script(config_setting_t *setting, int *output_indices, char **err) { + struct script_output_info outputs[ARR_SIZE(win_script_outputs)]; + memcpy(outputs, win_script_outputs, sizeof(win_script_outputs)); + + struct script_parse_config parse_config = { + .context_info = win_script_context_info, + .output_info = outputs, + }; + auto script = script_compile(setting, parse_config, err); + if (script == NULL) { + return script; + } + for (int i = 0; i < NUM_OF_WIN_SCRIPT_OUTPUTS; i++) { + output_indices[i] = outputs[i].slot; + } + return script; +} + +static bool set_animation(struct win_script *animations, + const enum animation_trigger *triggers, int number_of_triggers, + struct script *script, const int *output_indices, unsigned line) { + bool needed = false; + for (int i = 0; i < number_of_triggers; i++) { + if (triggers[i] == ANIMATION_TRIGGER_INVALID) { + log_error("Invalid trigger defined at line %d", line); + continue; + } + if (animations[triggers[i]].script != NULL) { + log_error("Duplicate animation defined for trigger %s at line " + "%d, it will be ignored.", + animation_trigger_names[triggers[i]], line); + continue; + } + memcpy(animations[triggers[i]].output_indices, output_indices, + sizeof(int[NUM_OF_WIN_SCRIPT_OUTPUTS])); + animations[triggers[i]].script = script; + needed = true; + } + return needed; +} + +static struct script * +parse_animation_one(struct win_script *animations, config_setting_t *setting) { + auto triggers = config_setting_lookup(setting, "triggers"); + if (!triggers) { + log_error("Missing triggers in animation script, at line %d", + config_setting_source_line(setting)); + return NULL; + } + if (!config_setting_is_list(triggers) && !config_setting_is_array(triggers) && + config_setting_get_string(triggers) == NULL) { + log_error("The \"triggers\" option must either be a string, a list, or " + "an array, but is none of those at line %d", + config_setting_source_line(triggers)); + return NULL; + } + auto number_of_triggers = + config_setting_get_string(triggers) == NULL ? config_setting_length(triggers) : 1; + if (number_of_triggers > ANIMATION_TRIGGER_LAST) { + log_error("Too many triggers in animation defined at line %d", + config_setting_source_line(triggers)); + return NULL; + } + if (number_of_triggers == 0) { + log_error("Trigger list is empty in animation defined at line %d", + config_setting_source_line(triggers)); + return NULL; + } + enum animation_trigger *trigger_types = + alloca(sizeof(enum animation_trigger[number_of_triggers])); + const char *trigger0 = config_setting_get_string(triggers); + if (trigger0 == NULL) { + for (int i = 0; i < number_of_triggers; i++) { + auto trigger_i = config_setting_get_string_elem(triggers, i); + trigger_types[i] = trigger_i == NULL + ? ANIMATION_TRIGGER_INVALID + : parse_animation_trigger(trigger_i); + } + } else { + trigger_types[0] = parse_animation_trigger(trigger0); + } + + // script parser shouldn't see this. + config_setting_remove(setting, "triggers"); + + int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS]; + char *err; + auto script = compile_win_script(setting, output_indices, &err); + if (!script) { + log_error("Failed to parse animation script at line %d: %s", + config_setting_source_line(setting), err); + free(err); + return NULL; + } + + bool needed = set_animation(animations, trigger_types, number_of_triggers, script, + output_indices, config_setting_source_line(setting)); + if (!needed) { + script_free(script); + script = NULL; + } + return script; +} + +static struct script **parse_animations(struct win_script *animations, + config_setting_t *setting, int *number_of_scripts) { + auto number_of_animations = config_setting_length(setting); + auto all_scripts = ccalloc(number_of_animations + 1, struct script *); + auto len = 0; + for (int i = 0; i < number_of_animations; i++) { + auto sub = config_setting_get_elem(setting, (unsigned)i); + auto script = parse_animation_one(animations, sub); + if (script != NULL) { + all_scripts[len++] = script; + } + } + if (len == 0) { + free(all_scripts); + all_scripts = NULL; + } + *number_of_scripts = len; + return all_scripts; +} + /** * Parse a configuration file from default location. * @@ -607,6 +743,12 @@ char *parse_config_libconfig(options_t *opt, const char *config_file) { 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) { + opt->all_scripts = + parse_animations(opt->animations, animations, &opt->number_of_scripts); + } + config_destroy(&cfg); return path; diff --git a/src/options.c b/src/options.c index 7a502f0f..d6fcf686 100644 --- a/src/options.c +++ b/src/options.c @@ -930,6 +930,13 @@ void options_destroy(struct options *options) { } free(options->blur_kerns); free(options->glx_fshader_win_str); + + for (int i = 0; i < options->number_of_scripts; i++) { + script_free(options->all_scripts[i]); + options->all_scripts[i] = NULL; + } + free(options->all_scripts); + memset(options->animations, 0, sizeof(options->animations)); } // vim: set noet sw=8 ts=8 : diff --git a/src/win.h b/src/win.h index 58c094d6..66da22c4 100644 --- a/src/win.h +++ b/src/win.h @@ -16,6 +16,7 @@ #include "list.h" #include "region.h" #include "render.h" +#include "script.h" #include "transition.h" #include "types.h" #include "utils.h" @@ -305,6 +306,32 @@ struct managed_win { region_t damaged; }; +struct win_script_context { + double x, y, width, height; + double opacity_before, opacity; +}; + +static const struct script_context_info win_script_context_info[] = { + {"window-x", offsetof(struct win_script_context, x)}, + {"window-y", offsetof(struct win_script_context, y)}, + {"window-width", offsetof(struct win_script_context, width)}, + {"window-height", offsetof(struct win_script_context, height)}, + {"window-raw-opacity-before", offsetof(struct win_script_context, opacity_before)}, + {"window-raw-opacity", offsetof(struct win_script_context, opacity)}, + {NULL, 0} // +}; + +static const struct script_output_info win_script_outputs[] = { + [WIN_SCRIPT_OFFSET_X] = {"offset-x"}, + [WIN_SCRIPT_OFFSET_Y] = {"offset-y"}, + [WIN_SCRIPT_SHADOW_OFFSET_X] = {"shadow-offset-x"}, + [WIN_SCRIPT_SHADOW_OFFSET_Y] = {"shadow-offset-y"}, + [WIN_SCRIPT_OPACITY] = {"opacity"}, + [WIN_SCRIPT_BLUR_OPACITY] = {"blur-opacity"}, + [WIN_SCRIPT_SHADOW_OPACITY] = {"shadow-opacity"}, + [NUM_OF_WIN_SCRIPT_OUTPUTS] = {NULL}, +}; + /// Process pending updates/images flags on a window. Has to be called in X critical /// section void win_process_update_flags(session_t *ps, struct managed_win *w); diff --git a/src/win_defs.h b/src/win_defs.h index a9d3b5fd..aca62a86 100644 --- a/src/win_defs.h +++ b/src/win_defs.h @@ -65,3 +65,14 @@ enum win_flags { /// need better name for this, is set when some aspects of the window changed WIN_FLAGS_FACTOR_CHANGED = 1024, }; + +enum win_script_output { + WIN_SCRIPT_OFFSET_X = 0, + WIN_SCRIPT_OFFSET_Y, + WIN_SCRIPT_SHADOW_OFFSET_X, + WIN_SCRIPT_SHADOW_OFFSET_Y, + WIN_SCRIPT_OPACITY, + WIN_SCRIPT_BLUR_OPACITY, + WIN_SCRIPT_SHADOW_OPACITY, +}; +#define NUM_OF_WIN_SCRIPT_OUTPUTS (WIN_SCRIPT_SHADOW_OPACITY + 1)