summaryrefslogtreecommitdiff
path: root/Userland/Services
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-06-27 16:47:52 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-27 19:38:11 +0200
commit75f870a93f7bbed7b877fa5dd11466eff7c94928 (patch)
tree76e379c3d6ca3bd786ddc0546df0f3dcae5e99bf /Userland/Services
parent1f33c517df4a7b3a9d53b15d602d1d9f59852277 (diff)
downloadserenity-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.cpp53
-rw-r--r--Userland/Services/WindowServer/Animation.h48
-rw-r--r--Userland/Services/WindowServer/CMakeLists.txt1
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp83
-rw-r--r--Userland/Services/WindowServer/Compositor.h9
-rw-r--r--Userland/Services/WindowServer/Window.cpp40
-rw-r--r--Userland/Services/WindowServer/Window.h7
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>;