Close standalone when process returns an error
For this to work the resize mutex has been replaced with a channel for sending tasks.
This commit is contained in:
parent
5d481e59f1
commit
60593e7cc3
|
@ -1,8 +1,8 @@
|
||||||
use parking_lot::Mutex;
|
use crossbeam::channel;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::backend::Backend;
|
use super::backend::Backend;
|
||||||
use super::wrapper::Wrapper;
|
use super::wrapper::{GuiTask, Wrapper};
|
||||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
||||||
use crate::midi::NoteEvent;
|
use crate::midi::NoteEvent;
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
|
@ -14,9 +14,9 @@ use crate::plugin::Plugin;
|
||||||
pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend> {
|
pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend> {
|
||||||
pub(super) wrapper: Arc<Wrapper<P, B>>,
|
pub(super) wrapper: Arc<Wrapper<P, B>>,
|
||||||
|
|
||||||
/// If the widnow should be resized, then we will write the size here. This will be set on the
|
/// This allows us to send tasks to the parent view that will be handled at the start of its
|
||||||
/// window at the start of the next frame.
|
/// next frame.
|
||||||
pub(super) new_window_size: Arc<Mutex<Option<(u32, u32)>>>,
|
pub(super) gui_task_sender: channel::Sender<GuiTask>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it
|
/// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it
|
||||||
|
@ -36,11 +36,18 @@ impl<P: Plugin, B: Backend> GuiContext for WrapperGuiContext<P, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_resize(&self) -> bool {
|
fn request_resize(&self) -> bool {
|
||||||
let new_size = self.wrapper.editor.as_ref().unwrap().size();
|
let (unscaled_width, unscaled_height) = self.wrapper.editor.as_ref().unwrap().size();
|
||||||
|
|
||||||
// This will cause the editor to be resized at the start of the next frame. If we need to do
|
// This will cause the editor to be resized at the start of the next frame
|
||||||
// more of these things, then we should consider using a channel instead.
|
let dpi_scale = self.wrapper.dpi_scale();
|
||||||
*self.new_window_size.lock() = Some(new_size);
|
let push_successful = self
|
||||||
|
.gui_task_sender
|
||||||
|
.send(GuiTask::Resize(
|
||||||
|
(unscaled_width as f32 * dpi_scale).round() as u32,
|
||||||
|
(unscaled_height as f32 * dpi_scale).round() as u32,
|
||||||
|
))
|
||||||
|
.is_ok();
|
||||||
|
nih_debug_assert!(push_successful, "Could not queue window resize");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use atomic_refcell::AtomicRefCell;
|
use atomic_refcell::AtomicRefCell;
|
||||||
use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions};
|
use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions};
|
||||||
|
use crossbeam::channel;
|
||||||
use crossbeam::queue::ArrayQueue;
|
use crossbeam::queue::ArrayQueue;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::RwLock;
|
||||||
use raw_window_handle::HasRawWindowHandle;
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -90,21 +91,34 @@ struct WrapperWindowHandler {
|
||||||
/// gets dropped.
|
/// gets dropped.
|
||||||
_editor_handle: Box<dyn Any>,
|
_editor_handle: Box<dyn Any>,
|
||||||
|
|
||||||
/// If contains a value, then the GUI will be resized at the start of the next frame. This is
|
/// This is used to communicate with the wrapper from the audio thread and from within the
|
||||||
/// set from [`WrapperGuiContext::request_resize()`].
|
/// baseview window handler on the GUI thread.
|
||||||
new_window_size: Arc<Mutex<Option<(u32, u32)>>>,
|
gui_task_receiver: channel::Receiver<GuiTask>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A message sent to the GUI thread.
|
||||||
|
pub enum GuiTask {
|
||||||
|
/// Resize the window to the following physical size.
|
||||||
|
Resize(u32, u32),
|
||||||
|
/// The close window. This will cause the application to terminate.
|
||||||
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowHandler for WrapperWindowHandler {
|
impl WindowHandler for WrapperWindowHandler {
|
||||||
fn on_frame(&mut self, window: &mut Window) {
|
fn on_frame(&mut self, window: &mut Window) {
|
||||||
if let Some((new_width, new_height)) = self.new_window_size.lock().take() {
|
while let Ok(task) = self.gui_task_receiver.try_recv() {
|
||||||
// Window resizing in baseview has only been implemented on Linux
|
match task {
|
||||||
#[cfg(target_os = "linux")]
|
GuiTask::Resize(new_width, new_height) => {
|
||||||
{
|
// Window resizing in baseview has only been implemented on Linux
|
||||||
window.resize(baseview::Size {
|
#[cfg(target_os = "linux")]
|
||||||
width: new_width as f64,
|
{
|
||||||
height: new_height as f64,
|
window.resize(baseview::Size {
|
||||||
});
|
width: new_width as f64,
|
||||||
|
height: new_height as f64,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GuiTask::Close => window.close(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,22 +210,21 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
/// Will return an error if the plugin threw an error during audio processing or if the editor
|
/// Will return an error if the plugin threw an error during audio processing or if the editor
|
||||||
/// could not be opened.
|
/// could not be opened.
|
||||||
pub fn run(self: Arc<Self>) -> Result<(), WrapperError> {
|
pub fn run(self: Arc<Self>) -> Result<(), WrapperError> {
|
||||||
|
let (gui_task_sender, gui_task_receiver) = channel::bounded(512);
|
||||||
|
|
||||||
// We'll spawn a separate thread to handle IO and to process audio. This audio thread should
|
// We'll spawn a separate thread to handle IO and to process audio. This audio thread should
|
||||||
// terminate together with this function.
|
// terminate together with this function.
|
||||||
let terminate_audio_thread = Arc::new(AtomicBool::new(false));
|
let terminate_audio_thread = Arc::new(AtomicBool::new(false));
|
||||||
let audio_thread = {
|
let audio_thread = {
|
||||||
let terminate_audio_thread = terminate_audio_thread.clone();
|
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
thread::spawn(move || this.run_audio_thread(terminate_audio_thread))
|
let terminate_audio_thread = terminate_audio_thread.clone();
|
||||||
|
let gui_task_sender = gui_task_sender.clone();
|
||||||
|
thread::spawn(move || this.run_audio_thread(terminate_audio_thread, gui_task_sender))
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.editor.clone() {
|
match self.editor.clone() {
|
||||||
Some(editor) => {
|
Some(editor) => {
|
||||||
// We'll use this mutex to communicate window size changes. If we need to send a lot
|
let context = self.clone().make_gui_context(gui_task_sender);
|
||||||
// more information to the window handler at some point, then consider replacing
|
|
||||||
// this with a channel.
|
|
||||||
let new_window_size = Arc::new(Mutex::new(None));
|
|
||||||
let context = self.clone().make_gui_context(new_window_size.clone());
|
|
||||||
|
|
||||||
// DPI scaling should not be used on macOS since the OS handles it there
|
// DPI scaling should not be used on macOS since the OS handles it there
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -247,13 +260,14 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
|
|
||||||
WrapperWindowHandler {
|
WrapperWindowHandler {
|
||||||
_editor_handle: editor_handle,
|
_editor_handle: editor_handle,
|
||||||
new_window_size,
|
gui_task_receiver,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// TODO: Block until SIGINT is received if the plugin does not have an editor
|
// TODO: Block until SIGINT is received if the plugin does not have an editor
|
||||||
|
// TODO: Make sure to handle `GuiTask::Close` here as well
|
||||||
todo!("Support standalone plugins without editors");
|
todo!("Support standalone plugins without editors");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,9 +298,22 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
push_succesful
|
push_succesful
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The DPI scale factor for this standalone application
|
||||||
|
pub fn dpi_scale(&self) -> f32 {
|
||||||
|
// DPI scaling should be ignored on macOS since the OS already handles this
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
return 1.0;
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
return self.config.dpi_scale;
|
||||||
|
}
|
||||||
|
|
||||||
/// The audio thread. This should be called from another thread, and it will run until
|
/// The audio thread. This should be called from another thread, and it will run until
|
||||||
/// `should_terminate` is `true`.
|
/// `should_terminate` is `true`.
|
||||||
fn run_audio_thread(self: Arc<Self>, should_terminate: Arc<AtomicBool>) {
|
fn run_audio_thread(
|
||||||
|
self: Arc<Self>,
|
||||||
|
should_terminate: Arc<AtomicBool>,
|
||||||
|
gui_task_sender: channel::Sender<GuiTask>,
|
||||||
|
) {
|
||||||
// TODO: We should add a way to pull the transport information from the JACK backend
|
// TODO: We should add a way to pull the transport information from the JACK backend
|
||||||
let mut num_processed_samples = 0;
|
let mut num_processed_samples = 0;
|
||||||
|
|
||||||
|
@ -313,6 +340,12 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
eprintln!("The plugin returned an error while processing:");
|
eprintln!("The plugin returned an error while processing:");
|
||||||
eprintln!("{}", err);
|
eprintln!("{}", err);
|
||||||
|
|
||||||
|
let push_successful = gui_task_sender.send(GuiTask::Close).is_ok();
|
||||||
|
nih_debug_assert!(
|
||||||
|
push_successful,
|
||||||
|
"Could not queue window close, the editor will remain open"
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,11 +377,11 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
|
|
||||||
fn make_gui_context(
|
fn make_gui_context(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
new_window_size: Arc<Mutex<Option<(u32, u32)>>>,
|
gui_task_sender: channel::Sender<GuiTask>,
|
||||||
) -> Arc<WrapperGuiContext<P, B>> {
|
) -> Arc<WrapperGuiContext<P, B>> {
|
||||||
Arc::new(WrapperGuiContext {
|
Arc::new(WrapperGuiContext {
|
||||||
wrapper: self,
|
wrapper: self,
|
||||||
new_window_size,
|
gui_task_sender,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue