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}