Skip to content
Open
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ objc2-app-kit = { version = "0.3.2", default-features = false, features = [
] }

[workspace]
members = ["examples/cursors", "examples/open_parented", "examples/open_window", "examples/plugin_clack", "examples/render_femtovg", "examples/render_wgpu"]
members = ["examples/cursors", "examples/open_parented", "examples/open_window", "examples/plugin_clack", "examples/render_femtovg", "examples/render_wgpu", "examples/plugin_clack_femtovg"]

[lints.clippy]
missing-safety-doc = "allow"
Expand Down
4 changes: 3 additions & 1 deletion examples/plugin_clack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ crate-type = ["cdylib"]
[dependencies]
clack-plugin = "0.1.1"
clack-extensions = { version = "0.1.1", features = ["gui", "state", "clack-plugin", "raw-window-handle_06"] }
baseview = { path = "../..", features = ["opengl"] }
baseview = { path = "../..", features = ["tracing"] }
softbuffer = "0.4.8"

tracing-subscriber = { workspace = true }
tracing = "0.1.44"
10 changes: 5 additions & 5 deletions examples/plugin_clack/src/gui.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::window_handler::OpenWindowExample;
use crate::ExamplePluginMainThread;
use baseview::dpi::*;
use baseview::gl::GlConfig;
use baseview::host::{Host, HostCallbacks, HostMainThreadCaller};
use baseview::{HandlerError, Window, WindowSettings, WindowSize};
use clack_extensions::gui::{
Expand All @@ -10,6 +9,7 @@ use clack_extensions::gui::{
};
use clack_plugin::plugin::PluginError;
use clack_plugin::prelude::{HostMainThreadHandle, HostSharedHandle};
use tracing::Level;

pub struct ExamplePluginGui {
pub handle: Window,
Expand All @@ -29,10 +29,10 @@ impl PluginGuiImpl for ExamplePluginMainThread<'_> {
}

fn create(&mut self, _configuration: GuiConfiguration) -> Result<(), PluginError> {
let options = WindowSettings::new()
.wait_for_parent()
.with_size(PhysicalSize::new(400, 200))
.with_gl_config(GlConfig::default());
tracing_subscriber::fmt::fmt().with_max_level(Level::DEBUG).with_ansi(false).init();

let options =
WindowSettings::new().wait_for_parent().with_size(PhysicalSize::new(400, 200));

let mut host = Host::new().with_main_thread(unsafe {
MainThreadHandler { host: self.host.shared().with_arbitrary_lifetime() }
Expand Down
17 changes: 17 additions & 0 deletions examples/plugin_clack_femtovg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "plugin_clack_femtovg"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
clack-plugin = "0.1.1"
clack-extensions = { version = "0.1.1", features = ["gui", "state", "clack-plugin", "raw-window-handle_06"] }
baseview = { path = "../..", features = ["opengl", "tracing"] }
femtovg = "0.26"
raw-window-handle = { version = "0.6.2", features = ["std"] }

tracing-subscriber = { workspace = true }
tracing = "0.1.44"
31 changes: 31 additions & 0 deletions examples/plugin_clack_femtovg/src/audio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::ExamplePluginMainThread;
use clack_plugin::prelude::*;

pub struct ExamplePluginAudioProcessor;

impl<'a> PluginAudioProcessor<'a, (), ExamplePluginMainThread<'a>> for ExamplePluginAudioProcessor {
fn activate(
_host: HostAudioProcessorHandle<'a>, _main_thread: &mut ExamplePluginMainThread,
_shared: &'a (), _audio_config: PluginAudioConfiguration,
) -> Result<Self, PluginError> {
Ok(Self)
}

fn process(
&mut self, _process: Process, mut audio: Audio, _events: Events,
) -> Result<ProcessStatus, PluginError> {
for mut port in audio.port_pairs() {
let channels = port.channels()?.into_f32().expect("Expected f32 channels");

for channel_pair in channels {
match channel_pair {
ChannelPair::OutputOnly(o) => o.fill(0.0),
ChannelPair::InputOutput(i, o) => o.copy_from_slice(i),
_ => {}
}
}
}

Ok(ProcessStatus::Continue)
}
}
194 changes: 194 additions & 0 deletions examples/plugin_clack_femtovg/src/gui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use crate::window_handler::FemtovgExample;
use crate::ExamplePluginMainThread;
use baseview::dpi::*;
use baseview::gl::GlConfig;
use baseview::host::{Host, HostCallbacks, HostMainThreadCaller};
use baseview::{HandlerError, Window, WindowSettings, WindowSize};
use clack_extensions::gui::{
AspectRatioStrategy, GuiApiType, GuiConfiguration, GuiResizeHints, GuiSize, HostGui,
PluginGuiImpl, Window as ClapWindow,
};
use clack_plugin::plugin::PluginError;
use clack_plugin::prelude::{HostMainThreadHandle, HostSharedHandle};
use tracing::Level;

pub struct ExamplePluginGui {
pub handle: Window,
}

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()
}

fn get_preferred_api(&mut self) -> Option<GuiConfiguration<'_>> {
Some(GuiConfiguration {
api_type: GuiApiType::default_for_current_platform()?,
is_floating: false,
})
}

fn create(&mut self, _configuration: GuiConfiguration) -> Result<(), PluginError> {
tracing_subscriber::fmt::fmt().with_max_level(Level::DEBUG).with_ansi(false).init();

let options = WindowSettings::new()
.wait_for_parent()
.with_size(PhysicalSize::new(400, 200))
.with_gl_config(GlConfig::default());

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 = Window::create_with_host(options, FemtovgExample::new, host)?;

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

fn destroy(&mut self) {
let Some(gui) = self.gui.take() else { return };

gui.handle.close()
}

fn set_scale(&mut self, scale: f64) -> Result<(), PluginError> {
let Some(gui) = &self.gui else {
return Err(PluginError::Message("set_scale called without a GUI active"));
};
gui.handle.suggest_fallback_scale_factor(scale)?;

Ok(())
}

fn get_size(&mut self) -> Option<GuiSize> {
let Some(gui) = &self.gui else {
eprintln!("get_size called without a GUI active");
return None;
};

let size = gui.handle.size().to_native_size();

Some(GuiSize { width: size.width, height: size.height })
}

fn can_resize(&mut self) -> bool {
let Some(gui) = &self.gui else { return false };

gui.handle.is_resizable()
}

fn get_resize_hints(&mut self) -> Option<GuiResizeHints> {
let can_resize = self.can_resize();

Some(GuiResizeHints {
strategy: AspectRatioStrategy::Disregard, // Not supported

can_resize_vertically: can_resize,
can_resize_horizontally: can_resize,
})
}

fn adjust_size(&mut self, mut size: GuiSize) -> Option<GuiSize> {
let Some(gui) = &self.gui else { return None };
let scale_factor = gui.handle.size().scale_factor;

if let Some(max_size) = gui.handle.max_size() {
let max_size = NativeSize::from_size(max_size, scale_factor);
size.width = size.width.min(max_size.width);
size.height = size.height.min(max_size.height);
}

if let Some(min_size) = gui.handle.min_size() {
let min_size = NativeSize::from_size(min_size, scale_factor);
size.width = size.width.max(min_size.width);
size.height = size.height.max(min_size.height);
}

Some(size)
}

fn set_size(&mut self, size: GuiSize) -> Result<(), PluginError> {
let Some(gui) = &self.gui else {
return Err(PluginError::Message("set_size called without a GUI active"));
};

gui.handle.resize(NativeSize { width: size.width, height: size.height })?;

Ok(())
}

fn set_parent(&mut self, window: ClapWindow) -> Result<(), PluginError> {
let Some(gui) = &self.gui else {
return Err(PluginError::Message("set_parent called without a GUI active"));
};

// SAFETY: The CLAP spec ensures the parent window handle is valid for at least this call
let parent = unsafe { window.borrow_handle_unchecked()? };

gui.handle.set_parent(&parent)?;
gui.handle.show()?;

Ok(())
}

fn set_transient(&mut self, _window: ClapWindow) -> Result<(), PluginError> {
unimplemented!() // Not supported yet
}

fn suggest_title(&mut self, _title: &str) {
// Not supported yet
}

fn show(&mut self) -> Result<(), PluginError> {
let Some(gui) = &self.gui else {
return Err(PluginError::Message("show called without a GUI active"));
};
gui.handle.show()?;

Ok(())
}

fn hide(&mut self) -> Result<(), PluginError> {
let Some(gui) = &self.gui else {
return Err(PluginError::Message("hide called without a GUI active"));
};
gui.handle.show()?;

Ok(())
}
}

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

impl HostMainThreadCaller for MainThreadHandler {
fn call_main_thread(&mut self) {
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 new_size = new_size.to_native_size();
self.ext.request_resize(&self.host, new_size.width, new_size.height)?;
Ok(())
}

fn destroyed(&mut self) {
self.ext.closed(&self.host, true);
}
}
77 changes: 77 additions & 0 deletions examples/plugin_clack_femtovg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::audio::ExamplePluginAudioProcessor;
use crate::gui::ExamplePluginGui;
use clack_extensions::gui::{HostGui, PluginGui};
use clack_extensions::state::{PluginState, PluginStateImpl};
use clack_plugin::prelude::*;
use clack_plugin::stream::{InputStream, OutputStream};

mod audio;
mod gui;
mod window_handler;

/// The type that represents our plugin in Clack.
///
/// This is what implements the [`Plugin`] trait, where all the other subtypes are attached.
pub struct ExamplePlugin;

impl Plugin for ExamplePlugin {
type AudioProcessor<'a> = ExamplePluginAudioProcessor;
type Shared<'a> = ();
type MainThread<'a> = ExamplePluginMainThread<'a>;

fn declare_extensions(builder: &mut PluginExtensions<Self>, _shared: Option<&()>) {
builder.register::<PluginGui>().register::<PluginState>();
}
}

impl DefaultPluginFactory for ExamplePlugin {
fn get_descriptor() -> PluginDescriptor {
use clack_plugin::plugin::features::*;

PluginDescriptor::new(
"org.rust-audio.clack.gain-baseview-femtovg",
"Clack Gain Baseview Femtovg Example",
)
.with_features([AUDIO_EFFECT, STEREO])
}

fn new_shared(_host: HostSharedHandle<'_>) -> Result<Self::Shared<'_>, PluginError> {
Ok(())
}

fn new_main_thread<'a>(
host: HostMainThreadHandle<'a>, _shared: &'a Self::Shared<'a>,
) -> Result<Self::MainThread<'a>, PluginError> {
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<'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<'a> {
fn on_main_thread(&mut self) {
if let Some(gui) = self.gui.as_mut() {
gui.handle.host_main_thread_callback();
}
}
}

impl PluginStateImpl for ExamplePluginMainThread<'_> {
fn save(&mut self, _output: &mut OutputStream) -> Result<(), PluginError> {
Ok(())
}

fn load(&mut self, _input: &mut InputStream) -> Result<(), PluginError> {
Ok(())
}
}

clack_export_entry!(SinglePluginEntry<ExamplePlugin>);
Loading
Loading