Skip to main content

app_window/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3/*!
4<!-- The authoritative version of this document is the crate documentation in src/lib.rs. Edit there, then mirror the changes here. -->
5
6# app_window
7
8A cross-platform window crate with an async-first API.
9
10![logo](https://github.com/drewcrawford/app_window/raw/main/art/logo.png)
11
12`app_window` creates windows and rendering surfaces on Windows, macOS, Linux, and
13WebAssembly. It is deliberately small: you get a window, a surface that plugs into
14anything that consumes `raw-window-handle` (wgpu, OpenGL, Vulkan), cross-platform
15keyboard and mouse input, and a main-thread executor. You bring the renderer and
16the rest of your application.
17
18The crate exists because platforms disagree about threads. macOS insists UI runs
19on the main thread. Wayland compositors behave best when rendering stays *off*
20the main thread. In the browser, the main thread is the event loop and blocking
21it is fatal. Most windowing libraries hand this problem to you: they own an event
22loop, call you back on a thread of their choosing, and your architecture bends
23around theirs. `app_window` inverts that. It takes ownership of the main thread
24once, at startup, and from then on every API is an async function you can call
25from any thread. The crate routes each call to whatever thread the current
26platform requires; you write straight-line code.
27
28# What it is — and isn't
29
30`app_window` provides:
31
32- **Windows** — created from any thread; a window closes when its `Window` value drops
33- **Surfaces** — implement the `raw-window-handle` traits, so wgpu, glutin, ash, and friends plug in directly
34- **Input** — keyboard (physical keys, layout-independent) and mouse, unified across platforms
35- **Main-thread dispatch** — `application::on_main_thread`, a main-thread executor integrated with the native event loop, and `MainThreadCell` for values pinned to that thread
36
37It is **not** a GUI toolkit. There are no widgets, no layout engine, no text
38rendering. If you want buttons out of the box, look at egui, iced, or Slint
39instead. The intended pairing is `app_window` + wgpu + your own rendering code —
40a game, a visualization, a custom-drawn UI.
41
42# Where it fits in the ecosystem
43
44```text
45┌─────────────────────────────────────────────────┐
46│                your application                 │
47├─────────────────────────────────────────────────┤
48│     wgpu / OpenGL / Vulkan / your renderer      │
49│            (via raw-window-handle)              │
50├─────────────────────────────────────────────────┤
51│    app_window: window · surface · input ·       │
52│             main-thread executor                │
53├──────────┬────────────┬───────────┬─────────────┤
54│  Win32   │   AppKit   │  Wayland  │   Canvas    │
55│(Windows) │(macOS, via │  (Linux)  │   (Web)     │
56│          │   Swift)   │           │             │
57└──────────┴────────────┴───────────┴─────────────┘
58```
59
60Three integration points matter:
61
62- **raw-window-handle** is the Rust ecosystem's standard interface between
63  windowing and graphics. `Surface` implements it, so any renderer that consumes
64  it works without `app_window` knowing anything about it: wgpu (recommended; see
65  `examples/gpu.rs`), OpenGL via glutin, Vulkan via ash, Metal via metal-rs,
66  DirectX via windows-rs.
67- **Async runtimes.** The crate is executor-agnostic through
68  [`some_executor`](https://sealedabstract.com/code/some_executor). At startup it
69  installs its main-thread executor via those traits, and it interoperates with
70  any runtime that speaks the same interface. There is no tokio dependency and no
71  runtime lock-in.
72- **WebAssembly.** The browser backend is built on
73  [`wasm_lite`](https://github.com/drewcrawford/wasm_lite) — hand-written DOM
74  bindings — rather than web-sys/wasm-bindgen, and targets shared-memory
75  threading (atomics + bulk memory), so the same multithreaded architecture you
76  use natively runs in the browser. wgpu still uses wasm-bindgen internally; a
77  one-line patch (see the WASM + wgpu section below) lets the two coexist.
78
79The macOS backend is written in Swift and doubles as a Swift package
80(`SwiftAppWindow/`), so the same windowing layer is callable from Swift.
81
82# Alternatives
83
84The Rust windowing space has a clear incumbent and several specialists. What
85follows is an honest comparison; `app_window` is not the right choice for every
86project.
87
88**[winit](https://crates.io/crates/winit)** is the de facto standard and the
89default answer for most projects — Bevy, eframe, and iced all sit on top of it.
90It supports a much larger platform matrix than `app_window`: X11 as well as
91Wayland, Android, iOS. The trade is architectural: winit owns your event loop and
92calls back into your `ApplicationHandler`, and each platform's thread-affinity
93rules — what must happen on the main thread, what must not — are yours to know
94and manage. Choose winit when you need its platform breadth or its ecosystem;
95choose `app_window` when you'd rather write async code and let the library carry
96the threading rules.
97
98**[tao](https://crates.io/crates/tao)** is Tauri's fork of winit, extended with
99app menus and a system tray, and GTK-backed on Linux. It's the natural choice if
100you're building around a webview.
101
102**[sdl2](https://crates.io/crates/sdl2)** / **[sdl3](https://crates.io/crates/sdl3)**
103bind the C SDL library: windowing plus audio, game controllers, haptics, and
104more, with decades of portability behind it. You accept a C dependency and a
105polling-style API. A good fit for games that want batteries included.
106
107**[glfw](https://crates.io/crates/glfw)** binds the C GLFW library: minimal,
108OpenGL-oriented, desktop-focused.
109
110**[miniquad](https://crates.io/crates/miniquad)** (and macroquad above it)
111bundles windowing with its own graphics abstraction and produces very small wasm
112builds — but you use its rendering API rather than wgpu.
113
114**[minifb](https://crates.io/crates/minifb)** puts a CPU framebuffer in a
115window. If "give me pixels" is the whole requirement, it's the simplest thing
116that works.
117
118**egui/eframe, iced, Slint, gtk4-rs, fltk-rs** are toolkits, not windowing
119crates: they bundle windowing (usually winit) and give you widgets. Compare them
120against `app_window` plus your renderer, not against `app_window` alone.
121
122| Crate      | API model                    | Linux         | Mobile | Web                         | Scope                          |
123|------------|------------------------------|---------------|--------|-----------------------------|--------------------------------|
124| app_window | async, call from any thread  | Wayland       | —      | wasm, shared-memory threads | window + surface + input       |
125| winit      | event loop, callbacks        | Wayland + X11 | yes    | wasm-bindgen                | window + surface + input       |
126| tao        | event loop, callbacks        | GTK           | yes    | —                           | winit fork + menus/tray        |
127| sdl2/sdl3  | C library, polling           | Wayland + X11 | yes    | emscripten                  | windowing + audio + controllers|
128| glfw       | C library, polling           | Wayland + X11 | —      | —                           | OpenGL-focused windowing       |
129| miniquad   | event callbacks              | X11 + Wayland | yes    | tiny wasm                   | window + built-in renderer     |
130
131In short: reach for `app_window` for the async API, the unified threading model,
132first-class shared-memory wasm, and a native Wayland backend. Pass on it if you
133need X11 or mobile, if a framework you use requires winit, or if you want the
134largest possible community behind your windowing layer.
135
136# Quick Start
137
138First, initialize the application from your main function:
139
140```no_run
141# // no_run because: application::main() must be called from the actual main thread, which is not available in doctests
142use app_window::application;
143fn main() {
144    application::main(|| {
145        // Your application code here
146        async fn run() {
147            // Create windows, handle events, etc.
148        }
149        futures::executor::block_on(run());
150    });
151}
152#[allow(clippy::needless_doctest_main)]
153```
154
155Then create windows from any async context:
156
157```
158# async fn example() {
159use app_window::{window::Window, coordinates::{Position, Size}};
160
161// Create a window at a specific position
162let window = Window::new(
163    Position::new(100.0, 100.0),
164    Size::new(800.0, 600.0),
165    "My Application".to_string()
166).await;
167
168// The window stays open as long as the Window instance exists
169// When dropped, the window automatically closes
170# }
171```
172
173Windows are tied to their Rust value: drop the `Window` and the window closes.
174There is no separate close/destroy step to forget.
175
176# Threading Model
177
178Every public API is async and callable from any thread; the crate dispatches to
179the right place per platform:
180
181```
182# async fn example() {
183use app_window::window::Window;
184
185// This works on any thread, on any platform
186let window = Window::default().await;
187
188// Platform-specific threading is handled internally:
189// - On macOS: dispatched to main thread
190// - On Windows/Linux: may run on current thread
191// - On Web: runs on the single thread
192# }
193```
194
195Under the hood:
196
197- **macOS**: All UI operations dispatched to main thread via GCD
198- **Windows**: UI operations can run on any thread
199- **Linux (Wayland)**: Compositor-dependent, handled per-connection
200- **WebAssembly**: Single-threaded, operations run directly
201
202When you need the main thread explicitly, ask for it:
203
204```
205# async fn example() {
206use app_window::application;
207
208// This works everywhere, regardless of platform requirements
209let result = application::on_main_thread("my_task".to_string(), || {
210    // Guaranteed to run on main thread
211    42
212}).await;
213# }
214```
215
216## wgpu threading strategies
217
218Platforms also disagree about which thread may drive the GPU. The crate encodes
219those rules in two constants so your rendering setup can branch on them instead
220of hardcoding per-OS knowledge:
221
222- `WGPU_STRATEGY` — where general wgpu work should happen
223- `WGPU_SURFACE_STRATEGY` — where surfaces may be created and configured
224  (notably: macOS is `Relaxed` for general wgpu use but `MainThread` for
225  surface creation)
226
227```
228use app_window::{WGPU_STRATEGY, WGPUStrategy};
229
230match WGPU_STRATEGY {
231    WGPUStrategy::MainThread => {
232        // Platform requires wgpu on main thread (Web, some macOS configs)
233    }
234    WGPUStrategy::NotMainThread => {
235        // Platform requires wgpu NOT on main thread (Linux/Wayland)
236    }
237    WGPUStrategy::Relaxed => {
238        // Platform allows wgpu on any thread (Windows, most macOS)
239    }
240    _ => {
241        // Future-proof: handle any new strategies
242        // Default to the safest option
243    }
244}
245```
246
247# Examples
248
249## Creating a fullscreen window
250
251```
252# async fn example() {
253use app_window::window::Window;
254
255match Window::fullscreen("My Game".to_string()).await {
256    Ok(mut window) => {
257        // Fullscreen window created
258        let surface = window.surface().await;
259        // Set up rendering...
260    }
261    Err(e) => eprintln!("Failed to create fullscreen window: {:?}", e),
262}
263# }
264```
265
266## Handling window resize
267
268```
269# async fn example() {
270use app_window::{window::Window, coordinates::Size};
271
272let mut window = Window::default().await;
273let mut surface = window.surface().await;
274
275// Register a callback for size changes
276surface.size_update(|new_size: Size| {
277    println!("Window resized to {}x{}", new_size.width(), new_size.height());
278    // Update your rendering viewport...
279});
280# }
281```
282
283## Input handling
284
285Keyboard input reports physical keys — the key labeled W on a QWERTY board,
286regardless of active layout. That makes it a fit for game controls and
287shortcuts, not for text entry. Mappings cover alphanumeric and symbol keys,
288F1–F24, the numeric keypad, media and navigation keys, modifiers, and
289international layouts (JIS, ISO).
290
291```
292# async fn example() {
293use app_window::input::{
294    keyboard::{Keyboard, key::KeyboardKey},
295    mouse::{Mouse, MOUSE_BUTTON_LEFT}
296};
297
298// Create input handlers
299let keyboard = Keyboard::coalesced().await;
300let mut mouse = Mouse::coalesced().await;
301
302if keyboard.is_pressed(KeyboardKey::Space) {
303    println!("Space key is pressed!");
304}
305
306if keyboard.is_pressed(KeyboardKey::W) {
307    println!("W key pressed - move forward!");
308}
309
310// Check mouse state
311if let Some(pos) = mouse.window_pos() {
312    println!("Mouse at ({}, {})", pos.pos_x(), pos.pos_y());
313}
314
315if mouse.button_state(MOUSE_BUTTON_LEFT) {
316    println!("Left mouse button is pressed!");
317}
318
319// Get scroll delta (clears after reading)
320let (scroll_x, scroll_y) = mouse.load_clear_scroll_delta();
321if scroll_y != 0.0 {
322    println!("Scrolled vertically by {}", scroll_y);
323}
324# }
325```
326
327## Integrating with wgpu
328
329For wgpu integration, use the platform-specific strategy:
330
331```no_run
332# // no_run because: full wgpu example requires graphics setup beyond scope of doctest
333# async fn example() -> Result<(), Box<dyn std::error::Error>> {
334use app_window::{window::Window, application, WGPU_STRATEGY, WGPUStrategy};
335
336let mut window = Window::default().await;
337let surface = window.surface().await;
338
339// Use the appropriate strategy for your platform
340match WGPU_STRATEGY {
341    WGPUStrategy::MainThread => {
342        application::on_main_thread("wgpu_init".to_string(), move || {
343            // Create wgpu instance and surface on main thread
344        }).await;
345    }
346    WGPUStrategy::NotMainThread => {
347        // Create wgpu instance and surface on worker thread
348    }
349    WGPUStrategy::Relaxed => {
350        // Create wgpu instance and surface on any thread
351    }
352    _ => {
353        // Handle future strategies
354    }
355}
356# Ok(())
357# }
358```
359
360See `examples/gpu.rs` for a complete wgpu integration example.
361
362## WASM + wgpu
363
364`wgpu` uses the wasm-bindgen API on WebAssembly, while this crate's browser
365backend uses `wasm_lite`. To build an application that combines both, add the
366wasm_lite compatibility patch to the application manifest:
367
368```toml
369[patch.crates-io]
370wasm-bindgen = { git = "https://github.com/drewcrawford/wasm_lite", rev = "f47bf4178d666e83017abe056f07bb20d33c14cd" }
371```
372
373The patch belongs in the final application's `Cargo.toml`; Cargo does not
374inherit patches from dependencies. Patch only `wasm-bindgen`—do not replace
375`wasm_lite` or `wasm_lite_std` with git or path dependencies, because the
376compatibility crate now resolves those released runtimes from crates.io. The
377application also needs the released `wasm_lite_cli` runner and the shared-memory
378WASM linker settings shown in this repository's `.cargo/config.toml`.
379
380# Platform Support
381
382| Platform | Backend | Status | Notes |
383|----------|---------|--------|-------|
384| Windows  | Win32 API | ✅ Stable | Full async support, relaxed threading |
385| macOS    | AppKit via Swift | ✅ Stable | Main thread UI, Swift interop |
386| Linux    | Wayland | ✅ Stable | Client-side decorations, compositor-dependent |
387| Web      | Canvas API | ✅ Stable | Requires atomics & bulk memory features |
388
389Linux support is Wayland-only; there is no X11 backend. Requires Rust 1.95+
390(2024 edition).
391
392# Cargo Features
393
394The default feature set is empty; everything above works with no features
395enabled. The optional extras are diagnostic:
396
397- `exfiltrate` — keeps a bounded registry of the windows this process has
398  created and exposes it, along with main-thread state, through the
399  `exfiltrate` crate's `snapshot` command for inspecting live processes. Off by
400  default because it costs a lock and a record per window.
401- `logwise-diagnostic`, `logwise-forensic`, `logwise-performance` — enable
402  progressively more detailed logging through the `logwise` facade. Operational
403  failures — a window that won't open, a frozen main thread — are always
404  compiled in and need no feature.
405
406# Development
407
408`scripts/check_all` runs the full gate: formatting, native and wasm checks,
409clippy, tests, and docs, with warnings as errors. Per-target variants live in
410`scripts/native/` and `scripts/wasm32/`; wasm tests run under the `wasm_lite`
411runner on nightly.
412
413## License
414
415This project is licensed under the Mozilla Public License 2.0 (MPL-2.0).
416
417*/
418
419/// Window creation and management.
420///
421/// This module provides the [`window::Window`] type for creating and managing windows
422/// across different platforms. Windows can be created from any thread after the
423/// application has been initialized.
424///
425/// # Example
426/// ```
427/// # async fn example() {
428/// use app_window::{window::Window, coordinates::{Position, Size}};
429///
430/// // Create a window with specific position and size
431/// let window = Window::new(
432///     Position::new(100.0, 100.0),
433///     Size::new(800.0, 600.0),
434///     "My Application".to_string()
435/// ).await;
436/// # }
437/// ```
438pub mod window;
439
440/// Application lifecycle and main thread management.
441///
442/// This module provides the entry point for app_window applications and utilities
443/// for executing code on the main thread. The [`application::main`] function must
444/// be called once from the first thread to initialize the platform event loop.
445///
446/// Key functions:
447/// - [`application::main`] - Initialize the application and event loop
448/// - [`application::on_main_thread`] - Execute async code on the main thread
449/// - [`application::submit_to_main_thread`] - Fire-and-forget main thread tasks
450///
451/// Most functions in this module will panic if [`application::main`] hasn't been called yet.
452/// [`application::main`] is called at the start of your program.
453///
454/// # Example
455/// ```no_run
456/// # // no_run because: application::main() must be called from the actual main thread, which is not available in doctests
457/// use app_window::application;
458///
459/// fn main() {
460///     application::main(|| {
461///         // Application code here
462///     });
463/// }
464/// ```
465pub mod application;
466
467mod sys;
468
469/// Coordinate types for window positioning and sizing.
470///
471/// This module provides [`coordinates::Position`] and [`coordinates::Size`] types
472/// for working with window coordinates. All values are in logical pixels, which
473/// may differ from physical pixels on high-DPI displays.
474///
475/// # Example
476/// ```
477/// use app_window::coordinates::{Position, Size};
478///
479/// let pos = Position::new(100.0, 200.0);
480/// assert_eq!(pos.x(), 100.0);
481/// assert_eq!(pos.y(), 200.0);
482///
483/// let size = Size::new(800.0, 600.0);
484/// assert_eq!(size.width(), 800.0);
485/// assert_eq!(size.height(), 600.0);
486/// ```
487pub mod coordinates;
488
489/// Rendering surface abstraction.
490///
491/// This module provides the [`surface::Surface`] type, which represents a drawable
492/// area within a window. Surfaces integrate with graphics APIs like wgpu through
493/// the `raw-window-handle` trait implementations.
494///
495/// # Example
496/// ```
497/// # async fn example() {
498/// # use app_window::{application, window::Window};
499/// let mut window = Window::default().await;
500/// let surface = window.surface().await;
501///
502/// // Get size and scale factor
503/// let (size, scale) = surface.size_scale().await;
504///
505/// // Get handles for graphics API integration
506/// let window_handle = surface.window_handle();
507/// let display_handle = surface.display_handle();
508/// # }
509/// ```
510pub mod surface;
511
512/// Cross-platform mouse and keyboard input handling.
513///
514/// This module provides keyboard and mouse input functionality that integrates
515/// with app_window. It handles platform-specific input events and provides
516/// a unified API across Windows, macOS, Linux, and WebAssembly.
517///
518/// # Keyboard Input
519///
520/// The keyboard module uses physical key mappings rather than logical characters.
521/// This means [`input::keyboard::key::KeyboardKey`] represents actual physical keys on the
522/// keyboard (e.g., the key labeled 'A' on QWERTY), independent of keyboard layout.
523/// This approach is ideal for game controls and shortcuts but not for text input.
524///
525/// Comprehensive key mappings include:
526/// - Standard alphanumeric keys (A-Z, 0-9) and symbol keys (brackets, quotes, etc.)
527/// - Function keys (F1-F24) with extensive coverage up to F24
528/// - Numeric keypad keys with full support (0-9, operators, decimal, Enter, Clear/Num Lock)
529/// - Media control keys (Play/Pause, Stop, Volume Up/Down, Mute, Previous/Next Track)
530/// - Navigation keys (arrows, Home, End, Page Up/Down, Insert, Delete)
531/// - Modifier keys (Shift, Control, Option/Alt, Command/Windows, Function)
532/// - International keyboard layouts (Japanese JIS keys: Yen, Kana, Eisu, Convert; ISO Section key)
533/// - Browser and application launcher keys
534/// - Editing keys (Undo, Copy, Cut, Paste, Find, Select)
535///
536/// On macOS, debug windows are available via `input::keyboard::macos::debug_window_show()`
537/// and `input::keyboard::macos::debug_window_hide()` to inspect real-time raw keyboard events,
538/// useful for debugging keyboard handling and understanding platform-specific key codes.
539///
540/// # Example
541/// ```
542/// # async fn example() {
543/// use app_window::input::{keyboard::Keyboard, mouse::Mouse};
544///
545/// // Create input handlers
546/// let keyboard = Keyboard::coalesced().await;
547/// let mouse = Mouse::coalesced().await;
548/// # }
549/// ```
550pub mod input;
551
552/// Main thread executor for async operations.
553///
554/// This module provides utilities for running futures on the main thread, which is
555/// required for UI operations on many platforms. The executor integrates with the
556/// native event loop to process both async tasks and platform events.
557///
558/// # Example
559/// ```
560/// #[cfg(target_arch = "wasm32")] {
561/// }
562/// use app_window::test_support::doctest_main;
563/// use some_executor::task::{Configuration, Task};
564///
565/// doctest_main(|| {
566///     Task::without_notifications(
567///         "doctest".to_string(),
568///         Configuration::default(),
569///         async {
570///             use app_window::executor;
571///
572///             async fn my_async_function() -> i32 { 42 }
573///
574///             // Run an async function on the main thread
575///             let result = executor::on_main_thread_async(
576///                 "ex".to_owned(),
577///                 my_async_function()
578///             ).await;
579///             assert_eq!(result, 42);
580///         },
581///     ).spawn_static_current();
582/// });
583/// ```
584pub mod executor;
585
586/// Integration with the `some_executor` crate.
587///
588/// This module provides [`some_executor::MainThreadExecutor`], which implements
589/// the `SomeExecutor` and `SomeLocalExecutor` traits from the `some_executor` crate.
590/// This allows the main thread executor to be used with any library that supports
591/// the `some_executor` abstraction.
592///
593/// When [`application::main`] is called, a `MainThreadExecutor` is automatically
594/// installed as the thread-local and thread-static executor, making it available
595/// to any code using `some_executor`'s convenience functions.
596pub mod some_executor;
597
598/// Thread-safe cell for main-thread-only values.
599///
600/// `MainThreadCell<T>` is a thread-safe container that allows `T` to be shared across threads
601/// while ensuring all access to the inner value happens on the main thread.
602/// This is useful for platform-specific resources that have main-thread requirements.
603///
604/// # Example
605/// ```
606/// # async fn example() {
607/// use app_window::main_thread_cell::MainThreadCell;
608///
609/// // Create a cell on the main thread. From another thread, use
610/// // MainThreadCell::new_on_main_thread instead.
611/// let cell = MainThreadCell::new(42);
612///
613/// // Access from main thread directly
614/// if app_window::application::is_main_thread() {
615///     let guard = cell.lock();
616///     println!("Value: {}", *guard);
617/// }
618///
619/// // Access from any thread via async dispatch
620/// let result = cell.with(|value| {
621///     // This runs on the main thread
622///     *value * 2
623/// }).await;
624/// assert_eq!(result, 84);
625/// # }
626/// ```
627pub mod main_thread_cell;
628
629/// Main-thread liveness accounting, always compiled in.
630pub mod instrument;
631
632/// A bounded record of this process's windows.
633#[cfg(feature = "exfiltrate")]
634pub mod registry;
635
636/// Window and main-thread state as exfiltrate `snapshot` subsystems.
637#[cfg(feature = "exfiltrate")]
638pub mod exfiltrate_provider;
639
640/// Test support utilities for working with the main thread.
641///
642/// This module provides utilities for writing tests (both doctests and integration tests)
643/// that need to interact with the main thread. Since many platforms require UI operations
644/// to run on the main thread, these utilities help set up and tear down the appropriate
645/// environment for testing.
646///
647/// # Available utilities
648///
649/// - `doctest_main` - For writing doctests that need main thread access
650/// - `integration_test_harness` - For integration tests with custom harness
651///
652/// # Example
653///
654/// For doctests that use async window operations:
655/// ```
656/// #[cfg(target_arch = "wasm32")] {
657/// }
658/// use app_window::test_support::doctest_main;
659///
660/// doctest_main(|| {
661///     // Your test code here - has access to main thread
662/// });
663/// ```
664///
665/// See the module documentation for more details and integration test examples.
666pub mod test_support;
667
668/// Describes the preferred strategy for interacting with wgpu on different platforms.
669///
670/// Different platforms have different requirements for which thread can access
671/// graphics APIs. This enum encodes those platform-specific requirements to help
672/// applications use wgpu correctly.
673///
674/// # Example
675///
676/// ```
677/// use app_window::{WGPU_STRATEGY, WGPUStrategy};
678///
679/// // Check the platform's wgpu threading requirements
680/// match WGPU_STRATEGY {
681///     WGPUStrategy::MainThread => {
682///         println!("wgpu must be accessed from the main thread");
683///     }
684///     WGPUStrategy::NotMainThread => {
685///         println!("wgpu must NOT be accessed from the main thread");
686///     }
687///     WGPUStrategy::Relaxed => {
688///         println!("wgpu can be accessed from any thread");
689///     }
690///     _ => {
691///         println!("Unknown strategy - using default behavior");
692///     }
693/// }
694/// ```
695#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
696#[non_exhaustive]
697pub enum WGPUStrategy {
698    /// The main thread should be used to access wgpu.
699    ///
700    /// This is required on WebAssembly and some macOS configurations where
701    /// the graphics context must be created and used from the main thread.
702    MainThread,
703
704    /// The main thread should NOT be used to access wgpu.
705    ///
706    /// This is required on Linux with Wayland, where blocking the main thread
707    /// with graphics operations can cause compositor issues.
708    NotMainThread,
709
710    /// On this platform, wgpu types are sendable and can be used from any thread.
711    ///
712    /// This is the case on Windows and most macOS configurations, providing
713    /// maximum flexibility for application architecture.
714    Relaxed,
715}
716
717/// Displays an alert dialog with the given message.
718///
719/// This function displays a modal alert dialog to the user. The behavior is platform-specific:
720///
721/// - **WebAssembly**: Uses the browser's native `window.alert()` function
722/// - **macOS, Windows, Linux**: Not yet implemented (will panic with `todo!`)
723///
724/// # Platform-specific behavior
725///
726/// On WebAssembly, this function will block execution until the user dismisses the alert dialog.
727/// The function automatically dispatches to the main thread as required by the platform.
728///
729/// # Example
730///
731/// ```no_run
732/// # // no_run because: alert() requires application::main() to be called first, which is not available in doctests
733/// # async fn example() {
734/// use app_window::alert;
735///
736/// alert("Hello, world!".to_string()).await;
737/// # }
738/// ```
739///
740/// # Panics
741///
742/// Currently panics with `todo!` on macOS, Windows, and Linux platforms.
743pub async fn alert(message: String) {
744    sys::alert(message).await
745}
746
747/// The preferred strategy for interacting with wgpu on the current platform.
748///
749/// This constant provides the platform-specific threading requirements for wgpu
750/// operations. Applications should check this value to determine the correct
751/// thread to use for wgpu initialization and rendering.
752///
753/// # Platform Values
754///
755/// - **Linux**: `NotMainThread` - wgpu should be accessed from a worker thread
756/// - **Windows**: `Relaxed` - wgpu can be accessed from any thread
757/// - **macOS**: `Relaxed` - wgpu can be accessed from any thread
758/// - **WebAssembly**: `MainThread` - wgpu must be accessed from the main thread
759#[cfg(target_os = "linux")]
760pub const WGPU_STRATEGY: WGPUStrategy = WGPUStrategy::NotMainThread;
761
762/// The preferred strategy for interacting with wgpu on the current platform.
763///
764/// See [`WGPU_STRATEGY`] documentation for details.
765#[cfg(target_os = "windows")]
766pub const WGPU_STRATEGY: WGPUStrategy = WGPUStrategy::Relaxed;
767
768/// The preferred strategy for interacting with wgpu on the current platform.
769///
770/// See [`WGPU_STRATEGY`] documentation for details.
771#[cfg(target_os = "macos")]
772pub const WGPU_STRATEGY: WGPUStrategy = WGPUStrategy::Relaxed;
773
774/// The preferred strategy for interacting with wgpu on the current platform.
775///
776/// See [`WGPU_STRATEGY`] documentation for details.
777#[cfg(target_arch = "wasm32")]
778pub const WGPU_STRATEGY: WGPUStrategy = WGPUStrategy::MainThread;
779
780/// The preferred strategy for interacting with wgpu surfaces on the current platform.
781///
782/// This constant provides the platform-specific threading requirements for wgpu
783/// surface creation and configuration. Some platforms have different requirements
784/// for surface operations compared to general wgpu operations.
785///
786/// # Platform Values
787///
788/// - **Linux**: `NotMainThread` - surfaces should be created from a worker thread
789/// - **Windows**: `Relaxed` - surfaces can be created from any thread
790/// - **macOS**: `MainThread` - surfaces must be created from the main thread
791/// - **WebAssembly**: `MainThread` - surfaces must be created from the main thread
792///
793/// # Difference from `WGPU_STRATEGY`
794///
795/// While `WGPU_STRATEGY` applies to general wgpu operations, `WGPU_SURFACE_STRATEGY`
796/// specifically applies to surface creation and configuration. On macOS, for example,
797/// general wgpu operations are `Relaxed` but surface operations require `MainThread`.
798#[cfg(target_os = "linux")]
799pub const WGPU_SURFACE_STRATEGY: WGPUStrategy = WGPUStrategy::NotMainThread;
800
801/// The preferred strategy for interacting with wgpu surfaces on the current platform.
802///
803/// See [`WGPU_SURFACE_STRATEGY`] documentation for details.
804#[cfg(target_os = "windows")]
805pub const WGPU_SURFACE_STRATEGY: WGPUStrategy = WGPUStrategy::Relaxed;
806
807/// The preferred strategy for interacting with wgpu surfaces on the current platform.
808///
809/// See [`WGPU_SURFACE_STRATEGY`] documentation for details.
810#[cfg(target_os = "macos")]
811pub const WGPU_SURFACE_STRATEGY: WGPUStrategy = WGPUStrategy::MainThread;
812
813/// The preferred strategy for interacting with wgpu surfaces on the current platform.
814///
815/// See [`WGPU_SURFACE_STRATEGY`] documentation for details.
816#[cfg(target_arch = "wasm32")]
817pub const WGPU_SURFACE_STRATEGY: WGPUStrategy = WGPUStrategy::MainThread;