miracle_plugin/
workspace.rs

1use super::bindings;
2use super::container::*;
3use super::core::Rectangle;
4use super::host::*;
5use super::output::*;
6
7/// Represents one workspace on an output.
8///
9/// Each workspace hosts a tree of [`crate::container::Container`]s. Use [`Workspace::trees`]
10/// to get the top-level containers, and [`Workspace::output`] to find the display this
11/// workspace is hosted on.
12#[derive(Debug, Clone)]
13pub struct Workspace {
14    /// The workspace number (if set).
15    pub number: Option<u32>,
16    /// The workspace name (if set).
17    pub name: Option<String>,
18    /// The number of container trees in this workspace.
19    pub num_trees: u32,
20    /// The current area of the workspace.
21    pub rectangle: Rectangle,
22    internal: u64,
23}
24
25impl Workspace {
26    /// Returns the opaque internal ID used to refer to this workspace across API calls.
27    pub fn id(&self) -> u64 {
28        self.internal
29    }
30
31    #[doc(hidden)]
32    pub unsafe fn from_c_with_name(value: &bindings::miracle_workspace_t, name: String) -> Self {
33        Self {
34            number: if value.has_number != 0 {
35                Some(value.number)
36            } else {
37                None
38            },
39            name: if value.has_name != 0 {
40                Some(name)
41            } else {
42                None
43            },
44            num_trees: value.num_trees,
45            internal: value.internal,
46            rectangle: Rectangle {
47                x: value.position.x,
48                y: value.position.y,
49                width: value.size.w,
50                height: value.size.h,
51            },
52        }
53    }
54
55    /// Get the output that this workspace is on.
56    pub fn output(&self) -> Option<Output> {
57        const NAME_BUF_LEN: usize = 256;
58        let mut output = std::mem::MaybeUninit::<crate::bindings::miracle_output_t>::uninit();
59        let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
60
61        unsafe {
62            let result = miracle_workspace_get_output(
63                self.internal as i64,
64                output.as_mut_ptr() as i32,
65                name_buf.as_mut_ptr() as i32,
66                NAME_BUF_LEN as i32,
67            );
68
69            if result != 0 {
70                return None;
71            }
72
73            let output = output.assume_init();
74
75            // Find the null terminator to get the actual string length
76            let name_len = name_buf
77                .iter()
78                .position(|&c| c == 0)
79                .unwrap_or(NAME_BUF_LEN);
80            let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
81
82            Some(Output::from_c_with_name(&output, name))
83        }
84    }
85
86    /// Get a tree from the workspace by index.
87    ///
88    /// Returns `None` if the index is out of bounds.
89    pub fn tree_at(&self, index: u32) -> Option<Container> {
90        if index >= self.num_trees {
91            return None;
92        }
93
94        let mut container = std::mem::MaybeUninit::<crate::bindings::miracle_container_t>::uninit();
95        unsafe {
96            let result = miracle_workspace_get_tree(
97                self.internal as i64,
98                index,
99                container.as_mut_ptr() as i32,
100            );
101
102            if result != 0 {
103                return None;
104            }
105
106            let container = container.assume_init();
107            Some(Container::from(container))
108        }
109    }
110
111    /// Get all trees from the workspace.
112    pub fn trees(&self) -> Vec<Container> {
113        (0..self.num_trees)
114            .filter_map(|i| self.tree_at(i))
115            .collect()
116    }
117}