diff --git a/src/transition.c b/src/transition.c index 89f4930c..939fc525 100644 --- a/src/transition.c +++ b/src/transition.c @@ -51,6 +51,11 @@ void animatable_step(struct animatable *a, unsigned int steps) { if (a->step_state) { a->step_state->current = a->target; } + if (a->callback) { + a->callback(TRANSITION_COMPLETED, a->callback_data); + a->callback = NULL; + a->callback_data = NULL; + } } } @@ -76,6 +81,11 @@ bool animatable_cancel(struct animatable *a) { if (a->step_state) { a->step_state->current = a->start; } + if (a->callback) { + a->callback(TRANSITION_CANCELED, a->callback_data); + a->callback = NULL; + a->callback_data = NULL; + } return true; } @@ -93,16 +103,25 @@ bool animatable_early_stop(struct animatable *a) { if (a->step_state) { a->step_state->current = a->target; } + if (a->callback) { + a->callback(TRANSITION_STOPPED_EARLY, a->callback_data); + a->callback = NULL; + a->callback_data = NULL; + } return true; } /// Change the target value of an `animatable`. /// If the `animatable` is already animating, the animation will be canceled first. -void animatable_set_target(struct animatable *a, double target, unsigned int duration) { +void animatable_set_target(struct animatable *a, double target, unsigned int duration, + transition_callback_fn cb, void *data) { animatable_cancel(a); if (!duration) { a->start = target; a->target = target; + if (cb) { + cb(TRANSITION_COMPLETED, data); + } return; } @@ -112,6 +131,8 @@ void animatable_set_target(struct animatable *a, double target, unsigned int dur if (a->step_state) { a->step(a, 0); } + a->callback = cb; + a->callback_data = data; } /// Create a new animatable. diff --git a/src/transition.h b/src/transition.h index c57cf4dc..1d55918b 100644 --- a/src/transition.h +++ b/src/transition.h @@ -5,6 +5,7 @@ #include struct animatable; +enum transition_event; /// The interpolator function for an animatable. This function should calculate the /// current value of the `animatable` based on its `start`, `target`, `duration` and /// `progress`. @@ -18,6 +19,22 @@ typedef double (*interpolator_fn)(const struct animatable *); /// is 0 or `step_state` is NULL, in which case `steps` is 0. typedef void (*step_fn)(struct animatable *, unsigned int steps); +/// Callback when the transition state changes. Callback might be called by: +/// - `animatable_set_target` generates TRANSITION_COMPLETED when the specified duration +/// is 0. also generates TRANSITION_CANCELLED if the animatable was already animating. +/// - `animatable_cancel` generates TRANSITION_CANCELED +/// - `animatable_early_stop` generates TRANSITION_STOPPED_EARLY +/// - `animatable_step` generates TRANSITION_COMPLETED when the animation is completed. +/// Callback is guaranteed to be called exactly once for each `animatable_set_target` +/// call, unless an animatable is freed before the transition is completed. +typedef void (*transition_callback_fn)(enum transition_event event, void *data); + +enum transition_event { + TRANSITION_COMPLETED, + TRANSITION_CANCELED, + TRANSITION_STOPPED_EARLY, +}; + /// The base type for step_state. struct step_state_base { /// The current value of the `animatable`. @@ -40,6 +57,9 @@ struct animatable { /// If the `animatable` is not animated, this is 0. unsigned int progress; + transition_callback_fn callback; + void *callback_data; + /// Step function state. struct step_state_base *step_state; /// The function for calculating the current value. If @@ -74,7 +94,8 @@ bool animatable_cancel(struct animatable *a); bool animatable_early_stop(struct animatable *a); /// Change the target value of an `animatable`. /// If the `animatable` is already animating, the animation will be canceled first. -void animatable_set_target(struct animatable *a, double target, unsigned int duration); +void animatable_set_target(struct animatable *a, double target, unsigned int duration, + transition_callback_fn cb, void *data); /// Create a new animatable. struct animatable animatable_new(double value, interpolator_fn interpolator, step_fn step); diff --git a/src/win.c b/src/win.c index ae5cb7e1..ab3d7722 100644 --- a/src/win.c +++ b/src/win.c @@ -903,7 +903,8 @@ static inline unsigned int win_start_fade(session_t *ps, struct managed_win *w) target_opacity > current_opacity ? ps->o.fade_in_step : ps->o.fade_out_step; unsigned int duration = (unsigned int)(fabs(target_opacity - current_opacity) / step_size); - animatable_set_target(&w->opacity, target_opacity, duration); + animatable_set_target(&w->opacity, target_opacity, duration, + NULL, NULL); return duration; } @@ -2412,7 +2413,7 @@ void unmap_win_start(session_t *ps, struct managed_win *w) { w->a.map_state = XCB_MAP_STATE_UNMAPPED; w->state = WSTATE_UNMAPPING; auto duration = win_start_fade(ps, w); - animatable_set_target(&w->blur_opacity, 0, duration); + animatable_set_target(&w->blur_opacity, 0, duration, NULL, NULL); #ifdef CONFIG_DBUS // Send D-Bus signal @@ -2527,7 +2528,7 @@ void map_win_start(session_t *ps, struct managed_win *w) { // iff `state` is MAPPED w->state = WSTATE_MAPPING; auto duration = win_start_fade(ps, w); - animatable_set_target(&w->blur_opacity, 1, duration); + animatable_set_target(&w->blur_opacity, 1, duration, NULL, NULL); log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id, animatable_get(&w->opacity), w->opacity.target); @@ -2572,7 +2573,8 @@ void win_update_opacity_target(session_t *ps, struct managed_win *w) { } else if (w->state == WSTATE_MAPPING) { // Opacity target changed while fading in, keep the blur_opacity // in lock step with the opacity - animatable_set_target(&w->blur_opacity, w->blur_opacity.target, duration); + animatable_set_target(&w->blur_opacity, w->blur_opacity.target, duration, + NULL, NULL); log_debug("Opacity changed while fading in"); } else if (w->state == WSTATE_FADING) { // Opacity target changed while FADING.