Add C++20 RAII wrapper around wlroots 0.20 + scenefx

Wraps wlroots' C ABI behind owning types (Display, Backend, Compositor,
Output, XdgShell/Toplevel, Seat/Cursor/Keyboard, Scene/Node/Tree) with
wl_signal -> std::function callbacks. Links the scenefx effect engine and
ships a wlr::fx animation helper. Includes a runnable tinywl-style compositor
and a scene/blur demo.

License: AGPL-3.0-only.
This commit is contained in:
AstralZX
2026-08-29 09:12:28 +02:00
commit 89cbf733b3
20 changed files with 2008 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: AGPL-3.0-only
#pragma once
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <functional>
namespace wlr::fx {
enum class Easing {
Linear,
InQuad, OutQuad, InOutQuad,
InCubic, OutCubic, InOutCubic,
InElastic, OutElastic, InOutElastic,
};
inline float ease(Easing e, float t) {
t = std::clamp(t, 0.0f, 1.0f);
switch (e) {
case Easing::Linear: return t;
case Easing::InQuad: return t * t;
case Easing::OutQuad: return t * (2.0f - t);
case Easing::InCubic: return t * t * t;
case Easing::OutCubic: { float u = t - 1.0f; return u * u * u + 1.0f; }
case Easing::InOutQuad: return t < 0.5f ? 2.0f * t * t : -1.0f + (4.0f - 2.0f * t) * t;
case Easing::InOutCubic: return t < 0.5f ? 4.0f * t * t * t : (t - 1.0f) * (2.0f * t - 2.0f) * (2.0f * t - 2.0f) + 1.0f;
case Easing::InElastic: {
const float c4 = (2.0f * M_PI) / 3.0f;
return t <= 0.0f ? 0.0f : t >= 1.0f ? 1.0f
: -std::pow(2.0f, 10.0f * t - 10.0f) * std::sin((t * 10.0f - 10.75f) * c4);
}
case Easing::OutElastic: {
const float c4 = (2.0f * M_PI) / 3.0f;
return t <= 0.0f ? 0.0f : t >= 1.0f ? 1.0f
: std::pow(2.0f, -10.0f * t) * std::sin((t * 10.0f - 0.75f) * c4) + 1.0f;
}
case Easing::InOutElastic: {
const float c5 = (2.0f * M_PI) / 4.5f;
return t <= 0.0f ? 0.0f : t >= 1.0f ? 1.0f
: t < 0.5f
? -(std::pow(2.0f, 20.0f * t - 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f
: (std::pow(2.0f, -20.0f * t + 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f + 1.0f;
}
}
return t;
}
template <typename Clock = std::uint64_t>
class Animated {
public:
Animated() = default;
Animated(float value) : m_value(value), m_target(value), m_equal(true) {}
void set(float value) {
if (value == m_target && !m_equal) { jump_to(value); return; }
m_target = value;
if (m_duration == 0) { jump_to(value); return; }
m_startValue = m_value;
m_startTime = now();
m_elapsed = 0;
m_equal = false;
}
void jump_to(float value) {
m_value = value; m_target = value; m_equal = true; m_startTime = now();
}
void on_frame(Clock time) {
if (m_equal) return;
m_elapsed = time - m_startTime;
if (m_elapsed >= m_duration) { m_value = m_target; m_equal = true; return; }
const float t = ease(m_easing, static_cast<float>(m_elapsed) / static_cast<float>(m_duration));
m_value = m_startValue + (m_target - m_startValue) * t;
}
void set_duration(Clock d) { m_duration = d > 0 ? d : 1; }
void set_easing(Easing e) { m_easing = e; }
void set_clock_source(std::function<Clock()> src) { m_clock = std::move(src); }
float value() const { return m_value; }
float target() const { return m_target; }
bool animating() const { return !m_equal; }
Clock remaining() const { return m_equal ? 0 : m_duration - m_elapsed; }
private:
Clock now() { return m_clock ? m_clock() : Clock(0); }
float m_value = 0.0f;
float m_startValue = 0.0f;
float m_target = 0.0f;
Clock m_duration = 1;
Clock m_startTime = 0;
Clock m_elapsed = 0;
bool m_equal = true;
Easing m_easing = Easing::OutCubic;
std::function<Clock()> m_clock;
};
}
+52
View File
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: AGPL-3.0-only
#pragma once
#include <cstdint>
#include <type_traits>
#include "wlr/scenefx/anim.hpp"
#include "wlr/scene.hpp"
#include "wlr/node.hpp"
namespace wlr::fx {
class Effect {
public:
Animated<>& position_x() { return m_x; }
Animated<>& position_y() { return m_y; }
Animated<>& scale() { return m_scale; }
Animated<>& opacity() { return m_opacity; }
void on_frame(std::uint64_t time) {
m_x.on_frame(time);
m_y.on_frame(time);
m_scale.on_frame(time);
m_opacity.on_frame(time);
}
void apply_to(Node& node) {
node.set_position((int)m_x.value(), (int)m_y.value());
if (node.type() != WLR_SCENE_NODE_BUFFER)
return;
Buffer b = node.as<Buffer>();
b.set_opacity(m_opacity.value());
const float s = m_scale.value();
const int dw = b.raw()->dst_width;
const int dh = b.raw()->dst_height;
if (s != 1.0f && dw > 0 && dh > 0)
b.set_dest_size((int)(s * dw), (int)(s * dh));
}
bool animating() const {
return m_x.animating() || m_y.animating() ||
m_scale.animating() || m_opacity.animating();
}
private:
Animated<> m_x;
Animated<> m_y;
Animated<> m_scale{1.0f};
Animated<> m_opacity{1.0f};
};
}