diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 11dd0ffd..52fc3ff4 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -6,6 +6,11 @@ new and what's changed, this document lists all breaking changes in reverse chronological order. If a new feature did not require any changes to existing code then it will not be listed here. +## [2022-10-21] + +- The `Editor` trait and the `ParentWindowHandle` struct have been moved from + `nih_plug::plugin` to a new `nih_plug::editor` module. + ## [2022-10-20] - Similar to the below change, `Plugin` also no longer requires `Sync`. diff --git a/src/editor.rs b/src/editor.rs new file mode 100644 index 00000000..31ef2e9a --- /dev/null +++ b/src/editor.rs @@ -0,0 +1,81 @@ +//! Traits for working with plugin editors. + +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use std::any::Any; +use std::sync::Arc; + +use crate::context::GuiContext; + +/// An editor for a [`Plugin`][crate::prelude::Plugin]. +pub trait Editor: Send { + /// Create an instance of the plugin's editor and embed it in the parent window. As explained in + /// [`Plugin::editor()`][crate::prelude::Plugin::editor()], you can then read the parameter + /// values directly from your [`Params`][crate::prelude::Params] object, and modifying the + /// values can be done using the functions on the [`ParamSetter`][crate::prelude::ParamSetter]. + /// When you change a parameter value that way it will be broadcasted to the host and also + /// updated in your [`Params`][crate::prelude::Params] struct. + /// + /// This function should return a handle to the editor, which will be dropped when the editor + /// gets closed. Implement the [`Drop`] trait on the returned handle if you need to explicitly + /// handle the editor's closing behavior. + /// + /// If [`set_scale_factor()`][Self::set_scale_factor()] has been called, then any created + /// windows should have their sizes multiplied by that factor. + /// + /// The wrapper guarantees that a previous handle has been dropped before this function is + /// called again. + // + // TODO: Think of how this would work with the event loop. On Linux the wrapper must provide a + // timer using VST3's `IRunLoop` interface, but on Window and macOS the window would + // normally register its own timer. Right now we just ignore this because it would + // otherwise be basically impossible to have this still be GUI-framework agnostic. Any + // callback that deos involve actual GUI operations will still be spooled to the IRunLoop + // instance. + // TODO: This function should return an `Option` instead. Right now window opening failures are + // always fatal. This would need to be fixed in baseview first. + fn spawn( + &self, + parent: ParentWindowHandle, + context: Arc, + ) -> Box; + + /// Returns the (current) size of the editor in pixels as a `(width, height)` pair. This size + /// must be reported in _logical pixels_, i.e. the size before being multiplied by the DPI + /// scaling factor to get the actual physical screen pixels. + fn size(&self) -> (u32, u32); + + /// Set the DPI scaling factor, if supported. The plugin APIs don't make any guarantees on when + /// this is called, but for now just assume it will be the first function that gets called + /// before creating the editor. If this is set, then any windows created by this editor should + /// have their sizes multiplied by this scaling factor on Windows and Linux. + /// + /// Right now this is never called on macOS since DPI scaling is built into the operating system + /// there. + fn set_scale_factor(&self, factor: f32) -> bool; + + /// A callback that will be called whenever the parameter values changed while the editor is + /// open. You don't need to do anything with this, but this can be used to force a redraw when + /// the host sends a new value for a parameter or when a parameter change sent to the host gets + /// processed. + /// + /// This function will be called from the **audio thread**. It must thus be lock-free and may + /// not allocate. + fn param_values_changed(&self); + + // TODO: Reconsider adding a tick function here for the Linux `IRunLoop`. To keep this platform + // and API agnostic, add a way to ask the GuiContext if the wrapper already provides a + // tick function. If it does not, then the Editor implementation must handle this by + // itself. This would also need an associated `PREFERRED_FRAME_RATE` constant. + // TODO: Host->Plugin resizing +} + +/// A raw window handle for platform and GUI framework agnostic editors. +pub struct ParentWindowHandle { + pub handle: RawWindowHandle, +} + +unsafe impl HasRawWindowHandle for ParentWindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + self.handle + } +} diff --git a/src/lib.rs b/src/lib.rs index 78a27708..dd247c65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,7 @@ pub mod util; pub mod buffer; pub mod context; +pub mod editor; mod event_loop; pub mod midi; pub mod params; diff --git a/src/plugin.rs b/src/plugin.rs index 750853b3..6f30c28c 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,11 +1,10 @@ //! Traits and structs describing plugins and editors. -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use std::any::Any; use std::sync::Arc; use crate::buffer::Buffer; -use crate::context::{GuiContext, InitContext, ProcessContext}; +use crate::context::{InitContext, ProcessContext}; +use crate::editor::Editor; use crate::midi::MidiConfig; use crate::params::Params; use crate::wrapper::clap::features::ClapFeature; @@ -252,79 +251,6 @@ const fn swap_vst3_uid_byte_order(mut uid: [u8; 16]) -> [u8; 16] { uid } -/// An editor for a [`Plugin`]. -pub trait Editor: Send { - /// Create an instance of the plugin's editor and embed it in the parent window. As explained in - /// [`Plugin::editor()`], you can then read the parameter values directly from your [`Params`] - /// object, and modifying the values can be done using the functions on the - /// [`ParamSetter`][crate::prelude::ParamSetter]. When you change a parameter value that way it will be - /// broadcasted to the host and also updated in your [`Params`] struct. - /// - /// This function should return a handle to the editor, which will be dropped when the editor - /// gets closed. Implement the [`Drop`] trait on the returned handle if you need to explicitly - /// handle the editor's closing behavior. - /// - /// If [`set_scale_factor()`][Self::set_scale_factor()] has been called, then any created - /// windows should have their sizes multiplied by that factor. - /// - /// The wrapper guarantees that a previous handle has been dropped before this function is - /// called again. - // - // TODO: Think of how this would work with the event loop. On Linux the wrapper must provide a - // timer using VST3's `IRunLoop` interface, but on Window and macOS the window would - // normally register its own timer. Right now we just ignore this because it would - // otherwise be basically impossible to have this still be GUI-framework agnostic. Any - // callback that deos involve actual GUI operations will still be spooled to the IRunLoop - // instance. - // TODO: This function should return an `Option` instead. Right now window opening failures are - // always fatal. This would need to be fixed in baseview first. - fn spawn( - &self, - parent: ParentWindowHandle, - context: Arc, - ) -> Box; - - /// Returns the (current) size of the editor in pixels as a `(width, height)` pair. This size - /// must be reported in _logical pixels_, i.e. the size before being multiplied by the DPI - /// scaling factor to get the actual physical screen pixels. - fn size(&self) -> (u32, u32); - - /// Set the DPI scaling factor, if supported. The plugin APIs don't make any guarantees on when - /// this is called, but for now just assume it will be the first function that gets called - /// before creating the editor. If this is set, then any windows created by this editor should - /// have their sizes multiplied by this scaling factor on Windows and Linux. - /// - /// Right now this is never called on macOS since DPI scaling is built into the operating system - /// there. - fn set_scale_factor(&self, factor: f32) -> bool; - - /// A callback that will be called whenever the parameter values changed while the editor is - /// open. You don't need to do anything with this, but this can be used to force a redraw when - /// the host sends a new value for a parameter or when a parameter change sent to the host gets - /// processed. - /// - /// This function will be called from the **audio thread**. It must thus be lock-free and may - /// not allocate. - fn param_values_changed(&self); - - // TODO: Reconsider adding a tick function here for the Linux `IRunLoop`. To keep this platform - // and API agnostic, add a way to ask the GuiContext if the wrapper already provides a - // tick function. If it does not, then the Editor implementation must handle this by - // itself. This would also need an associated `PREFERRED_FRAME_RATE` constant. - // TODO: Host->Plugin resizing -} - -/// A raw window handle for platform and GUI framework agnostic editors. -pub struct ParentWindowHandle { - pub handle: RawWindowHandle, -} - -unsafe impl HasRawWindowHandle for ParentWindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - self.handle - } -} - /// The plugin's IO configuration. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BusConfig { diff --git a/src/prelude.rs b/src/prelude.rs index f2acc6f5..7d5a8953 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,6 +13,7 @@ pub use crate::util; pub use crate::buffer::Buffer; pub use crate::context::{GuiContext, InitContext, ParamSetter, PluginApi, ProcessContext}; // This also includes the derive macro +pub use crate::editor::{Editor, ParentWindowHandle}; pub use crate::midi::{control_change, MidiConfig, NoteEvent}; pub use crate::params::enums::{Enum, EnumParam}; pub use crate::params::internals::ParamPtr; @@ -21,9 +22,8 @@ pub use crate::params::smoothing::{Smoothable, Smoother, SmoothingStyle}; pub use crate::params::Params; pub use crate::params::{BoolParam, FloatParam, IntParam, Param, ParamFlags}; pub use crate::plugin::{ - AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Editor, - ParentWindowHandle, Plugin, PolyModulationConfig, PortNames, ProcessMode, ProcessStatus, - Vst3Plugin, + AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Plugin, + PolyModulationConfig, PortNames, ProcessMode, ProcessStatus, Vst3Plugin, }; pub use crate::wrapper::clap::features::ClapFeature; pub use crate::wrapper::state::PluginState; diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index c2dabca3..970f37f5 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -78,13 +78,13 @@ use super::descriptor::PluginDescriptor; use super::util::ClapPtr; use crate::buffer::Buffer; use crate::context::Transport; +use crate::editor::{Editor, ParentWindowHandle}; use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}; use crate::midi::{MidiConfig, NoteEvent}; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ - AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, Editor, ParentWindowHandle, ProcessMode, - ProcessStatus, + AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, ProcessMode, ProcessStatus, }; use crate::util::permit_alloc; use crate::wrapper::clap::util::{read_stream, write_stream}; diff --git a/src/wrapper/standalone/wrapper.rs b/src/wrapper/standalone/wrapper.rs index 0c86356c..6a7d1d82 100644 --- a/src/wrapper/standalone/wrapper.rs +++ b/src/wrapper/standalone/wrapper.rs @@ -14,12 +14,13 @@ use super::backend::Backend; use super::config::WrapperConfig; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; use crate::context::Transport; +use crate::editor::{Editor, ParentWindowHandle}; use crate::midi::NoteEvent; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ - AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Editor, ParentWindowHandle, - Plugin, ProcessMode, ProcessStatus, + AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Plugin, ProcessMode, + ProcessStatus, }; use crate::util::permit_alloc; use crate::wrapper::state::{self, PluginState}; diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs index 6d4e6998..016aa322 100644 --- a/src/wrapper/vst3/inner.rs +++ b/src/wrapper/vst3/inner.rs @@ -16,11 +16,12 @@ use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_STAR use super::view::WrapperView; use crate::buffer::Buffer; use crate::context::Transport; +use crate::editor::Editor; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop}; use crate::midi::{MidiConfig, NoteEvent}; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; -use crate::plugin::{BufferConfig, BusConfig, Editor, ProcessMode, ProcessStatus, Vst3Plugin}; +use crate::plugin::{BufferConfig, BusConfig, ProcessMode, ProcessStatus, Vst3Plugin}; use crate::wrapper::state::{self, PluginState}; use crate::wrapper::util::{hash_param_id, process_wrapper}; diff --git a/src/wrapper/vst3/view.rs b/src/wrapper/vst3/view.rs index 937032c5..14f8ff10 100644 --- a/src/wrapper/vst3/view.rs +++ b/src/wrapper/vst3/view.rs @@ -13,7 +13,8 @@ use vst3_sys::VST3; use super::inner::{Task, WrapperInner}; use super::util::{ObjectPtr, VstPtr}; -use crate::plugin::{Editor, ParentWindowHandle, Vst3Plugin}; +use crate::editor::{Editor, ParentWindowHandle}; +use crate::plugin::Vst3Plugin; // Alias needed for the VST3 attribute macro use vst3_sys as vst3_com;