raw-window-handle: the handshake
A tiny crate that lets any window talk to any renderer.
raw-window-handle is one tiny crate with one job: describe the native object behind a window in a form any library can accept. On macOS that is an NSView, on Windows an HWND, on Wayland a wl_surface, and in a browser a canvas. It has no dependencies of its own to speak of, and yet 789 crates depend on it.
use book_error::Result;
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
/// What kind of native object is behind this window?
/// Asking can fail, for example if the window has not been created yet.
fn describe(window: &impl HasWindowHandle) -> Result<&'static str> {
let handle = window.window_handle()?;
Ok(match handle.as_raw() {
RawWindowHandle::AppKit(_) => "NSView (macOS)",
RawWindowHandle::Win32(_) => "HWND (Windows)",
RawWindowHandle::Xlib(_) | RawWindowHandle::Xcb(_) => "X11 window",
RawWindowHandle::Wayland(_) => "wl_surface (Wayland)",
RawWindowHandle::Web(_) => "canvas element (browser)",
_ => "something else",
})
}
fn main() {
// winit's Window implements HasWindowHandle, so this just works:
let _ = |w: &winit::window::Window| describe(w);
} The real value is in the traits. A renderer does not ask for a winit Window; it asks for something that implements HasWindowHandle and HasDisplayHandle. That is the entire contract between a window library and a graphics library.
use book_error::Result;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
/// This bound is the entire contract between a window library and a renderer.
/// wgpu, glutin, softbuffer and Slint's backends all ask for exactly this.
trait RenderTarget: HasWindowHandle + HasDisplayHandle {}
impl<T: HasWindowHandle + HasDisplayHandle> RenderTarget for T {}
fn create_surface(target: &dyn RenderTarget) -> Result<()> {
// Either handle can be unavailable, so both are fallible.
let _window = target.window_handle()?;
let _display = target.display_handle()?;
// ...hand the raw handles to Metal / Vulkan / D3D / WebGPU here...
Ok(())
}
fn main() {
// winit's Window satisfies it, so does tao's, and so does any test double.
let _: fn(&winit::window::Window) -> Result<()> = |w| create_surface(w);
} It is why you can mix and match: winit's window works with wgpu, glutin, and every toolkit above them, and Tauri's and tao's windows expose the same traits. In our lockfile survey it appears in every native framework we built except Makepad and Druid, which have their own platform layers. One wrinkle: the Dioxus desktop and Floem lockfiles resolved both 0.5.2 and 0.6.2, because some of their dependencies have not moved to the newer trait versions yet.