Skip to main content

truce_core/
editor.rs

1use std::sync::Arc;
2
3/// A raw pointer wrapper that is `Send + Sync`.
4///
5/// Used to capture `*const Params` in EditorContext closures without
6/// the `ptr as usize` hack. Safe because `Params` fields use atomic
7/// storage — concurrent reads from the GUI thread while the audio
8/// thread writes are safe by design.
9///
10/// The pointed-to data must outlive the `SendPtr`. In the plugin
11/// context, the plugin instance (which owns the params) always
12/// outlives the editor.
13pub struct SendPtr<T>(*const T);
14
15impl<T> SendPtr<T> {
16    /// Wrap a raw pointer.
17    pub fn new(ptr: *const T) -> Self {
18        Self(ptr)
19    }
20
21    /// Dereference the pointer.
22    ///
23    /// # Safety
24    /// The pointed-to data must still be alive.
25    pub unsafe fn get(&self) -> &T {
26        &*self.0
27    }
28
29    /// Get the raw pointer.
30    pub fn as_ptr(&self) -> *const T {
31        self.0
32    }
33}
34
35impl<T> Clone for SendPtr<T> {
36    fn clone(&self) -> Self {
37        Self(self.0)
38    }
39}
40
41impl<T> Copy for SendPtr<T> {}
42
43// SAFETY: The pointed-to data is accessed only from the GUI thread
44// (EditorContext closures run on the main/GUI thread). The data is
45// pinned (Box::into_raw'd plugin instance) and outlives the editor.
46// For Params types, concurrent reads are safe because they use
47// AtomicF64. For Plugin types, access is single-threaded (GUI only).
48unsafe impl<T> Send for SendPtr<T> {}
49unsafe impl<T> Sync for SendPtr<T> {}
50
51/// Raw platform window handle for GUI parenting.
52#[derive(Clone, Copy, Debug)]
53pub enum RawWindowHandle {
54    AppKit(*mut std::ffi::c_void), // NSView*
55    Win32(*mut std::ffi::c_void),  // HWND
56    X11(u64),                      // X11 Window ID
57}
58
59/// Plugin GUI editor.
60pub trait Editor: Send {
61    /// Initial window size in logical points.
62    ///
63    /// On a 2x Retina display, `(400, 300)` produces an 800x600 pixel window.
64    /// On a 1x display, it produces a 400x300 pixel window.
65    fn size(&self) -> (u32, u32);
66
67    /// Create the GUI as a child of the host-provided parent window.
68    fn open(&mut self, parent: RawWindowHandle, context: EditorContext);
69
70    /// Destroy the GUI.
71    fn close(&mut self);
72
73    /// Called ~60fps on the host's UI thread for repaint/animation.
74    fn idle(&mut self) {}
75
76    /// Host requests a resize. Return true to accept.
77    fn set_size(&mut self, _width: u32, _height: u32) -> bool {
78        false
79    }
80
81    /// Whether the plugin supports resizing.
82    fn can_resize(&self) -> bool {
83        false
84    }
85
86    /// DPI scale factor changed.
87    fn set_scale_factor(&mut self, _factor: f64) {}
88}
89
90/// Context passed to Editor::open(). Provides communication
91/// with the host and parameter store.
92///
93/// All fields are `Arc`-wrapped, so cloning is cheap (reference count bump).
94#[derive(Clone)]
95pub struct EditorContext {
96    pub begin_edit: Arc<dyn Fn(u32) + Send + Sync>,
97    pub set_param: Arc<dyn Fn(u32, f64) + Send + Sync>,
98    pub end_edit: Arc<dyn Fn(u32) + Send + Sync>,
99    pub request_resize: Arc<dyn Fn(u32, u32) -> bool + Send + Sync>,
100    /// Read a parameter's normalized value from the plugin (for host→GUI sync).
101    pub get_param: Arc<dyn Fn(u32) -> f64 + Send + Sync>,
102    /// Read a parameter's plain value from the plugin.
103    pub get_param_plain: Arc<dyn Fn(u32) -> f64 + Send + Sync>,
104    /// Format a parameter's current value as a display string.
105    pub format_param: Arc<dyn Fn(u32) -> String + Send + Sync>,
106    /// Read a meter value (0.0–1.0) by meter ID. Used for level meters.
107    /// Returns 0.0 if the meter ID doesn't exist.
108    pub get_meter: Arc<dyn Fn(u32) -> f32 + Send + Sync>,
109}