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}
26
27impl From<bindings::miracle_plugin_animation_frame_data_t> for AnimationFrameData {
28    fn from(value: bindings::miracle_plugin_animation_frame_data_t) -> Self {
29        Self {
30            runtime_seconds: value.runtime_seconds,
31            origin: Rect::from_array(value.origin),
32            destination: Rect::from_array(value.destination),
33            opacity_start: value.opacity_start,
34            opacity_end: value.opacity_end,
35        }
36    }
37}
38
39/// Returned from animation hooks to describe the frame's visual state.
40pub struct AnimationFrameResult {
41    /// Set to `true` to signal that the animation is finished.
42    pub completed: bool,
43    /// Override the window's rectangle for this frame. `None` leaves it unchanged.
44    ///
45    /// When also setting `clip_area`, this should carry the window's **final target
46    /// size** so the client receives the correct configure event immediately.
47    pub area: Option<Rect>,
48    /// Override the window's transform matrix for this frame. `None` leaves it unchanged.
49    pub transform: Option<Mat4>,
50    /// Override the window's opacity for this frame. `None` leaves it unchanged.
51    pub opacity: Option<f32>,
52    /// Set the scissor-clip rectangle for this frame independently from `area`.
53    ///
54    /// When set, this rectangle is used as the compositor scissor region rather
55    /// than `area`, allowing the window surface to be revealed gradually while
56    /// `area` carries the final target geometry for the Wayland configure event.
57    /// This avoids blank space during resize animations.
58    pub clip_area: Option<Rect>,
59}
60
61impl From<AnimationFrameResult> for bindings::miracle_plugin_animation_frame_result_t {
62    fn from(value: AnimationFrameResult) -> Self {
63        Self {
64            completed: value.completed as i32,
65            has_area: value.area.is_some() as i32,
66            area: value.area.map(|r| r.to_array()).unwrap_or_default(),
67            has_transform: value.transform.is_some() as i32,
68            transform: value.transform.map(mat4_to_f32_array).unwrap_or_default(),
69            has_opacity: value.opacity.is_some() as i32,
70            opacity: value.opacity.unwrap_or_default(),
71            has_clip_area: value.clip_area.is_some() as i32,
72            clip_area: value.clip_area.map(|r| r.to_array()).unwrap_or_default(),
73        }
74    }
75}
76
77impl From<bindings::miracle_plugin_animation_frame_result_t> for AnimationFrameResult {
78    fn from(value: bindings::miracle_plugin_animation_frame_result_t) -> Self {
79        Self {
80            completed: value.completed != 0,
81            area: if value.has_area != 0 {
82                Some(Rect::from_array(value.area))
83            } else {
84                None
85            },
86            transform: if value.has_transform != 0 {
87                Some(mat4_from_f32_array(value.transform))
88            } else {
89                None
90            },
91            opacity: if value.has_opacity != 0 {
92                Some(value.opacity)
93            } else {
94                None
95            },
96            clip_area: if value.has_clip_area != 0 {
97                Some(Rect::from_array(value.clip_area))
98            } else {
99                None
100            },
101        }
102    }
103}