Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/plugin_clack/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clack_plugin::prelude::*;

pub struct ExamplePluginAudioProcessor;

impl<'a> PluginAudioProcessor<'a, (), ExamplePluginMainThread> for ExamplePluginAudioProcessor {
impl<'a> PluginAudioProcessor<'a, (), ExamplePluginMainThread<'a>> for ExamplePluginAudioProcessor {
fn activate(
_host: HostAudioProcessorHandle<'a>, _main_thread: &mut ExamplePluginMainThread,
_shared: &'a (), _audio_config: PluginAudioConfiguration,
Expand Down
53 changes: 46 additions & 7 deletions examples/plugin_clack/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ use crate::window_handler::OpenWindowExample;
use crate::ExamplePluginMainThread;
use baseview::dpi::*;
use baseview::gl::GlConfig;
use baseview::{WindowHandle, WindowOpenOptions, WindowSize};
use baseview::host::{Host, HostCallbacks, HostMainThreadCaller};
use baseview::{HandlerError, WindowHandle, WindowOpenOptions, WindowSize};
use clack_extensions::gui::{
AspectRatioStrategy, GuiApiType, GuiConfiguration, GuiResizeHints, GuiSize, PluginGuiImpl,
Window as ClapWindow,
AspectRatioStrategy, GuiApiType, GuiConfiguration, GuiResizeHints, GuiSize, HostGui,
PluginGuiImpl, Window as ClapWindow,
};
use clack_plugin::plugin::PluginError;

use clack_plugin::prelude::{HostMainThreadHandle, HostSharedHandle};
#[allow(deprecated)]
use raw_window_handle::HasRawWindowHandle;

pub struct ExamplePluginGui {
handle: WindowHandle,
pub handle: WindowHandle,
}

impl PluginGuiImpl for ExamplePluginMainThread {
impl PluginGuiImpl for ExamplePluginMainThread<'_> {
fn is_api_supported(&mut self, configuration: GuiConfiguration) -> bool {
!configuration.is_floating
&& Some(configuration.api_type) == GuiApiType::default_for_current_platform()
Expand Down Expand Up @@ -97,7 +98,17 @@ impl PluginGuiImpl for ExamplePluginMainThread {
.with_gl_config(GlConfig::default())
.with_parent(&parent);

let window = baseview::create_window(options, OpenWindowExample::new)?;
let mut host = Host::new().with_main_thread(unsafe {
MainThreadHandler { host: self.host.shared().with_arbitrary_lifetime() }
});

if let Some(gui) = self.host_gui {
host = host.with_callbacks(unsafe {
HostGuiCallbacks { ext: gui, host: self.host.with_arbitrary_lifetime() }
});
}

let window = baseview::create_window_with_host(options, OpenWindowExample::new, host)?;

self.gui = Some(ExamplePluginGui { handle: window });

Expand Down Expand Up @@ -145,3 +156,31 @@ fn gui_size_to_window_size(size: GuiSize) -> Size {
Size::Physical(PhysicalSize::new(size.width, size.height))
}
}

struct MainThreadHandler {
host: HostSharedHandle<'static>,
}

impl HostMainThreadCaller for MainThreadHandler {
fn call_main_thread(&mut self) {
eprintln!("Requesting main thread");
self.host.request_callback();
}
}

struct HostGuiCallbacks {
ext: HostGui,
host: HostMainThreadHandle<'static>,
}

impl HostCallbacks for HostGuiCallbacks {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), HandlerError> {
let size = window_size_to_gui_size(new_size);
self.ext.request_resize(&self.host, size.width, size.height)?;
Ok(())
}

fn destroyed(&mut self) {
self.ext.closed(&self.host, true);
}
}
22 changes: 15 additions & 7 deletions examples/plugin_clack/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::audio::ExamplePluginAudioProcessor;
use crate::gui::ExamplePluginGui;
use clack_extensions::gui::PluginGui;
use clack_extensions::gui::{HostGui, PluginGui};
use clack_plugin::prelude::*;

mod audio;
Expand All @@ -15,7 +15,7 @@ pub struct ExamplePlugin;
impl Plugin for ExamplePlugin {
type AudioProcessor<'a> = ExamplePluginAudioProcessor;
type Shared<'a> = ();
type MainThread<'a> = ExamplePluginMainThread;
type MainThread<'a> = ExamplePluginMainThread<'a>;

fn declare_extensions(builder: &mut PluginExtensions<Self>, _shared: Option<&()>) {
builder.register::<PluginGui>();
Expand All @@ -35,20 +35,28 @@ impl DefaultPluginFactory for ExamplePlugin {
}

fn new_main_thread<'a>(
_host: HostMainThreadHandle<'a>, _shared: &'a Self::Shared<'a>,
host: HostMainThreadHandle<'a>, _shared: &'a Self::Shared<'a>,
) -> Result<Self::MainThread<'a>, PluginError> {
Ok(Self::MainThread { gui: None })
Ok(Self::MainThread { gui: None, host_gui: host.get_extension(), host })
}
}

/// The data that belongs to the main thread of our plugin.
pub struct ExamplePluginMainThread {
pub struct ExamplePluginMainThread<'a> {
/// The host handle
host: HostMainThreadHandle<'a>,
// The host GUI extension handle
host_gui: Option<HostGui>,
/// The plugin's GUI state and context
gui: Option<ExamplePluginGui>,
}

impl<'a> PluginMainThread<'a, ()> for ExamplePluginMainThread {
fn on_main_thread(&mut self) {}
impl<'a> PluginMainThread<'a, ()> for ExamplePluginMainThread<'a> {
fn on_main_thread(&mut self) {
if let Some(gui) = self.gui.as_mut() {
gui.handle.host_main_thread_callback();
}
}
}

clack_export_entry!(SinglePluginEntry<ExamplePlugin>);
13 changes: 12 additions & 1 deletion examples/plugin_clack/src/window_handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use baseview::dpi::PhysicalPosition;
use baseview::{
Event, EventStatus, HandlerError, MouseEvent, WindowContext, WindowHandler, WindowSize,
Event, EventStatus, HandlerError, MouseButton, MouseEvent, WindowContext, WindowHandler,
WindowSize,
};
use std::cell::{Cell, RefCell};
use std::num::NonZeroU32;
Expand Down Expand Up @@ -111,6 +112,16 @@ impl WindowHandler for OpenWindowExample {
self.is_cursor_inside.set(false);
self.damaged.set(true);
}
Event::Mouse(MouseEvent::ButtonPressed { button: MouseButton::Left, .. }) => {
let mut size = self.window_context.size().physical;
if self.mouse_pos.get().x < 100.0 {
size.width -= 50;
} else {
size.width += 50;
}

self.window_context.resize(size).unwrap();
}
_ => {}
}

Expand Down
122 changes: 122 additions & 0 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use crate::{HandlerError, WindowSize};
use std::cell::RefCell;

/// A special handler for the Window thread to wake up and call methods on the main thread.
///
/// [`WindowHandle::host_main_thread_callback`](crate::WindowHandle::host_main_thread_callback)
/// should be called as a response to this.
///
/// # Platform compatibility notes
///
/// This is only needed on X11, as Windows and macOS windows already run on the main thread.
pub trait HostMainThreadCaller: Send + 'static {
/// Schedules a callback on the main thread.
///
/// [`WindowHandle::host_main_thread_callback`](crate::WindowHandle::host_main_thread_callback)
/// should be called as a response to this.
///
/// # Platform compatibility notes
///
/// Only X11 needs this. This can be implemented as a no-op on Windows and macOS.
fn call_main_thread(&mut self);
}

/// A handler for baseview windows to interact with their host.
pub trait HostCallbacks: 'static {
/// Requests the parent window to be resized to accommodate the child window with the given new
/// size.
///
/// # Errors
///
/// This can return any type of error, indicating the host either failed or denied to handle the
/// resize request.
/// If it does, the error is logged and the resize operation is canceled or reverted.
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), HandlerError>;
/// Notifies the host that the child window has been destroyed for a reason outside the host's
/// control.
///
/// This can be because the display connection was lost, because the window handler crashed, or
/// because the window handler decided to close the window itself.
///
/// The host should close its parent window, as it will not show anything useful anymore.
fn destroyed(&mut self);
}

/// Configuration and callbacks for a window's host.
///
/// # Safety
///
/// This type and its methods are always safe to use.
///
/// It also brings the additional safety guarantee that all handlers given to this types will be
/// destroyed alongside with the window, guaranteeing callbacks cannot be fired after the
/// [`WindowHandle`](crate::WindowHandle) is dropped. (or after this [`Host`] object is dropped, if
/// it never made it to a [`create_window_with_host`](crate::create_window_with_host) call).
pub struct Host {
#[cfg(target_os = "linux")]
pub(crate) main_thread: Option<Box<dyn HostMainThreadCaller>>,
pub(crate) callbacks: Option<RefCell<Box<dyn HostCallbacks>>>,
}

impl Default for Host {
fn default() -> Self {
Self::new()
}
}

impl Host {
/// Creates a new, empty host with no callbacks.
///
/// Calling [`create_window_with_host`](crate::create_window_with_host) with this is equivalent
/// to just calling [`create_window`](crate::create_window).
#[inline]
pub fn new() -> Self {
Self {
#[cfg(target_os = "linux")]
main_thread: None,
callbacks: None,
}
}

/// Sets the [`HostMainThreadCaller`] handler to be used.
///
/// If another callback handler was already set, it is replaced.
///
/// # Platform Compatibility notes
///
/// This is only useful on X11. On Window and macOS, this is a no-op.
#[inline]
#[allow(unused)]
pub fn with_main_thread(mut self, main_thread: impl HostMainThreadCaller) -> Self {
#[cfg(target_os = "linux")]
{
self.main_thread = Some(Box::new(main_thread));
}

self
}

/// Sets the [`HostCallbacks`] handler to be used.
///
/// If another callback handler was already set, it is replaced.
#[inline]
pub fn with_callbacks(mut self, callbacks: impl HostCallbacks) -> Self {
self.callbacks = Some(RefCell::new(Box::new(callbacks)));
self
}
}

#[allow(unused)]
impl Host {
pub(crate) fn notify_destroyed(&self) {
let Some(callbacks) = &self.callbacks else { return };
let Ok(mut callbacks) = callbacks.try_borrow_mut() else { return };
callbacks.destroyed();
}

pub(crate) fn request_resize(&self, new_size: WindowSize) -> Result<(), HandlerError> {
let Some(callbacks) = &self.callbacks else { return Ok(()) };
let Ok(mut callbacks) = callbacks.try_borrow_mut() else { return Ok(()) };
callbacks.request_resize(new_size)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod context;
mod error;
mod event;
mod handler;
pub mod host;
mod keyboard;
mod mouse_cursor;
mod tracing;
Expand Down
4 changes: 2 additions & 2 deletions src/platform/macos/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl WindowContext {
pub fn request_close(&self) {
let Some(view) = self.view.load() else { return };
let Some(view) = view.inner_ref() else { return };
BaseviewView::close(view);
BaseviewView::close(view, false);
}

pub fn has_focus(&self) -> bool {
Expand Down Expand Up @@ -67,7 +67,7 @@ impl WindowContext {
return Ok(());
}

BaseviewView::resize(view, size);
BaseviewView::resize(view, size, true);

Ok(())
}
Expand Down
Loading