miracle_plugin/
output.rs

1use super::bindings;
2use super::core::{Point, Size};
3use super::host::*;
4use super::workspace::*;
5
6/// Represents a physical or logical display.
7///
8/// Each output hosts one or more [`crate::workspace::Workspace`]s. Use [`Output::workspaces`]
9/// to get all workspaces on this output, or [`Output::workspace`] to retrieve one by index.
10#[derive(Debug, Clone)]
11pub struct Output {
12    /// The position of the output.
13    pub position: Point,
14    /// The size of the output.
15    pub size: Size,
16    /// The name of the output.
17    pub name: String,
18    /// Whether this is the primary output.
19    pub is_primary: bool,
20    /// The number of workspaces on this output.
21    pub num_workspaces: u32,
22    /// Internal pointer for C interop.
23    internal: u64,
24}
25
26impl Output {
27    /// Returns the opaque internal ID used to refer to this output across API calls.
28    pub fn id(&self) -> u64 {
29        self.internal
30    }
31
32    #[doc(hidden)]
33    pub fn from_c_with_name(value: &bindings::miracle_output_t, name: String) -> Self {
34        Self {
35            position: value.position.into(),
36            size: value.size.into(),
37            name,
38            is_primary: value.is_primary != 0,
39            num_workspaces: value.num_workspaces,
40            internal: value.internal,
41        }
42    }
43
44    /// Returns all workspaces hosted by this output.
45    pub fn workspaces(&self) -> Vec<Workspace> {
46        (0..self.num_workspaces)
47            .filter_map(|i| self.workspace(i))
48            .collect()
49    }
50
51    /// Returns the workspace at `index`, or `None` if out of bounds.
52    pub fn workspace(&self, index: u32) -> Option<Workspace> {
53        if index >= self.num_workspaces {
54            return None;
55        }
56
57        const NAME_BUF_LEN: usize = 256;
58        let mut workspace = std::mem::MaybeUninit::<crate::bindings::miracle_workspace_t>::uninit();
59        let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
60
61        unsafe {
62            let result = miracle_output_get_workspace(
63                self.internal as i64,
64                index,
65                workspace.as_mut_ptr() as i32,
66                name_buf.as_mut_ptr() as i32,
67                NAME_BUF_LEN as i32,
68            );
69
70            if result != 0 {
71                return None;
72            }
73
74            let workspace = workspace.assume_init();
75
76            // Find the null terminator to get the actual string length
77            let name_len = name_buf
78                .iter()
79                .position(|&c| c == 0)
80                .unwrap_or(NAME_BUF_LEN);
81            let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
82
83            Some(Workspace::from_c_with_name(&workspace, name))
84        }
85    }
86}