Skip to main content

miracle_plugin/
animation.rs

1use crate::bindings;
2use crate::core::{Rect, mat4_from_f32_array, mat4_to_f32_array};
3use glam::Mat4;
4
5#[doc(hidden)]
6#[repr(C)]
7pub struct RawCustomAnimationData {
8    pub animation_id: u32,
9    pub dt: f32,
10    pub elapsed_seconds: f32,
11}
12
13/// Data passed to animation hook methods describing the current frame.
14pub struct AnimationFrameData {
15    /// Elapsed time since the animation started, in seconds.
16    pub runtime_seconds: f32,
17    /// The starting rectangle of the window (position and size at animation start).
18    pub origin: Rect,
19    /// The target rectangle of the window (position and size at animation end).
20    pub destination: Rect,
21    /// The starting opacity of the window.
22    pub opacity_start: f32,
23    /// The target opacity of the window.
24    pub opacity_end: f32,
25    /// The frame that the configured built-in animation (e.g. `slide`) produces at
26    /// [`Self::runtime_seconds`].
27    ///
28    /// Return this unchanged from an animation hook to delegate the frame to the
29    /// built-in animation explicitly, or modify it first to build on top of it. It
30    /// also tells a plugin where the built-in animation is placing the window this
31    /// frame. `None` when the compositor does not provide it.
32    pub builtin: Option<AnimationFrameResult>,
33}
34
35impl From<bindings::miracle_plugin_animation_frame_data_t> for AnimationFrameData {
36    fn from(value: bindings::miracle_plugin_animation_frame_data_t) -> Self {
37        Self {
38            runtime_seconds: value.runtime_seconds,
39            origin: Rect::from_array(value.origin),
40            destination: Rect::from_array(value.destination),
41            opacity_start: value.opacity_start,
42            opacity_end: value.opacity_end,
43            builtin: if value.has_builtin != 0 {
44                Some(value.builtin.into())
45            } else {
46                None
47            },
48        }
49    }
50}
51
52/// Returned from animation hooks to describe the frame's visual state.
53#[derive(Debug, Clone, Copy, PartialEq)]
54pub struct AnimationFrameResult {
55    /// Set to `true` to signal that the animation is finished.
56    pub completed: bool,
57    /// Override the window's rectangle for this frame. `None` leaves it unchanged.
58    ///
59    /// When also setting `clip_area`, this should carry the window's **final target
60    /// size** so the client receives the correct configure event immediately.
61    pub area: Option<Rect>,
62    /// Override the window's transform matrix for this frame. `None` leaves it unchanged.
63    pub transform: Option<Mat4>,
64    /// Override the window's opacity for this frame. `None` leaves it unchanged.
65    pub opacity: Option<f32>,
66    /// Set the scissor-clip rectangle for this frame independently from `area`.
67    ///
68    /// When set, this rectangle is used as the compositor scissor region rather
69    /// than `area`, allowing the window surface to be revealed gradually while
70    /// `area` carries the final target geometry for the Wayland configure event.
71    /// This avoids blank space during resize animations.
72    pub clip_area: Option<Rect>,
73}
74
75impl From<AnimationFrameResult> for bindings::miracle_plugin_animation_frame_result_t {
76    fn from(value: AnimationFrameResult) -> Self {
77        Self {
78            completed: value.completed as i32,
79            has_area: value.area.is_some() as i32,
80            area: value.area.map(|r| r.to_array()).unwrap_or_default(),
81            has_transform: value.transform.is_some() as i32,
82            transform: value.transform.map(mat4_to_f32_array).unwrap_or_default(),
83            has_opacity: value.opacity.is_some() as i32,
84            opacity: value.opacity.unwrap_or_default(),
85            has_clip_area: value.clip_area.is_some() as i32,
86            clip_area: value.clip_area.map(|r| r.to_array()).unwrap_or_default(),
87        }
88    }
89}
90
91impl From<bindings::miracle_plugin_animation_frame_result_t> for AnimationFrameResult {
92    fn from(value: bindings::miracle_plugin_animation_frame_result_t) -> Self {
93        Self {
94            completed: value.completed != 0,
95            area: if value.has_area != 0 {
96                Some(Rect::from_array(value.area))
97            } else {
98                None
99            },
100            transform: if value.has_transform != 0 {
101                Some(mat4_from_f32_array(value.transform))
102            } else {
103                None
104            },
105            opacity: if value.has_opacity != 0 {
106                Some(value.opacity)
107            } else {
108                None
109            },
110            clip_area: if value.has_clip_area != 0 {
111                Some(Rect::from_array(value.clip_area))
112            } else {
113                None
114            },
115        }
116    }
117}