diff --git a/src/context.rs b/src/context.rs index ebfa8cf7..591b7ebd 100644 --- a/src/context.rs +++ b/src/context.rs @@ -44,6 +44,14 @@ pub trait ProcessContext { // The implementing wrapper can assume that everything is being called from the main thread. Since // NIH-plug doesn't own the GUI event loop, this invariant cannot be part of the interface. pub trait GuiContext: Send + Sync + 'static { + /// Ask the host to resize the editor window to the size specified by [crate::Editor::size()]. + /// This will return false if the host somehow didn't like this and rejected the resize, in + /// which case the window should revert to its old size. You should only actually resize your + /// embedded window once this returns `true`. + /// + /// TODO: Host->Plugin resizing has not been implemented yet + fn request_resize(&self) -> bool; + /// Inform the host a parameter will be automated. Create a [`ParamSetter`] and use /// [`ParamSetter::begin_set_parameter()`] instead for a safe, user friendly API. /// diff --git a/src/plugin.rs b/src/plugin.rs index 50cd6a04..bd7b36e8 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -256,7 +256,7 @@ pub trait Editor: Send + Sync { // 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: Resizing + // TODO: Host->Plugin resizing } /// A raw window handle for platform and GUI framework agnostic editors. diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs index 260db7a1..8b94b09a 100644 --- a/src/wrapper/clap/context.rs +++ b/src/wrapper/clap/context.rs @@ -26,6 +26,10 @@ pub(crate) struct WrapperProcessContext<'a, P: ClapPlugin> { } impl GuiContext for WrapperGuiContext

{ + fn request_resize(&self) -> bool { + todo!("Implement window resizing for CLAP"); + } + // All of these functions are supposed to be called from the main thread, so we'll put some // trust in the caller and assume that this is indeed the case unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) { diff --git a/src/wrapper/vst3/context.rs b/src/wrapper/vst3/context.rs index 785b67e1..31b31517 100644 --- a/src/wrapper/vst3/context.rs +++ b/src/wrapper/vst3/context.rs @@ -27,6 +27,13 @@ pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> { } impl GuiContext for WrapperGuiContext

{ + fn request_resize(&self) -> bool { + match &*self.inner.plug_view.read() { + Some(plug_view) => plug_view.request_resize(), + None => false, + } + } + // All of these functions are supposed to be called from the main thread, so we'll put some // trust in the caller and assume that this is indeed the case unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) { diff --git a/src/wrapper/vst3/view.rs b/src/wrapper/vst3/view.rs index 48628d90..7bed2e40 100644 --- a/src/wrapper/vst3/view.rs +++ b/src/wrapper/vst3/view.rs @@ -8,7 +8,7 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use vst3_com::utils::SharedVstPtr; use vst3_sys::base::{kInvalidArgument, kResultFalse, kResultOk, tresult, TBool}; -use vst3_sys::gui::{IPlugFrame, IPlugView, IPlugViewContentScaleSupport}; +use vst3_sys::gui::{IPlugFrame, IPlugView, IPlugViewContentScaleSupport, ViewRect}; use vst3_sys::VST3; use super::inner::WrapperInner; @@ -57,6 +57,42 @@ impl WrapperView

{ AtomicF32::new(1.0), ) } + + /// Ask the host to resize the view to the size specified by [Editor::size()]. Will return false + /// if the host doesn't like you. + pub fn request_resize(&self) -> bool { + // Don't do anything if the editor is not open, because that would be strange + if self + .editor_handle + .try_read() + .map(|e| e.is_none()) + .unwrap_or(true) + { + return false; + } + + match &*self.plug_frame.read() { + Some(plug_frame) => { + let (unscaled_width, unscaled_height) = self.editor.size(); + let scaling_factor = self.scaling_factor.load(Ordering::Relaxed); + let mut size = ViewRect { + right: (unscaled_width as f32 * scaling_factor).round() as i32, + bottom: (unscaled_height as f32 * scaling_factor).round() as i32, + ..Default::default() + }; + + // The argument types are a bit wonky here because you can't construct a + // `SharedVstPtr`. This _should_ work however. + // FIXME: Run this in the `IRonLoop` on Linux. Otherwise REAPER will be very cross + // with us. + let plug_view: SharedVstPtr = unsafe { mem::transmute(self) }; + let result = unsafe { plug_frame.resize_view(plug_view, &mut size) }; + + result == kResultOk + } + None => false, + } + } } impl IPlugView for WrapperView

{ @@ -179,11 +215,14 @@ impl IPlugView for WrapperView

{ kResultOk } - unsafe fn get_size(&self, size: *mut vst3_sys::gui::ViewRect) -> tresult { + unsafe fn get_size(&self, size: *mut ViewRect) -> tresult { check_null_ptr!(size); *size = mem::zeroed(); + // TODO: This is technically incorrect during resizing, this should still report the old + // size until `.on_size()` has been called. We should probably only bother fixing this + // if it turns out to be an issue. let (unscaled_width, unscaled_height) = self.editor.size(); let scaling_factor = self.scaling_factor.load(Ordering::Relaxed); let size = &mut *size; @@ -195,10 +234,10 @@ impl IPlugView for WrapperView

{ kResultOk } - unsafe fn on_size(&self, new_size: *mut vst3_sys::gui::ViewRect) -> tresult { - // TODO: Implement resizing + unsafe fn on_size(&self, new_size: *mut ViewRect) -> tresult { check_null_ptr!(new_size); + // TODO: Implement Host->Plugin resizing let (unscaled_width, unscaled_height) = self.editor.size(); let scaling_factor = self.scaling_factor.load(Ordering::Relaxed); let (editor_width, editor_height) = ( @@ -231,14 +270,14 @@ impl IPlugView for WrapperView

{ } unsafe fn can_resize(&self) -> tresult { - // TODO: Implement resizing + // TODO: Implement Host->Plugin resizing kResultFalse } - unsafe fn check_size_constraint(&self, rect: *mut vst3_sys::gui::ViewRect) -> tresult { + unsafe fn check_size_constraint(&self, rect: *mut ViewRect) -> tresult { check_null_ptr!(rect); - // TODO: Add this with the resizing + // TODO: Implement Host->Plugin resizing if (*rect).right - (*rect).left > 0 && (*rect).bottom - (*rect).top > 0 { kResultOk } else {