Overview of baseview
masterwinapi (Windows), cocoa (macOS), and xcb (Linux). The library is designed to be lightweight and stay out of the developer's way, allowing focus on UI implementation.repository·master·Indexed 18 days ago
https://github.com/rustaudio/baseviewA low-level, platform-independent windowing abstraction layer designed for creating audio plugin user interfaces. It abstracts platform-specific implementations such as winapi (Windows), cocoa (macOS), and xcb (Linux) to provide a lightweight API for window management, event handling, and input.
winapi (Windows), cocoa (macOS), and xcb (Linux). The library is designed to be lightweight and stay out of the developer's way, allowing focus on UI implementation.The WindowSize struct provides a unified way to handle window dimensions across different pixel densities. It contains:
physical: PhysicalSize<u32>logical: LogicalSize<f64>scale_factor: f64If the platform fails to provide an accurate scaling factor, you can suggest one using suggest_fallback_scale_factor(scale_factor: f64).
Xft.dpi is set.let size = window.size();
println!("Logical size: {:?}, Physical size: {:?}", size.logical, size.physical);
// Suggest a fallback if needed
window.suggest_fallback_scale_factor(2.0)?;PlatformHandle is a lightweight, cheaply cloneable, and thread-safe (Send + Sync) handle to a window and its display connection.
Use PlatformHandle when you need to pass window information to other threads. It implements both HasWindowHandle and HasDisplayHandle from the raw-window-handle crate.
window_handle() will return HandleError::Unavailable.Send and Sync, platform limitations may apply. On some platforms, calling window_handle() from a thread other than the main thread may return HandleError::Unavailable even if the window is still alive.PlatformHandle from a WindowContext using the platform_handle() method.// Obtaining a PlatformHandle from WindowContext
let handle = window_context.platform_handle();
// The handle can be sent to another thread
std::thread::spawn(move || {
if let Ok(window_handle) = handle.window_handle() {
// Use the window handle
}
});In baseview, a Window manages its own lifecycle. Unlike other libraries where you might manage the event loop externally, the Window owns its associated WindowHandler and Host types.
Window::create or Window::create_with_host. This creates the window but does not show it. You must call .show() or .run_until_closed() to make it visible.Window handle will always destroy the window and its associated handler/host. You can also call .close() to explicitly destroy it.WindowHandler requests a close (e.g., via WindowContext::request_close).WindowContext.Window methods return Result or become no-ops if the window has already been closed. Use Window::is_open() to check the current state.The baseview crate provides a cross-platform abstraction for window management, event handling, and input. The primary entry points for users are:
WindowContext: Manages the platform-specific state and handles.Window: Represents an individual window instance.WindowHandler: A trait that must be implemented to process window events (keyboard, mouse, etc.).PlatformHandle: Provides access to underlying platform resources.Event: An enum representing various input and window events.MouseCursor: Represents the state of the mouse pointer.Clipboard: Provides access to system clipboard operations.Settings: Configuration for window and platform behavior.To allow a baseview window to interact with its parent application (the host), you must implement the HostCallbacks trait. This is used to handle requests from the window to resize itself or to notify the host when the window is destroyed.
Implement the following methods:
request_resize(new_size: WindowSize) -> Result<(), HandlerError>: Called when the window wants to change its size. Return Ok(()) to accept or an error to deny/cancel the resize.destroyed(): Called when the window is destroyed due to external factors (e.g., lost display connection or handler crash). The host should use this signal to close its parent window.impl HostCallbacks for MyHost {
fn request_resize(&mut self, new_size: WindowSize) -> Result<(), HandlerError> {
// Handle resize logic here
Ok(())
}
fn destroyed(&mut self) {
// Clean up host resources
}
}To handle window events, rendering, and resizing in baseview, you must implement the WindowHandler trait. This trait provides three primary callbacks:
on_frame(): Called when the window needs to draw a new frame. If this returns an Err(HandlerError), the window is considered unable to render and will be closed.resized(new_size: WindowSize): Called when the window size changes. If this returns an error, baseview will attempt to revert the window and its parent to the previous size as a best effort.on_event(event: Event): Called when a window event occurs. It returns an EventStatus to indicate how the event should be handled.impl WindowHandler for MyHandler {
fn on_frame(&self) -> core::result::Result<(), HandlerError> {
// Your rendering logic here
Ok(())
}
fn resized(&self, new_size: WindowSize) -> core::result::Result<(), HandlerError> {
// Handle resize logic
Ok(())
}
fn on_event(&self, event: Event) -> EventStatus {
// Handle input events
EventStatus::Consumed
}
}The WindowEvent enum allows you to respond to changes in the window's state. Use these to manage focus or handle requests to close the application.
Variants:
Focused: The window has gained focus.Unfocused: The window has lost focus.WillClose: The window is requesting to close.match window_event {
WindowEvent::Focused => {
// Enable UI interactions
}
WindowEvent::Unfocused => {
// Pause certain animations or interactions
}
WindowEvent::WillClose => {
// Perform cleanup before exiting
}
}On Linux (X11), windowing operations often need to be coordinated with the main thread. If you are building a host for baseview, implement the HostMainThreadCaller trait to allow the window thread to schedule callbacks on the main thread.
Note: This is a no-op on Windows and macOS, as their windows already run on the main thread. On X11, call_main_thread() should be implemented to trigger the corresponding WindowHandle::host_main_thread_callback.
impl HostMainThreadCaller for MyMainThreadCaller {
fn call_main_thread(&mut self) {
// Implementation to wake up/signal the main thread
}
}When implementing a WindowHandler, you may encounter errors that are not directly related to Baseview's internal windowing logic. To return these errors from your handler, wrap them in a HandlerError.
HandlerError is a wrapper around a boxed dyn std::error::Error. It does not implement the std::error::Error trait itself, but it is designed to be easily converted into a Baseview Error using the ? operator or From implementation. This allows you to propagate your own application-specific errors up through the Baseview event loop.
Key methods:
HandlerError::from_boxed(error: Box<dyn std::error::Error>): Creates a HandlerError from a boxed error..source(): Returns the underlying error..into_inner(): Consumes the HandlerError and returns the underlying Box<dyn std::error::Error>.// Example of using HandlerError in a WindowHandler implementation
impl WindowHandler for MyHandler {
fn handle_event(&mut self, event: Event) -> Result<(), HandlerError> {
if let Some(data) = self.get_data() {
// Convert a standard error into a HandlerError using the From implementation
let custom_err = std::io::Error::new(std::io::ErrorKind::Other, "Something went wrong");
return Err(custom_err.into());
}
Ok(())
}
}The Host struct is used to pass callbacks and platform-specific threading requirements to a baseview window. You can build a Host using a builder-like pattern before passing it to Window::create_with_host.
Host::new(): Creates an empty host with no callbacks..with_callbacks(callbacks): Attaches an implementation of HostCallbacks..with_main_thread(main_thread): Attaches an implementation of HostMainThreadCaller (required for X11/Linux).Safety Guarantee: All handlers provided to a Host are guaranteed to be destroyed alongside the window, preventing callbacks from firing after the Window is dropped.
let host = Host::new()
.with_callbacks(MyHostCallbacks::new())
.with_main_thread(MyMainThreadCaller::new());
// Use the host to create a window
let window = Window::create_with_host(host, ...);If you are building a plugin or a system where the window is hosted by another application, use Window::create_with_host. This allows you to pass an optional Host containing callbacks for the hosting system.
let settings = WindowSettings::default();
let host = Host::default(); // Or your custom host
let window = Window::create_with_host(settings, |context| {
Ok(MyHandler::new(context))
}, host)?;
window.show()?;