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
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ opengl = [
[dependencies]
keyboard-types = { version = "0.8.3" }
raw-window-handle = "0.6.2"
dpi = "0.1.2"
baseview-host = { path = "./host", version = "0.1.0" }
tracing = { version = "0.1", optional = true }

[target.'cfg(target_os="linux")'.dependencies]
Expand Down Expand Up @@ -97,6 +97,7 @@ objc2-app-kit = { version = "0.3.2", default-features = false, features = [

[workspace]
members = [
"host",
"examples/cursors",
"examples/open_parented",
"examples/open_window",
Expand All @@ -108,7 +109,7 @@ members = [
"examples/timers",
"examples/external-wakeup",
"tests/partial-rendering"
]
, "host"]

[lints.clippy]
missing_safety_doc = "allow"
Expand Down
5 changes: 3 additions & 2 deletions examples/plugin_clack/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use crate::window_handler::OpenWindowExample;
use crate::ExamplePluginMainThread;
use baseview::dpi::*;
use baseview::host::{Host, HostCallbacks, HostMainThreadCaller};
use baseview::{HandlerError, Window, WindowSettings, WindowSize};
use baseview::{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 std::error::Error;

pub struct ExamplePluginGui {
pub handle: Window,
Expand Down Expand Up @@ -168,7 +169,7 @@ struct HostGuiCallbacks {
}

impl HostCallbacks for HostGuiCallbacks {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), HandlerError> {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), Box<dyn Error>> {
let new_size = new_size.to_native_size();
self.ext.request_resize(&self.host, new_size.width, new_size.height)?;
Ok(())
Expand Down
5 changes: 3 additions & 2 deletions examples/plugin_clack_femtovg/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use crate::ExamplePluginMainThread;
use baseview::dpi::*;
use baseview::gl::GlConfig;
use baseview::host::{Host, HostCallbacks, HostMainThreadCaller};
use baseview::{HandlerError, Window, WindowSettings, WindowSize};
use baseview::{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 std::error::Error;

pub struct ExamplePluginGui {
pub handle: Window,
Expand Down Expand Up @@ -168,7 +169,7 @@ struct HostGuiCallbacks {
}

impl HostCallbacks for HostGuiCallbacks {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), HandlerError> {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), Box<dyn Error>> {
let new_size = new_size.to_native_size();
self.ext.request_resize(&self.host, new_size.width, new_size.height)?;
Ok(())
Expand Down
18 changes: 18 additions & 0 deletions host/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "baseview-host"
version = "0.1.0"
authors = [
"Adrien Prokopowicz <prokopylmc@gmail.com>"
]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Interoperability library for plugin UIs"
keywords = ["windowing", "audio", "plugin"]
categories = ["gui"]
repository = "https://github.com/RustAudio/baseview"

rust-version = "1.82"

[dependencies]
dpi = "0.1.2"
raw-window-handle = "0.6.2"
7 changes: 7 additions & 0 deletions host/src/dpi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub use dpi::*;

mod native_size;
mod window_size;

pub use native_size::*;
pub use window_size::*;
108 changes: 108 additions & 0 deletions host/src/dpi/native_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use crate::dpi::*;

/// A size represented in the platform's native pixels.
///
/// This size is represented in physical pixels on Windows and Linux, and in logical pixels on macOS.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct NativeSize<P> {
pub width: P,
pub height: P,
}

#[cfg(target_os = "macos")]
const NATIVE_IS_LOGICAL: bool = true;
#[cfg(not(target_os = "macos"))]
const NATIVE_IS_LOGICAL: bool = false;

impl<P> NativeSize<P> {
#[inline]
pub const fn new(width: P, height: P) -> Self {
NativeSize { width, height }
}

/// This is `true` if the platform's native size is represented in logical pixels, and `false` otherwise.
///
/// This is `true` on macOS, and `false` on Windows and Linux.
pub const IS_LOGICAL: bool = NATIVE_IS_LOGICAL;
}

impl<P: Pixel> NativeSize<P> {
#[inline]
pub fn from_size(size: Size, scale_factor: f64) -> Self {
#[cfg(target_os = "macos")]
{
let size = size.to_logical(scale_factor);
Self { width: size.width, height: size.height }
}

#[cfg(not(target_os = "macos"))]
{
let size = size.to_physical(scale_factor);
Self { width: size.width, height: size.height }
}
}

#[inline]
pub fn cast<X: Pixel>(&self) -> NativeSize<X> {
NativeSize { width: self.width.cast(), height: self.height.cast() }
}

#[inline]
pub fn to_physical(self, scale_factor: f64) -> PhysicalSize<P> {
#[cfg(target_os = "macos")]
{
let size = LogicalSize { width: self.width, height: self.height };
size.to_physical(scale_factor)
}
#[cfg(not(target_os = "macos"))]
{
let _ = scale_factor;
PhysicalSize { width: self.width, height: self.height }
}
}

#[inline]
pub fn to_logical(self, scale_factor: f64) -> LogicalSize<P> {
#[cfg(target_os = "macos")]
{
let _ = scale_factor;
LogicalSize { width: self.width, height: self.height }
}
#[cfg(not(target_os = "macos"))]
{
let size = PhysicalSize { width: self.width, height: self.height };
size.to_logical(scale_factor)
}
}
}

impl<P: Pixel> From<WindowSize> for NativeSize<P> {
#[inline]
fn from(size: WindowSize) -> Self {
#[cfg(target_os = "macos")]
{
let size = size.logical.cast();
Self { width: size.width, height: size.height }
}

#[cfg(not(target_os = "macos"))]
{
let size = size.physical.cast();
Self { width: size.width, height: size.height }
}
}
}

impl<P: Pixel> From<NativeSize<P>> for Size {
#[inline]
fn from(size: NativeSize<P>) -> Self {
#[cfg(target_os = "macos")]
{
Size::Logical(LogicalSize::new(size.width, size.height).cast())
}
#[cfg(not(target_os = "macos"))]
{
Size::Physical(PhysicalSize::new(size.width, size.height).cast())
}
}
}
64 changes: 64 additions & 0 deletions host/src/dpi/window_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::dpi::*;

/// A window's size, which can be read in either logical or physical pixels.
///
/// Methods that produce this type in baseview guarantee that either the physical or the logical
/// size is directly from the underlying platform API.
///
/// This means that for either of the size types, there is at most only one conversion performed,
/// which minimizes errors that may occur due to rounding.
#[derive(Debug, Copy, Clone)]
pub struct WindowSize {
/// The window's size in physical pixels.
pub physical: PhysicalSize<u32>,
/// The window's size in logical pixels.
pub logical: LogicalSize<f64>,
/// The backing scale factor of the window.
///
/// This is the value used to convert between the physical and logical sizes.
pub scale_factor: f64,
}

impl WindowSize {
/// Constructs a [`WindowSize`] from a given [`PhysicalSize`] and `scale_factor`.
///
/// The [`LogicalSize`] is converted from the given physical size, using the given scale factor.
#[inline]
pub fn from_physical(physical: PhysicalSize<u32>, scale_factor: f64) -> Self {
Self { physical, logical: physical.to_logical(scale_factor), scale_factor }
}

/// Constructs a [`WindowSize`] from a given [`LogicalSize`] and `scale_factor`.
///
/// The [`PhysicalSize`] is converted from the given physical size, using the given scale factor.
#[inline]
pub fn from_logical(logical: LogicalSize<f64>, scale_factor: f64) -> Self {
Self { physical: logical.to_physical(scale_factor), logical, scale_factor }
}

#[inline]
pub fn to_native_size<P: Pixel>(&self) -> NativeSize<P> {
(*self).into()
}
}

impl<P: Pixel> From<WindowSize> for PhysicalSize<P> {
#[inline]
fn from(size: WindowSize) -> Self {
size.physical.cast()
}
}

impl<P: Pixel> From<WindowSize> for LogicalSize<P> {
#[inline]
fn from(size: WindowSize) -> Self {
size.logical.cast()
}
}

impl From<WindowSize> for Size {
#[inline]
fn from(value: WindowSize) -> Self {
value.to_native_size::<f64>().into()
}
}
53 changes: 24 additions & 29 deletions src/host.rs → host/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{HandlerError, WindowSize};
use std::cell::RefCell;
use crate::dpi::WindowSize;
use std::error::Error;

/// A special handler for the Window thread to wake up and call methods on the main thread.
///
/// [`WindowHandle::host_main_thread_callback`](crate::Window::host_main_thread_callback)
/// [`HostedWindow::host_main_thread_callback`](crate::HostedWindow::host_main_thread_callback)
/// should be called as a response to this.
///
/// # Platform compatibility notes
Expand All @@ -12,7 +12,7 @@ use std::cell::RefCell;
pub trait HostMainThreadCaller: Send + 'static {
/// Schedules a callback on the main thread.
///
/// [`WindowHandle::host_main_thread_callback`](crate::Window::host_main_thread_callback)
/// [`HostedWindow::host_main_thread_callback`](crate::HostedWindow::host_main_thread_callback)
/// should be called as a response to this.
///
/// # Platform compatibility notes
Expand All @@ -26,16 +26,12 @@ pub trait HostCallbacks: 'static {
/// Requests the parent window to be resized to accommodate the child window with the given new
/// size.
///
/// Note that this method may be called *during* window initialization, i.e. in the middle of
/// [`Window::create_with_host`](crate::Window::create_with_host). Implementations should still
/// properly handle requests to the host in this case.
///
/// # 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>;
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), Box<dyn Error>>;
/// Notifies the host that the child window has been destroyed for a reason outside the host's
/// control.
///
Expand All @@ -55,13 +51,12 @@ pub trait HostCallbacks: 'static {
/// It also brings the additional safety guarantee that all handlers given to this type will be
/// destroyed alongside with the window.
///
/// This guarantees callbacks cannot be fired after the [`Window`](crate::Window) is dropped.
/// (or after this [`Host`] object is dropped, if it never made it to a
/// [`Window::create_with_host`](crate::Window::create_with_host) call).
/// This guarantees callbacks cannot be fired after the window is dropped.
/// (or after this [`Host`] object is dropped, if it never made it to a window creation call).
pub struct Host {
callbacks: Option<Box<dyn HostCallbacks>>,
#[cfg(target_os = "linux")]
pub(crate) main_thread: Option<Box<dyn HostMainThreadCaller>>,
pub(crate) callbacks: Option<RefCell<Box<dyn HostCallbacks>>>,
main_thread: Option<Box<dyn HostMainThreadCaller>>,
}

impl Default for Host {
Expand All @@ -72,9 +67,6 @@ impl Default for Host {

impl Host {
/// Creates a new, empty host with no callbacks.
///
/// Calling [`Window::create_with_host`](crate::Window::create_with_host) with this is equivalent
/// to just calling [`Window::create`](crate::Window::create).
#[inline]
pub fn new() -> Self {
Self {
Expand All @@ -90,7 +82,7 @@ impl Host {
///
/// # Platform Compatibility notes
///
/// This is only useful on X11. On Window and macOS, this is a no-op.
/// This is only useful on X11. On Windows and macOS, this is a no-op.
#[inline]
pub fn with_main_thread(self, main_thread: impl HostMainThreadCaller) -> Self {
#[cfg(target_os = "linux")]
Expand All @@ -111,21 +103,24 @@ impl Host {
/// 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.callbacks = Some(Box::new(callbacks));
self
}

#[cfg(any(target_os = "macos", target_os = "windows"))]
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();
#[inline]
pub fn take_callbacks(&mut self) -> Option<Box<dyn HostCallbacks>> {
self.callbacks.take()
}

#[cfg(any(target_os = "macos", target_os = "windows"))]
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)
#[inline]
pub fn take_main_thread(&mut self) -> Option<Box<dyn HostMainThreadCaller>> {
#[cfg(target_os = "linux")]
{
self.main_thread.take()
}
#[cfg(not(target_os = "linux"))]
{
None
}
}
}
5 changes: 5 additions & 0 deletions host/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod dpi;
pub mod host;
mod window;

pub use window::HostedWindow;
Loading
Loading