voicemeeter\interface/
macro_buttons.rs

1//! Functions and data types for macro buttons.
2//!
3//! # Functions
4//!
5//! * [`is_macrobutton_dirty`](VoicemeeterRemote::is_macrobutton_dirty)
6//! * [`get_macrobutton_state`](VoicemeeterRemote::get_macrobutton_state)
7//! * [`set_macrobutton_state`](VoicemeeterRemote::set_macrobutton_state)
8//! * [`get_macrobutton_trigger_state`](VoicemeeterRemote::get_macrobutton_trigger_state)
9//! * [`set_macrobutton_trigger_state`](VoicemeeterRemote::set_macrobutton_trigger_state)
10use crate::{bindings::VBVMR_MACROBUTTON_MODE, types::LogicalButton};
11
12use super::VoicemeeterRemote;
13
14/// Status of a macro button.
15#[repr(transparent)]
16pub struct MacroButtonStatus(pub bool);
17
18impl std::ops::Deref for MacroButtonStatus {
19    type Target = bool;
20
21    fn deref(&self) -> &Self::Target {
22        &self.0
23    }
24}
25
26impl std::fmt::Display for MacroButtonStatus {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        if self.0 {
29            f.write_str("🟢")
30        } else {
31            f.write_str("🔴")
32        }
33    }
34}
35
36impl VoicemeeterRemote {
37    // FIXME: Only call from one thread, limit it
38    /// Check if macro buttons have changed
39    ///
40    /// Call this function periodically to check if the macro buttons have changed, typically every 10ms.
41    ///
42    /// # Security
43    ///
44    /// This method must only be called from one thread.
45    pub fn is_macrobutton_dirty(&self) -> Result<bool, IsMacroButtonDirtyError> {
46        let res = unsafe { self.raw.VBVMR_MacroButton_IsDirty() };
47        match res {
48            0 => Ok(false),
49            s if s > 0 => Ok(true),
50            -1 => Err(IsMacroButtonDirtyError::CannotGetClient),
51            -2 => Err(IsMacroButtonDirtyError::NoServer),
52            s => Err(IsMacroButtonDirtyError::Other(s)),
53        }
54    }
55
56    /// Get a specific macro buttons status.
57    pub fn get_macrobutton_state(
58        &self,
59        button: impl Into<LogicalButton>,
60    ) -> Result<MacroButtonStatus, GetMacroButtonStatusError> {
61        let mut f = 0.0f32;
62        let button = button.into();
63        let res = unsafe { self.raw.VBVMR_MacroButton_GetStatus(button.0 .0, &mut f, 0) };
64        match res {
65            0 => Ok(MacroButtonStatus(f == 1.)),
66            -1 => Err(GetMacroButtonStatusError::CannotGetClient),
67            -2 => Err(GetMacroButtonStatusError::NoServer),
68            -3 => Err(GetMacroButtonStatusError::UnknownParameter(button)), /* FIXME: Lossless always (assuming vmr doesn't modify :) ), unsafe? */
69            -5 => Err(GetMacroButtonStatusError::StructureMismatch(button, 1)),
70            s => Err(GetMacroButtonStatusError::Other(s)),
71        }
72    }
73
74    /// Set a macro buttons state.
75    ///
76    /// use `displayed_state_only` to only set the displayed state of the macro button, but not trigger it's associated requests.
77    pub fn set_macrobutton_state(
78        &self,
79        button: impl Into<LogicalButton>,
80        state: bool,
81        displayed_state_only: bool,
82    ) -> Result<(), SetMacroButtonStatusError> {
83        let _f = 0.0f32;
84        let button = button.into();
85        let bitmode = if displayed_state_only {
86            VBVMR_MACROBUTTON_MODE::STATEONLY
87        } else {
88            VBVMR_MACROBUTTON_MODE::DEFAULT
89        };
90        let res = unsafe {
91            self.raw
92                .VBVMR_MacroButton_SetStatus(button.0 .0, (state as u32) as f32, bitmode.0)
93        };
94        match res {
95            0 => Ok(()),
96            -1 => Err(SetMacroButtonStatusError::CannotGetClient),
97            -2 => Err(SetMacroButtonStatusError::NoServer),
98            -3 => Err(SetMacroButtonStatusError::UnknownParameter(button)), /* FIXME: Lossless always (assuming vmr doesn't modify :) ), unsafe? */
99            -5 => Err(SetMacroButtonStatusError::StructureMismatch(button, 1)),
100            s => Err(SetMacroButtonStatusError::Other(s)),
101        }
102    }
103
104    /// Get the trigger state of the macrobutton.
105    pub fn get_macrobutton_trigger_state(
106        &self,
107        button: impl Into<LogicalButton>,
108    ) -> Result<MacroButtonStatus, GetMacroButtonStatusError> {
109        let mut f = 0.0f32;
110        let button = button.into();
111        let res = unsafe { self.raw.VBVMR_MacroButton_GetStatus(button.0 .0, &mut f, 3) };
112        match res {
113            0 => Ok(MacroButtonStatus(f == 1.)),
114            -1 => Err(GetMacroButtonStatusError::CannotGetClient),
115            -2 => Err(GetMacroButtonStatusError::NoServer),
116            -3 => Err(GetMacroButtonStatusError::UnknownParameter(button)), /* FIXME: Lossless always (assuming vmr doesn't modify :) ), unsafe? */
117            -5 => Err(GetMacroButtonStatusError::StructureMismatch(button, 3)),
118            s => Err(GetMacroButtonStatusError::Other(s)),
119        }
120    }
121
122    /// Set the trigger state of the macrobutton.
123    pub fn set_macrobutton_trigger_state(
124        &self,
125        button: impl Into<LogicalButton>,
126        state: bool,
127    ) -> Result<(), SetMacroButtonStatusError> {
128        let button = button.into();
129        let res = unsafe {
130            self.raw
131                .VBVMR_MacroButton_SetStatus(button.0 .0, (state as u32) as f32, 3)
132        };
133        match res {
134            0 => Ok(()),
135            -1 => Err(SetMacroButtonStatusError::CannotGetClient),
136            -2 => Err(SetMacroButtonStatusError::NoServer),
137            -3 => Err(SetMacroButtonStatusError::UnknownParameter(button)), /* FIXME: Lossless always (assuming vmr doesn't modify :) ), unsafe? */
138            -5 => Err(SetMacroButtonStatusError::StructureMismatch(button, 3)),
139            s => Err(SetMacroButtonStatusError::Other(s)),
140        }
141    }
142}
143
144/// Errors that can happen when querying macro button "dirty" flag.
145#[derive(Debug, thiserror::Error, Clone)]
146#[non_exhaustive]
147pub enum IsMacroButtonDirtyError {
148    // TODO: is this correct? docs say "error (unexpected)""
149    /// Cannot get client.
150    #[error("cannot get client (unexpected)")]
151    CannotGetClient,
152    /// No server.
153    #[error("no server")]
154    NoServer,
155    /// An unknown error code occured.
156    #[error("unexpected error occurred: error code {0}")]
157    Other(i32),
158}
159
160/// Errors that can happen when getting macrobutton status.
161#[derive(Debug, thiserror::Error, Clone)]
162#[non_exhaustive]
163pub enum GetMacroButtonStatusError {
164    // TODO: is this correct? docs say "error"
165    /// Cannot get client.
166    #[error("cannot get client (unexpected)")]
167    CannotGetClient,
168    /// No server.
169    #[error("no server")]
170    NoServer,
171    /// Unknown parameter/button.
172    #[error("unknown button: {}", 0.0)]
173    UnknownParameter(LogicalButton),
174    /// Structure mismatch.
175    #[error("tried to parse parameter {0:?} as a {1} but it is not")]
176    StructureMismatch(LogicalButton, i32),
177    /// An unknown error code occured.
178    #[error("unexpected error occurred: error code {0}")]
179    Other(i32),
180}
181
182/// Errors that can happen when setting macrobutton status.
183#[derive(Debug, thiserror::Error, Clone)]
184#[non_exhaustive]
185pub enum SetMacroButtonStatusError {
186    // TODO: is this correct? docs say "error"
187    /// Cannot get client.
188    #[error("cannot get client (unexpected)")]
189    CannotGetClient,
190    /// No server.
191    #[error("no server")]
192    NoServer,
193    /// Unknown parameter/button.
194    #[error("unknown button: {}", 0.0)]
195    UnknownParameter(LogicalButton),
196    /// Structure mismatch.
197    #[error("tried to parse parameter {0:?} as a {1} but it is not")]
198    StructureMismatch(LogicalButton, i32),
199    /// An unknown error code occured.
200    #[error("unexpected error occurred: error code {0}")]
201    Other(i32),
202}