diff options
author | Andreas Kling <kling@serenityos.org> | 2021-06-27 16:47:52 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-27 19:38:11 +0200 |
commit | 75f870a93f7bbed7b877fa5dd11466eff7c94928 (patch) | |
tree | 76e379c3d6ca3bd786ddc0546df0f3dcae5e99bf /Userland/Services | |
parent | 1f33c517df4a7b3a9d53b15d602d1d9f59852277 (diff) | |
download | serenity-75f870a93f7bbed7b877fa5dd11466eff7c94928.zip |
WindowServer: Add a more generic mechanism for animations
This patch adds the WindowServer::Animation class, which represents
a simple animation driven by the compositor.
An animation has a length (in milliseconds) and two hooks:
- on_update: called whenever the animation should render something.
- on_stop: called when the animation is finished and/or stopped.
This patch also ports the window minimization animation to this new
mechanism. :^)
Diffstat (limited to 'Userland/Services')
-rw-r--r-- | Userland/Services/WindowServer/Animation.cpp | 53 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Animation.h | 48 | ||||
-rw-r--r-- | Userland/Services/WindowServer/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Compositor.cpp | 83 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Compositor.h | 9 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.cpp | 40 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.h | 7 |
7 files changed, 171 insertions, 70 deletions
diff --git a/Userland/Services/WindowServer/Animation.cpp b/Userland/Services/WindowServer/Animation.cpp new file mode 100644 index 0000000000..bb35691b4d --- /dev/null +++ b/Userland/Services/WindowServer/Animation.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Animation.h" +#include "Compositor.h" +#include <AK/Badge.h> + +namespace WindowServer { + +Animation::Animation() +{ + Compositor::the().register_animation({}, *this); +} + +Animation::~Animation() +{ + Compositor::the().unregister_animation({}, *this); +} + +void Animation::set_length(int length_in_ms) +{ + m_length = length_in_ms; +} + +void Animation::start() +{ + m_running = true; + m_timer.start(); +} + +void Animation::stop() +{ + m_running = false; + if (on_stop) + on_stop(); +} + +void Animation::update(Badge<Compositor>, Gfx::Painter& painter, Screen& screen, Gfx::DisjointRectSet& flush_rects) +{ + int elapsed_ms = m_timer.elapsed(); + float progress = min((float)elapsed_ms / (float)m_length, 1.0f); + + if (on_update) + on_update(progress, painter, screen, flush_rects); + + if (progress >= 1.0f) + stop(); +} + +} diff --git a/Userland/Services/WindowServer/Animation.h b/Userland/Services/WindowServer/Animation.h new file mode 100644 index 0000000000..6bd0072b61 --- /dev/null +++ b/Userland/Services/WindowServer/Animation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Forward.h> +#include <AK/Function.h> +#include <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <LibCore/ElapsedTimer.h> +#include <LibGfx/Forward.h> + +namespace WindowServer { + +class Compositor; +class Screen; + +class Animation : public RefCounted<Animation> { +public: + static NonnullRefPtr<Animation> create() { return adopt_ref(*new Animation); } + + ~Animation(); + + bool is_running() const { return m_running; } + + void start(); + void stop(); + + void set_length(int length_in_ms); + int length() const { return m_length; } + + void update(Badge<Compositor>, Gfx::Painter&, Screen&, Gfx::DisjointRectSet& flush_rects); + + Function<void(float progress, Gfx::Painter&, Screen&, Gfx::DisjointRectSet& flush_rects)> on_update; + Function<void()> on_stop; + +private: + Animation(); + + Core::ElapsedTimer m_timer; + int m_length { 0 }; + bool m_running { false }; +}; + +} diff --git a/Userland/Services/WindowServer/CMakeLists.txt b/Userland/Services/WindowServer/CMakeLists.txt index 3dd7c0be51..b0fd2f652e 100644 --- a/Userland/Services/WindowServer/CMakeLists.txt +++ b/Userland/Services/WindowServer/CMakeLists.txt @@ -10,6 +10,7 @@ compile_ipc(WindowManagerServer.ipc WindowManagerServerEndpoint.h) compile_ipc(WindowManagerClient.ipc WindowManagerClientEndpoint.h) set(SOURCES + Animation.cpp AppletManager.cpp Button.cpp ClientConnection.cpp diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index af5b9dc3c7..554895eee6 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -1,10 +1,11 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #include "Compositor.h" +#include "Animation.h" #include "ClientConnection.h" #include "Event.h" #include "EventLoop.h" @@ -527,10 +528,9 @@ void Compositor::compose() m_invalidated_window = false; m_invalidated_cursor = false; - bool did_render_animation = false; Screen::for_each([&](auto& screen) { auto& screen_data = m_screen_data[screen.index()]; - did_render_animation |= render_animation_frame(screen, screen_data.m_flush_special_rects); + update_animations(screen, screen_data.m_flush_special_rects); return IterationDecision::Continue; }); @@ -546,9 +546,6 @@ void Compositor::compose() flush(screen); return IterationDecision::Continue; }); - - if (did_render_animation) - step_animations(); } void Compositor::flush(Screen& screen) @@ -709,60 +706,6 @@ void Compositor::ScreenData::flip_buffers(Screen& screen) m_buffers_are_flipped = !m_buffers_are_flipped; } -static const int minimize_animation_steps = 10; - -bool Compositor::render_animation_frame(Screen& screen, Gfx::DisjointRectSet& flush_rects) -{ - bool did_render_any = false; - auto& painter = *m_screen_data[screen.index()].m_back_painter; - Gfx::PainterStateSaver saver(painter); - painter.set_draw_op(Gfx::Painter::DrawOp::Invert); - - WindowManager::the().window_stack().for_each_window([&](Window& window) { - if (window.in_minimize_animation()) { - int animation_index = window.minimize_animation_index(); - - auto from_rect = window.is_minimized() ? window.frame().rect() : window.taskbar_rect(); - auto to_rect = window.is_minimized() ? window.taskbar_rect() : window.frame().rect(); - - float x_delta_per_step = (float)(from_rect.x() - to_rect.x()) / minimize_animation_steps; - float y_delta_per_step = (float)(from_rect.y() - to_rect.y()) / minimize_animation_steps; - float width_delta_per_step = (float)(from_rect.width() - to_rect.width()) / minimize_animation_steps; - float height_delta_per_step = (float)(from_rect.height() - to_rect.height()) / minimize_animation_steps; - - Gfx::IntRect rect { - from_rect.x() - (int)(x_delta_per_step * animation_index), - from_rect.y() - (int)(y_delta_per_step * animation_index), - from_rect.width() - (int)(width_delta_per_step * animation_index), - from_rect.height() - (int)(height_delta_per_step * animation_index) - }; - - dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {} on screen #{}", from_rect, to_rect, animation_index, rect, screen.index()); - - painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted - flush_rects.add(rect.intersected(screen.rect())); - invalidate_screen(rect); - - did_render_any = true; - } - return IterationDecision::Continue; - }); - - return did_render_any; -} - -void Compositor::step_animations() -{ - WindowManager::the().window_stack().for_each_window([&](Window& window) { - if (window.in_minimize_animation()) { - window.step_minimize_animation(); - if (window.minimize_animation_index() >= minimize_animation_steps) - window.end_minimize_animation(); - } - return IterationDecision::Continue; - }); -} - void Compositor::screen_resolution_changed() { // Screens may be gone now, invalidate any references to them @@ -1260,4 +1203,24 @@ void Compositor::recompute_occlusions() }); } +void Compositor::register_animation(Badge<Animation>, Animation& animation) +{ + auto result = m_animations.set(&animation); + VERIFY(result == AK::HashSetResult::InsertedNewEntry); +} + +void Compositor::unregister_animation(Badge<Animation>, Animation& animation) +{ + bool was_removed = m_animations.remove(&animation); + VERIFY(was_removed); +} + +void Compositor::update_animations(Screen& screen, Gfx::DisjointRectSet& flush_rects) +{ + auto& painter = *m_screen_data[screen.index()].m_back_painter; + for (RefPtr<Animation> animation : m_animations) { + animation->update({}, painter, screen, flush_rects); + } +} + } diff --git a/Userland/Services/WindowServer/Compositor.h b/Userland/Services/WindowServer/Compositor.h index ef1d76d6c3..75a046f481 100644 --- a/Userland/Services/WindowServer/Compositor.h +++ b/Userland/Services/WindowServer/Compositor.h @@ -16,6 +16,7 @@ namespace WindowServer { +class Animation; class ClientConnection; class Cursor; class MultiScaleBitmaps; @@ -95,11 +96,12 @@ public: const Gfx::Bitmap* cursor_bitmap_for_screenshot(Badge<ClientConnection>, Screen&) const; const Gfx::Bitmap& front_bitmap_for_screenshot(Badge<ClientConnection>, Screen&) const; + void register_animation(Badge<Animation>, Animation&); + void unregister_animation(Badge<Animation>, Animation&); + private: Compositor(); void init_bitmaps(); - bool render_animation_frame(Screen&, Gfx::DisjointRectSet&); - void step_animations(); void invalidate_current_screen_number_rects(); void overlays_theme_changed(); @@ -114,6 +116,7 @@ private: bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&); void change_cursor(const Cursor*); void flush(Screen&); + void update_animations(Screen&, Gfx::DisjointRectSet& flush_rects); RefPtr<Core::Timer> m_compose_timer; RefPtr<Core::Timer> m_immediate_compose_timer; @@ -195,6 +198,8 @@ private: size_t m_show_screen_number_count { 0 }; Optional<Gfx::Color> m_custom_background_color; + + HashTable<Animation*> m_animations; }; } diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index 3e5099a2dd..98a33c34e9 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -1,10 +1,11 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #include "Window.h" +#include "Animation.h" #include "AppletManager.h" #include "ClientConnection.h" #include "Compositor.h" @@ -306,6 +307,21 @@ void Window::set_taskbar_rect(const Gfx::IntRect& rect) m_have_taskbar_rect = !m_taskbar_rect.is_empty(); } +static Gfx::IntRect interpolate_rect(Gfx::IntRect const& from_rect, Gfx::IntRect const& to_rect, float progress) +{ + auto dx = to_rect.x() - from_rect.x(); + auto dy = to_rect.y() - from_rect.y(); + auto dw = to_rect.width() - from_rect.width(); + auto dh = to_rect.height() - from_rect.height(); + + return Gfx::IntRect { + from_rect.x() + ((float)dx * progress), + from_rect.y() + ((float)dy * progress), + from_rect.width() + ((float)dw * progress), + from_rect.height() + ((float)dh * progress), + }; +} + void Window::start_minimize_animation() { if (!m_have_taskbar_rect) { @@ -327,7 +343,26 @@ void Window::start_minimize_animation() return IterationDecision::Continue; }); } - m_minimize_animation_step = 0; + + m_animation = Animation::create(); + m_animation->set_length(150); + m_animation->on_update = [this](float progress, Gfx::Painter& painter, Screen& screen, Gfx::DisjointRectSet& flush_rects) { + Gfx::PainterStateSaver saver(painter); + painter.set_draw_op(Gfx::Painter::DrawOp::Invert); + + auto from_rect = is_minimized() ? frame().rect() : taskbar_rect(); + auto to_rect = is_minimized() ? taskbar_rect() : frame().rect(); + + auto rect = interpolate_rect(from_rect, to_rect, progress); + + painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted + flush_rects.add(rect.intersected(screen.rect())); + Compositor::the().invalidate_screen(rect); + }; + m_animation->on_stop = [this] { + m_animation = nullptr; + }; + m_animation->start(); } void Window::set_opacity(float opacity) @@ -1071,5 +1106,4 @@ String Window::computed_title() const return String::formatted("{} (Not responding)", title); return title; } - } diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index 129f7d7281..2eaa624985 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -20,6 +20,7 @@ namespace WindowServer { +class Animation; class ClientConnection; class Cursor; class KeyEvent; @@ -256,11 +257,7 @@ public: Gfx::DisjointRectSet take_pending_paint_rects() { return move(m_pending_paint_rects); } bool has_taskbar_rect() const { return m_have_taskbar_rect; }; - bool in_minimize_animation() const { return m_minimize_animation_step != -1; } - int minimize_animation_index() const { return m_minimize_animation_step; } - void step_minimize_animation() { m_minimize_animation_step += 1; } void start_minimize_animation(); - void end_minimize_animation() { m_minimize_animation_step = -1; } Gfx::IntRect tiled_rect(Screen*, WindowTileType) const; void recalculate_rect(); @@ -408,10 +405,10 @@ private: MenuItem* m_window_menu_move_item { nullptr }; MenuItem* m_window_menu_close_item { nullptr }; MenuItem* m_window_menu_menubar_visibility_item { nullptr }; - int m_minimize_animation_step { -1 }; Optional<int> m_progress; bool m_should_show_menubar { true }; WindowStack* m_outer_stack { nullptr }; + RefPtr<Animation> m_animation; public: using List = IntrusiveList<Window, RawPtr<Window>, &Window::m_list_node>; |