Skip to content

Commit 0efe479

Browse files
authored
Merge pull request #1303 from yshui/animation-rules
Animation rules
2 parents 567c0ba + 4777327 commit 0efe479

File tree

5 files changed

+76
-53
lines changed

5 files changed

+76
-53
lines changed

src/config.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ static const char *animation_trigger_names[] attr_unused = {
9999

100100
struct script;
101101
struct win_script {
102-
int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS];
103102
/// A running animation can be configured to prevent other animations from
104103
/// starting.
105104
uint64_t suppressions;
106105
struct script *script;
107106
/// true if this script is generated by us, false if this is a user choice.
107+
int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS];
108108
bool is_generated;
109109
};
110110

@@ -192,6 +192,9 @@ struct window_maybe_options {
192192
enum window_unredir_option unredir;
193193
/// Whether shadow should be rendered beneath this window.
194194
enum tristate full_shadow;
195+
196+
/// Window specific animations
197+
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
195198
};
196199

197200
// Make sure `window_options` has no implicit padding.
@@ -212,12 +215,16 @@ struct window_options {
212215
bool clip_shadow_above;
213216
bool paint;
214217
bool full_shadow;
218+
219+
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
215220
};
216221
#pragma GCC diagnostic pop
217222

218223
static inline bool
219-
win_options_eq(const struct window_options *a, const struct window_options *b) {
220-
return memcmp(a, b, sizeof(struct window_options)) == 0;
224+
win_options_no_damage(const struct window_options *a, const struct window_options *b) {
225+
// Animation changing does not immediately change how window is rendered, so
226+
// they don't cause damage.
227+
return memcmp(a, b, offsetof(struct window_options, animations)) == 0;
221228
}
222229

223230
extern struct shader_info null_shader;

src/config_libconfig.c

+12-9
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ static const struct {
580580
{"transparent-clipping", offsetof(struct window_maybe_options, transparent_clipping)},
581581
};
582582

583-
static c2_lptr_t *parse_rule(config_setting_t *setting) {
583+
static c2_lptr_t *parse_rule(config_setting_t *setting, struct script ***out_scripts) {
584584
if (!config_setting_is_group(setting)) {
585585
log_error("Invalid rule at line %d. It must be a group.",
586586
config_setting_source_line(setting));
@@ -603,10 +603,7 @@ static c2_lptr_t *parse_rule(config_setting_t *setting) {
603603
}
604604

605605
auto wopts = cmalloc(struct window_maybe_options);
606-
*wopts = (struct window_maybe_options){
607-
.opacity = NAN,
608-
.corner_radius = -1,
609-
};
606+
*wopts = WIN_MAYBE_OPTIONS_DEFAULT;
610607
c2_list_set_data(rule, wopts);
611608

612609
for (size_t i = 0; i < ARR_SIZE(all_window_options); i++) {
@@ -625,14 +622,20 @@ static c2_lptr_t *parse_rule(config_setting_t *setting) {
625622
wopts->corner_radius = ival;
626623
}
627624

628-
auto unredir_setting = config_setting_lookup(setting, "unredir-if-possible");
625+
auto unredir_setting = config_setting_lookup(setting, "unredir");
629626
if (unredir_setting) {
630627
wopts->unredir = parse_unredir_option(unredir_setting);
631628
}
629+
630+
auto animations = config_setting_lookup(setting, "animations");
631+
if (animations) {
632+
parse_animations(wopts->animations, animations, out_scripts);
633+
}
632634
return rule;
633635
}
634636

635-
static void parse_rules(config_setting_t *setting, c2_lptr_t **rules) {
637+
static void
638+
parse_rules(config_setting_t *setting, struct script ***out_scripts, c2_lptr_t **rules) {
636639
if (!config_setting_is_list(setting)) {
637640
log_error("Invalid value for \"rules\" at line %d. It must be a list.",
638641
config_setting_source_line(setting));
@@ -641,7 +644,7 @@ static void parse_rules(config_setting_t *setting, c2_lptr_t **rules) {
641644
const auto length = (unsigned int)config_setting_length(setting);
642645
for (unsigned int i = 0; i < length; i++) {
643646
auto sub = config_setting_get_elem(setting, i);
644-
auto rule = parse_rule(sub);
647+
auto rule = parse_rule(sub, out_scripts);
645648
if (rule != NULL) {
646649
c2_condlist_insert(rules, rule);
647650
}
@@ -767,7 +770,7 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
767770

768771
config_setting_t *rules = config_lookup(&cfg, "rules");
769772
if (rules) {
770-
parse_rules(rules, &opt->rules);
773+
parse_rules(rules, &opt->all_scripts, &opt->rules);
771774
}
772775

773776
// --dbus

src/picom.c

+14-12
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,10 @@ static void destroy_backend(session_t *ps) {
463463
// `win_process_animation_and_state_change` hasn't been called.)
464464
// TBH, this assertion is probably too complex than what it's worth.
465465
assert(!w->win_image || w->state == WSTATE_MAPPED ||
466-
w->running_animation != NULL || w->previous.state != w->state);
466+
w->running_animation_instance != NULL || w->previous.state != w->state);
467467
// Wrapping up animation in progress
468-
free(w->running_animation);
469-
w->running_animation = NULL;
468+
free(w->running_animation_instance);
469+
w->running_animation_instance = NULL;
470470

471471
if (ps->backend_data) {
472472
// Unmapped windows could still have shadow images.
@@ -751,15 +751,15 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
751751
const winmode_t mode_old = w->mode;
752752
const bool was_painted = w->to_paint;
753753

754-
if (w->running_animation != NULL) {
754+
if (w->running_animation_instance != NULL) {
755755
*animation = true;
756756
}
757757

758758
// Add window to damaged area if its opacity changes
759759
// If was_painted == false, and to_paint is also false, we don't care
760760
// If was_painted == false, but to_paint is true, damage will be added in
761761
// the loop below
762-
if (was_painted && w->running_animation != NULL) {
762+
if (was_painted && w->running_animation_instance != NULL) {
763763
add_damage_from_win(ps, w);
764764
}
765765

@@ -817,7 +817,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
817817
// pixmap is gone (for example due to a ConfigureNotify), or when it's
818818
// excluded
819819
if ((w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYED) &&
820-
w->running_animation == NULL) {
820+
w->running_animation_instance == NULL) {
821821
log_trace("|- is unmapped");
822822
to_paint = false;
823823
} else if (unlikely(ps->debug_window != XCB_NONE) &&
@@ -929,7 +929,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
929929
reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid;
930930
w->reg_ignore_valid = true;
931931

932-
if (w->state == WSTATE_DESTROYED && w->running_animation == NULL) {
932+
if (w->state == WSTATE_DESTROYED && w->running_animation_instance == NULL) {
933933
// the window should be destroyed because it was destroyed
934934
// by X server and now its animations are finished
935935
win_destroy_finish(ps, w);
@@ -1662,8 +1662,8 @@ static void handle_pending_updates(struct session *ps, double delta_t) {
16621662
// Window might be freed by this function, if it's destroyed and its
16631663
// animation finished
16641664
if (w != NULL && win_process_animation_and_state_change(ps, w, delta_t)) {
1665-
free(w->running_animation);
1666-
w->running_animation = NULL;
1665+
free(w->running_animation_instance);
1666+
w->running_animation_instance = NULL;
16671667
w->in_openclose = false;
16681668
if (w->state == WSTATE_UNMAPPED) {
16691669
unmap_win_finish(ps, w);
@@ -1738,8 +1738,8 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
17381738
if (w == NULL) {
17391739
continue;
17401740
}
1741-
free(w->running_animation);
1742-
w->running_animation = NULL;
1741+
free(w->running_animation_instance);
1742+
w->running_animation_instance = NULL;
17431743
if (w->state == WSTATE_DESTROYED) {
17441744
win_destroy_finish(ps, w);
17451745
}
@@ -1991,7 +1991,7 @@ static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data)
19911991
}
19921992

19931993
static struct window_options win_options_from_config(const struct options *opts) {
1994-
return (struct window_options){
1994+
struct window_options ret = {
19951995
.blur_background = opts->blur_method != BLUR_METHOD_NONE,
19961996
.full_shadow = false,
19971997
.shadow = opts->shadow_enable,
@@ -2006,6 +2006,8 @@ static struct window_options win_options_from_config(const struct options *opts)
20062006
.unredir = WINDOW_UNREDIR_WHEN_POSSIBLE_ELSE_TERMINATE,
20072007
.opacity = 1,
20082008
};
2009+
memcpy(ret.animations, opts->animations, sizeof(ret.animations));
2010+
return ret;
20092011
}
20102012

20112013
/**

src/wm/win.c

+25-24
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ void win_process_secondary_flags(session_t *ps, struct win *w) {
492492
}
493493

494494
auto new_options = win_options(w);
495-
if (win_options_eq(&old_options, &new_options)) {
495+
if (win_options_no_damage(&old_options, &new_options)) {
496496
pixman_region32_fini(&extents);
497497
return;
498498
}
@@ -792,7 +792,7 @@ void unmap_win_finish(session_t *ps, struct win *w) {
792792
if (w->state != WSTATE_DESTROYED) {
793793
win_clear_flags(w, WIN_FLAGS_PIXMAP_ERROR);
794794
}
795-
assert(w->running_animation == NULL);
795+
assert(w->running_animation_instance == NULL);
796796
}
797797

798798
/**
@@ -1715,8 +1715,9 @@ struct win_script_context win_script_context_prepare(struct session *ps, struct
17151715
}
17161716

17171717
double win_animatable_get(const struct win *w, enum win_script_output output) {
1718-
if (w->running_animation && w->running_animation_outputs[output] >= 0) {
1719-
return w->running_animation->memory[w->running_animation_outputs[output]];
1718+
if (w->running_animation_instance && w->running_animation.output_indices[output] >= 0) {
1719+
return w->running_animation_instance
1720+
->memory[w->running_animation.output_indices[output]];
17201721
}
17211722
switch (output) {
17221723
case WIN_SCRIPT_BLUR_OPACITY: return w->state == WSTATE_MAPPED ? 1.0 : 0.0;
@@ -1758,23 +1759,23 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
17581759
bool state_changed = w->previous.state != w->state;
17591760
w->previous.state = w->state;
17601761
w->previous.opacity = w->opacity;
1761-
return state_changed || (w->running_animation != NULL);
1762+
return state_changed || (w->running_animation_instance != NULL);
17621763
}
17631764

17641765
auto win_ctx = win_script_context_prepare(ps, w);
17651766
w->previous.opacity = w->opacity;
17661767
if (w->previous.state == w->state && win_ctx.opacity_before == win_ctx.opacity) {
17671768
advance_animation:
17681769
// No state changes, if there's a animation running, we just continue it.
1769-
if (w->running_animation == NULL) {
1770+
if (w->running_animation_instance == NULL) {
17701771
return false;
17711772
}
17721773
log_verbose("Advance animation for %#010x (%s) %f seconds", win_id(w),
17731774
w->name, delta_t);
1774-
if (!script_instance_is_finished(w->running_animation)) {
1775-
w->running_animation->elapsed += delta_t;
1776-
auto result =
1777-
script_instance_evaluate(w->running_animation, &win_ctx);
1775+
if (!script_instance_is_finished(w->running_animation_instance)) {
1776+
w->running_animation_instance->elapsed += delta_t;
1777+
auto result = script_instance_evaluate(
1778+
w->running_animation_instance, &win_ctx);
17781779
if (result != SCRIPT_EVAL_OK) {
17791780
log_error("Failed to run animation script: %d", result);
17801781
return true;
@@ -1824,7 +1825,7 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
18241825
break;
18251826
case WSTATE_PAIR(WSTATE_UNMAPPED, WSTATE_DESTROYED):
18261827
if ((!ps->o.no_fading_destroyed_argb || !win_has_alpha(w)) &&
1827-
w->running_animation != NULL) {
1828+
w->running_animation_instance != NULL) {
18281829
trigger = ANIMATION_TRIGGER_CLOSE;
18291830
}
18301831
break;
@@ -1847,19 +1848,20 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
18471848
}
18481849
}
18491850

1850-
if (trigger != ANIMATION_TRIGGER_INVALID && w->running_animation &&
1851-
(w->running_animation_suppressions & (1 << trigger)) != 0) {
1851+
if (trigger != ANIMATION_TRIGGER_INVALID && w->running_animation_instance &&
1852+
(w->running_animation.suppressions & (1 << trigger)) != 0) {
18521853
log_debug("Not starting animation %s for window %#010x (%s) because it "
18531854
"is being suppressed.",
18541855
animation_trigger_names[trigger], win_id(w), w->name);
18551856
goto advance_animation;
18561857
}
18571858

1858-
if (trigger == ANIMATION_TRIGGER_INVALID || ps->o.animations[trigger].script == NULL) {
1859+
auto wopts = win_options(w);
1860+
if (trigger == ANIMATION_TRIGGER_INVALID || wopts.animations[trigger].script == NULL) {
18591861
return true;
18601862
}
18611863

1862-
if (ps->o.animations[trigger].is_generated && !win_options(w).fade) {
1864+
if (wopts.animations[trigger].is_generated && !wopts.fade) {
18631865
// Window's animation is fading (as signified by the fact that it's
18641866
// generated), but the user has disabled fading for this window.
18651867
return true;
@@ -1868,16 +1870,15 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
18681870
log_debug("Starting animation %s for window %#010x (%s)",
18691871
animation_trigger_names[trigger], win_id(w), w->name);
18701872

1871-
auto new_animation = script_instance_new(ps->o.animations[trigger].script);
1872-
if (w->running_animation) {
1873-
script_instance_resume_from(w->running_animation, new_animation);
1874-
free(w->running_animation);
1873+
auto new_animation = script_instance_new(wopts.animations[trigger].script);
1874+
if (w->running_animation_instance) {
1875+
script_instance_resume_from(w->running_animation_instance, new_animation);
1876+
free(w->running_animation_instance);
18751877
}
1876-
w->running_animation = new_animation;
1877-
w->running_animation_outputs = ps->o.animations[trigger].output_indices;
1878-
w->running_animation_suppressions = ps->o.animations[trigger].suppressions;
1879-
script_instance_evaluate(w->running_animation, &win_ctx);
1880-
return false;
1878+
w->running_animation_instance = new_animation;
1879+
w->running_animation = wopts.animations[trigger];
1880+
script_instance_evaluate(w->running_animation_instance, &win_ctx);
1881+
return script_instance_is_finished(w->running_animation_instance);
18811882
}
18821883

18831884
#undef WSTATE_PAIR

src/wm/win.h

+15-5
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,8 @@ struct win {
251251
/// by `win_process_animation_and_state_change` to trigger appropriate
252252
/// animations.
253253
struct win_state_change previous;
254-
struct script_instance *running_animation;
255-
const int *running_animation_outputs;
256-
uint64_t running_animation_suppressions;
254+
struct script_instance *running_animation_instance;
255+
struct win_script running_animation;
257256
};
258257

259258
struct win_script_context {
@@ -310,12 +309,19 @@ static const struct window_maybe_options WIN_MAYBE_OPTIONS_DEFAULT = {
310309
.unredir = WINDOW_UNREDIR_INVALID,
311310
};
312311

312+
static inline void win_script_fold(const struct win_script *upper,
313+
const struct win_script *lower, struct win_script *output) {
314+
for (size_t i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
315+
output[i] = upper[i].script ? upper[i] : lower[i];
316+
}
317+
}
318+
313319
/// Combine two window options. The `upper` value has higher priority, the `lower` value
314320
/// will only be used if the corresponding value in `upper` is not set (e.g. it is
315321
/// TRI_UNKNOWN for tristate values, NaN for opacity, -1 for corner_radius).
316322
static inline struct window_maybe_options __attribute__((always_inline))
317323
win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_options lower) {
318-
return (struct window_maybe_options){
324+
struct window_maybe_options ret = {
319325
.unredir = upper.unredir == WINDOW_UNREDIR_INVALID ? lower.unredir : upper.unredir,
320326
.blur_background = tri_or(upper.blur_background, lower.blur_background),
321327
.clip_shadow_above = tri_or(upper.clip_shadow_above, lower.clip_shadow_above),
@@ -329,14 +335,16 @@ win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_op
329335
.shader = upper.shader ? upper.shader : lower.shader,
330336
.corner_radius = upper.corner_radius >= 0 ? upper.corner_radius : lower.corner_radius,
331337
};
338+
win_script_fold(upper.animations, lower.animations, ret.animations);
339+
return ret;
332340
}
333341

334342
/// Unwrap a `window_maybe_options` to a `window_options`, using the default value for
335343
/// values that are not set in the `window_maybe_options`.
336344
static inline struct window_options __attribute__((always_inline))
337345
win_maybe_options_or(struct window_maybe_options maybe, struct window_options def) {
338346
assert(def.unredir != WINDOW_UNREDIR_INVALID);
339-
return (struct window_options){
347+
struct window_options ret = {
340348
.unredir = maybe.unredir == WINDOW_UNREDIR_INVALID ? def.unredir : maybe.unredir,
341349
.blur_background = tri_or_bool(maybe.blur_background, def.blur_background),
342350
.clip_shadow_above = tri_or_bool(maybe.clip_shadow_above, def.clip_shadow_above),
@@ -351,6 +359,8 @@ win_maybe_options_or(struct window_maybe_options maybe, struct window_options de
351359
.dim = !safe_isnan(maybe.dim) ? maybe.dim : def.dim,
352360
.shader = maybe.shader ? maybe.shader : def.shader,
353361
};
362+
win_script_fold(maybe.animations, def.animations, ret.animations);
363+
return ret;
354364
}
355365

356366
static inline struct window_options __attribute__((always_inline))

0 commit comments

Comments
 (0)