Crates.io | app_window |
lib.rs | app_window |
version | 0.3.0 |
created_at | 2025-06-10 02:17:25.401326+00 |
updated_at | 2025-08-29 01:10:25.953844+00 |
description | Cross-platform window library |
homepage | https://sealedabstract.com/code/app_window |
repository | https://github.com/drewcrawford/app_window |
max_upload_size | |
id | 1706595 |
size | 980,473 |
A cross-platform window management crate with async-first APIs.
app_window
provides a modern alternative to winit for creating and managing windows across
Windows, macOS, Linux, and WebAssembly. The crate's primary goal is to provide a unified,
async-first API that works seamlessly across platforms with wildly different threading
requirements.
raw-window-handle
for wgpu, OpenGL, Vulkan, etc.some_executor
First, initialize the application from your main function:
use app_window::application;
fn main() {
application::main(|| {
// Your application code here
async fn run() {
// Create windows, handle events, etc.
}
futures::executor::block_on(run());
});
}
Then create windows from any async context:
use app_window::{window::Window, coordinates::{Position, Size}};
async fn example() {
// Create a window at a specific position
let window = Window::new(
Position::new(100.0, 100.0),
Size::new(800.0, 600.0),
"My Application".to_string()
).await;
// The window stays open as long as the Window instance exists
// When dropped, the window automatically closes
}
Unlike traditional windowing libraries, app_window
uses async functions throughout.
This design elegantly handles platform differences:
// This works on any thread, on any platform
let window = Window::default().await;
// Platform-specific threading is handled internally:
// - On macOS: dispatched to main thread
// - On Windows/Linux: may run on current thread
// - On Web: runs on the single thread
Windows are tied to their Rust object lifetime. No manual cleanup needed:
{
let window = Window::default().await;
// Window is open and visible
} // Window automatically closes when dropped
The crate provides platform-specific strategies for graphics APIs:
use app_window::{WGPU_STRATEGY, WGPUStrategy};
match WGPU_STRATEGY {
WGPUStrategy::MainThread => {
// Platform requires wgpu on main thread (Web, some macOS configs)
}
WGPUStrategy::NotMainThread => {
// Platform requires wgpu NOT on main thread (Linux/Wayland)
}
WGPUStrategy::Relaxed => {
// Platform allows wgpu on any thread (Windows, most macOS)
}
_ => {
// Future-proof: handle any new strategies
// Default to the safest option
}
}
This crate abstracts over platform threading differences:
You write the same async code for all platforms:
use app_window::application;
// This works everywhere, regardless of platform requirements
let result = application::on_main_thread("my_task".to_string(), || {
// Guaranteed to run on main thread
42
}).await;
use app_window::window::Window;
match Window::fullscreen("My Game".to_string()).await {
Ok(mut window) => {
// Fullscreen window created
let surface = window.surface().await;
// Set up rendering...
}
Err(e) => eprintln!("Failed to create fullscreen window: {:?}", e),
}
use app_window::{window::Window, coordinates::Size};
let mut window = Window::default().await;
let mut surface = window.surface().await;
// Register a callback for size changes
surface.size_update(|new_size: Size| {
println!("Window resized to {}x{}", new_size.width(), new_size.height());
// Update your rendering viewport...
});
use app_window::input::{
keyboard::{Keyboard, key::KeyboardKey},
mouse::{Mouse, MOUSE_BUTTON_LEFT}
};
// Create input handlers
let keyboard = Keyboard::coalesced().await;
let mut mouse = Mouse::coalesced().await;
// Check keyboard state
if keyboard.is_pressed(KeyboardKey::Space) {
println!("Space key is pressed!");
}
// Check mouse state
if let Some(pos) = mouse.window_pos() {
println!("Mouse at ({}, {})", pos.pos_x(), pos.pos_y());
}
if mouse.button_state(MOUSE_BUTTON_LEFT) {
println!("Left mouse button is pressed!");
}
// Get scroll delta (clears after reading)
let (scroll_x, scroll_y) = mouse.load_clear_scroll_delta();
if scroll_y != 0.0 {
println!("Scrolled vertically by {}", scroll_y);
}
For wgpu integration, use the platform-specific strategy:
use app_window::{window::Window, application, WGPU_STRATEGY, WGPUStrategy};
let mut window = Window::default().await;
let surface = window.surface().await;
// Use the appropriate strategy for your platform
match WGPU_STRATEGY {
WGPUStrategy::MainThread => {
application::on_main_thread("wgpu_init".to_string(), move || {
// Create wgpu instance and surface on main thread
}).await;
}
WGPUStrategy::NotMainThread => {
// Create wgpu instance and surface on worker thread
}
WGPUStrategy::Relaxed => {
// Create wgpu instance and surface on any thread
}
_ => {
// Handle future strategies
}
}
See examples/gpu.rs
for a complete wgpu integration example.
window.surface()
The crate implements raw-window-handle
traits, enabling integration with:
examples/gpu.rs
)some_executor
- Provides interop with the some-executor
crate.wgpu
- Helper functions for creating a wgpu surface.app_input
- Created windows are configured to receive input via app_input
crate.Platform | Backend | Status | Notes |
---|---|---|---|
Windows | Win32 API | ✅ Stable | Full async support, relaxed threading |
macOS | AppKit via Swift | ✅ Stable | Main thread UI, Swift interop |
Linux | Wayland | ✅ Stable | Client-side decorations, compositor-dependent |
Web | Canvas API | ✅ Stable | Requires atomics & bulk memory features |
This project is licensed under the Mozilla Public License 2.0 (MPL-2.0).