#include #include "sway/animation_manager.h" #include "sway/config.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/node.h" #include "sway/tree/root.h" struct animation_manager { float tick_time; float progress_delta; struct wl_event_source *tick; struct wl_list animations; } animation_manager; struct animation init_animation(void *data) { return (struct animation){ .data = data, .progress = 0.0f, .multiplier = 0.0f, .initialized = false, .update = NULL, .complete = NULL, }; } static float ease_out_cubic(float p) { return pow(p - 1, 3) + 1; } static int animation_timer() { struct animation *animation, *tmp; wl_list_for_each_reverse_safe(animation, tmp, &animation_manager.animations, link) { animation->progress = MIN(animation->progress + animation_manager.progress_delta, 1.0f); animation->multiplier = ease_out_cubic(animation->progress); if (animation->update) { animation->update(animation->data); } if (animation->progress == 1.0f) { finish_animation(animation); if (animation->complete) { animation->complete(animation->data); } } } if (!wl_list_empty(&animation_manager.animations)) { wl_event_source_timer_update(animation_manager.tick, animation_manager.tick_time); } return 0; } void add_animation(struct animation *animation, void (*update_callback)(void *), void (*complete_callback)(void *)) { if (!config->animation_duration_ms) { if (complete_callback) { complete_callback(animation->data); } return; } // remove previous instances of this animation if (animation->initialized) { wl_list_remove(&animation->link); } animation->progress = 0.0f; animation->multiplier = 0.0f; animation->initialized = true; animation->update = update_callback; animation->complete = complete_callback; wl_list_insert(&animation_manager.animations, &animation->link); } void finish_animation(struct animation *animation) { if (animation->initialized) { wl_list_remove(&animation->link); animation->initialized = false; } animation->progress = 1.0f; animation->multiplier = 1.0f; } void start_animations() { if (!config->animation_duration_ms) { return; } assert(animation_manager.tick); wl_event_source_timer_update(animation_manager.tick, 1); } static float get_fastest_output_refresh_ms() { float fastest_output_refresh_ms = 16.6667; // fallback to 60 Hz for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; if (output->refresh_nsec > 0) { float output_refresh_ms = output->refresh_nsec / 1000000.0; fastest_output_refresh_ms = MIN(fastest_output_refresh_ms, output_refresh_ms); } } return fastest_output_refresh_ms; } void refresh_animation_manager_timing() { if (!config || !root) { return; } animation_manager.tick_time = get_fastest_output_refresh_ms(); animation_manager.progress_delta = animation_manager.tick_time / config->animation_duration_ms; } void animation_manager_init(struct sway_server *server) { animation_manager.tick = wl_event_loop_add_timer(server->wl_event_loop, animation_timer, NULL); wl_list_init(&animation_manager.animations); refresh_animation_manager_timing(); } static float lerp(float a, float b, float t) { return a * (1.0 - t) + b * t; } float get_animated_value(float from, float to, const struct animation *animation) { if (!config->animation_duration_ms || !animation->initialized) { return to; } return lerp(from, to, animation->multiplier); }