1
0
Fork 0

Drop Sync requirement for Editor

This commit is contained in:
Robbert van der Helm 2022-10-20 14:31:48 +02:00
parent eed5a62abb
commit a2a52e0ff1
7 changed files with 59 additions and 32 deletions

View file

@ -8,6 +8,8 @@ code then it will not be listed here.
## [2022-10-20]
- `Editor` now only requires `Send` and no longer needs `Sync`. This is not a
breaking change, but it might be worth being aware of.
- The `create_egui_editor()` function from `nih_plug_egui` now also takes a
build closure to apply initialization logic to the egui context.
- The `nih_plug::param` module has been renamed to `nih_plug::params`. Code that

View file

@ -253,7 +253,7 @@ const fn swap_vst3_uid_byte_order(mut uid: [u8; 16]) -> [u8; 16] {
}
/// An editor for a [`Plugin`].
pub trait Editor: Send + Sync {
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

View file

@ -59,7 +59,7 @@ use clap_sys::stream::{clap_istream, clap_ostream};
use crossbeam::atomic::AtomicCell;
use crossbeam::channel::{self, SendTimeoutError};
use crossbeam::queue::ArrayQueue;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use raw_window_handle::RawWindowHandle;
use std::any::Any;
use std::cmp;
@ -112,7 +112,7 @@ pub struct Wrapper<P: ClapPlugin> {
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
/// creating an editor.
editor: Option<Box<dyn Editor>>,
editor: Option<Mutex<Box<dyn Editor>>>,
/// A handle for the currently active editor instance. The plugin should implement `Drop` on
/// this handle for its closing behavior.
editor_handle: RwLock<Option<Box<dyn Any + Send + Sync>>>,
@ -381,7 +381,7 @@ impl<P: ClapPlugin> MainThreadExecutor<Task> for Wrapper<P> {
impl<P: ClapPlugin> Wrapper<P> {
pub fn new(host_callback: *const clap_host) -> Arc<Self> {
let plugin = P::default();
let editor = plugin.editor();
let editor = plugin.editor().map(Mutex::new);
// This is used to allow the plugin to restore preset data from its editor, see the comment
// on `Self::updated_state_sender`
@ -713,10 +713,18 @@ impl<P: ClapPlugin> Wrapper<P> {
/// If there's an editor open, let it know that parameter values have changed. This should be
/// called whenever there's been a call or multiple calls to
/// [`update_plain_value_by_hash()[Self::update_plain_value_by_hash()`].
/// [`update_plain_value_by_hash()[Self::update_plain_value_by_hash()`]. In the off-chance that
/// the editor instance is currently locked then nothing will happen, and the request can safely
/// be ignored.
pub fn notify_param_values_changed(&self) {
if let Some(editor) = &self.editor {
editor.param_values_changed();
match editor.try_lock() {
Some(editor) => editor.param_values_changed(),
None => nih_debug_assert_failure!(
"The editor was locked when sending a parameter value change notification, \
ignoring"
),
}
}
}
@ -726,7 +734,7 @@ impl<P: ClapPlugin> Wrapper<P> {
pub fn request_resize(&self) -> bool {
match (&*self.host_gui.borrow(), &self.editor) {
(Some(host_gui), Some(editor)) => {
let (unscaled_width, unscaled_height) = editor.size();
let (unscaled_width, unscaled_height) = editor.lock().size();
let scaling_factor = self.editor_scaling_factor.load(Ordering::Relaxed);
unsafe_clap_call! {
@ -2672,6 +2680,7 @@ impl<P: ClapPlugin> Wrapper<P> {
.editor
.as_ref()
.unwrap()
.lock()
.set_scale_factor(scale as f32)
{
wrapper
@ -2692,7 +2701,7 @@ impl<P: ClapPlugin> Wrapper<P> {
let wrapper = &*(plugin as *const Self);
// For macOS the scaling factor is always 1
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().size();
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().lock().size();
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
(*width, *height) = (
(unscaled_width as f32 * scaling_factor).round() as u32,
@ -2734,7 +2743,7 @@ impl<P: ClapPlugin> Wrapper<P> {
check_null_ptr!(false, plugin);
let wrapper = &*(plugin as *const Self);
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().size();
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().lock().size();
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
let (editor_width, editor_height) = (
(unscaled_width as f32 * scaling_factor).round() as u32,
@ -2776,7 +2785,7 @@ impl<P: ClapPlugin> Wrapper<P> {
};
// This extension is only exposed when we have an editor
*editor_handle = Some(wrapper.editor.as_ref().unwrap().spawn(
*editor_handle = Some(wrapper.editor.as_ref().unwrap().lock().spawn(
ParentWindowHandle { handle },
wrapper.clone().make_gui_context(),
));

View file

@ -47,7 +47,7 @@ impl<P: Plugin, B: Backend> GuiContext for WrapperGuiContext<P, B> {
}
fn request_resize(&self) -> bool {
let (unscaled_width, unscaled_height) = self.wrapper.editor.as_ref().unwrap().size();
let (unscaled_width, unscaled_height) = self.wrapper.editor.as_ref().unwrap().lock().size();
// This will cause the editor to be resized at the start of the next frame
let push_successful = self

View file

@ -2,7 +2,7 @@ use atomic_refcell::AtomicRefCell;
use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions};
use crossbeam::channel;
use crossbeam::queue::ArrayQueue;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use raw_window_handle::HasRawWindowHandle;
use std::any::Any;
use std::collections::{HashMap, HashSet};
@ -47,7 +47,7 @@ pub struct Wrapper<P: Plugin, B: Backend> {
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
/// creating an editor.
pub editor: Option<Arc<dyn Editor>>,
pub editor: Option<Arc<Mutex<Box<dyn Editor>>>>,
config: WrapperConfig,
@ -132,7 +132,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
pub fn new(backend: B, config: WrapperConfig) -> Result<Arc<Self>, WrapperError> {
let plugin = P::default();
let params = plugin.params();
let editor = plugin.editor().map(Arc::from);
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor)));
// This is used to allow the plugin to restore preset data from its editor, see the comment
// on `Self::updated_state_sender`
@ -262,11 +262,11 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
let scaling_policy = baseview::WindowScalePolicy::SystemScaleFactor;
#[cfg(not(target_os = "macos"))]
let scaling_policy = {
editor.set_scale_factor(self.config.dpi_scale);
editor.lock().set_scale_factor(self.config.dpi_scale);
baseview::WindowScalePolicy::ScaleFactor(self.config.dpi_scale as f64)
};
let (width, height) = editor.size();
let (width, height) = editor.lock().size();
Window::open_blocking(
WindowOpenOptions {
title: String::from(P::NAME),
@ -282,7 +282,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
// baseview does not support this yet. Once this is added, we should
// immediately close the parent window when this happens so the loop
// can exit.
let editor_handle = editor.spawn(
let editor_handle = editor.lock().spawn(
ParentWindowHandle {
handle: window.raw_window_handle(),
},
@ -477,10 +477,18 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
);
}
/// Tell the editor that the parameter values have changed, if the plugin has an editor.
/// Tell the editor that the parameter values have changed, if the plugin has an editor. In the
/// off-chance that the editor instance is currently locked then nothing will happen, and the
/// request can safely be ignored.
fn notify_param_values_changed(&self) {
if let Some(editor) = &self.editor {
editor.param_values_changed();
match editor.try_lock() {
Some(editor) => editor.param_values_changed(),
None => nih_debug_assert_failure!(
"The editor was locked when sending a parameter value change notification, \
ignoring"
),
}
}
}

View file

@ -1,7 +1,7 @@
use atomic_refcell::AtomicRefCell;
use crossbeam::atomic::AtomicCell;
use crossbeam::channel::{self, SendTimeoutError};
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
@ -37,7 +37,7 @@ pub(crate) struct WrapperInner<P: Vst3Plugin> {
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
/// creating an editor.
pub editor: Option<Arc<dyn Editor>>,
pub editor: Option<Arc<Mutex<Box<dyn Editor>>>>,
/// The host's [`IComponentHandler`] instance, if passed through
/// [`IEditController::set_component_handler`].
@ -190,7 +190,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
#[allow(unused_unsafe)]
pub fn new() -> Arc<Self> {
let plugin = P::default();
let editor = plugin.editor().map(Arc::from);
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor)));
// This is used to allow the plugin to restore preset data from its editor, see the comment
// on `Self::updated_state_sender`
@ -372,10 +372,18 @@ impl<P: Vst3Plugin> WrapperInner<P> {
/// If there's an editor open, let it know that parameter values have changed. This should be
/// called whenever there's been a call or multiple calls to
/// [`set_normalized_value_by_hash()[Self::set_normalized_value_by_hash()`].
/// [`set_normalized_value_by_hash()[Self::set_normalized_value_by_hash()`]. In the off-chance
/// that the editor instance is currently locked then nothing will happen, and the request can
/// safely be ignored.
pub fn notify_param_values_changed(&self) {
if let Some(editor) = &self.editor {
editor.param_values_changed();
match editor.try_lock() {
Some(editor) => editor.param_values_changed(),
None => nih_debug_assert_failure!(
"The editor was locked when sending a parameter value change notification, \
ignoring"
),
}
}
}

View file

@ -1,5 +1,5 @@
use atomic_float::AtomicF32;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use raw_window_handle::RawWindowHandle;
use std::any::Any;
use std::ffi::{c_void, CStr};
@ -52,7 +52,7 @@ struct RunLoopEventHandlerWrapper<P: Vst3Plugin>(std::marker::PhantomData<P>);
#[VST3(implements(IPlugView, IPlugViewContentScaleSupport))]
pub(crate) struct WrapperView<P: Vst3Plugin> {
inner: Arc<WrapperInner<P>>,
editor: Arc<dyn Editor>,
editor: Arc<Mutex<Box<dyn Editor>>>,
editor_handle: RwLock<Option<Box<dyn Any>>>,
/// The `IPlugFrame` instance passed by the host during [IPlugView::set_frame()].
@ -99,7 +99,7 @@ struct RunLoopEventHandler<P: Vst3Plugin> {
}
impl<P: Vst3Plugin> WrapperView<P> {
pub fn new(inner: Arc<WrapperInner<P>>, editor: Arc<dyn Editor>) -> Box<Self> {
pub fn new(inner: Arc<WrapperInner<P>>, editor: Arc<Mutex<Box<dyn Editor>>>) -> Box<Self> {
Self::allocate(
inner,
editor,
@ -132,7 +132,7 @@ impl<P: Vst3Plugin> WrapperView<P> {
match &*self.plug_frame.read() {
Some(plug_frame) => {
let (unscaled_width, unscaled_height) = self.editor.size();
let (unscaled_width, unscaled_height) = self.editor.lock().size();
let scaling_factor = self.scaling_factor.load(Ordering::Relaxed);
let mut size = ViewRect {
right: (unscaled_width as f32 * scaling_factor).round() as i32,
@ -314,7 +314,7 @@ impl<P: Vst3Plugin> IPlugView for WrapperView<P> {
}
};
*editor_handle = Some(self.editor.spawn(
*editor_handle = Some(self.editor.lock().spawn(
ParentWindowHandle { handle },
self.inner.clone().make_gui_context(),
));
@ -376,7 +376,7 @@ impl<P: Vst3Plugin> IPlugView for WrapperView<P> {
// 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 (unscaled_width, unscaled_height) = self.editor.lock().size();
let scaling_factor = self.scaling_factor.load(Ordering::Relaxed);
let size = &mut *size;
size.left = 0;
@ -391,7 +391,7 @@ impl<P: Vst3Plugin> IPlugView for WrapperView<P> {
check_null_ptr!(new_size);
// TODO: Implement Host->Plugin resizing
let (unscaled_width, unscaled_height) = self.editor.size();
let (unscaled_width, unscaled_height) = self.editor.lock().size();
let scaling_factor = self.scaling_factor.load(Ordering::Relaxed);
let (editor_width, editor_height) = (
(unscaled_width as f32 * scaling_factor).round() as i32,
@ -470,7 +470,7 @@ impl<P: Vst3Plugin> IPlugViewContentScaleSupport for WrapperView<P> {
return kResultFalse;
}
if self.editor.set_scale_factor(factor) {
if self.editor.lock().set_scale_factor(factor) {
self.scaling_factor.store(factor, Ordering::Relaxed);
kResultOk
} else {