Running apps
run
Run a view in one window on Wayland, rendered by Impeller, until the window closes.
crownui::runuse crownui::prelude::*;
fn main() -> Result<(), crownui::RunError> {
crownui::run(
component(|cx| {
let count = cx.signal(0);
button(bind(move |rt| format!("Clicked {} times", count.get(rt))))
.on_click(move |cx, _| count.update(cx, |count| *count += 1))
}),
AppConfig::new("Hello").size(480, 320),
)
}pub fn run(root: impl View, config: AppConfig) -> Result<(), RunError>run opens one window, builds root into it once and runs the event loop until the window closes.
It uses the Wayland platform, the Impeller renderer (Vulkan first, GLES when Vulkan fails) and the
taffy layout engine. It blocks the calling thread for the life of the app.
run is run_windows(vec![(root.boxed(), config)]). Everything on the
run_windows page applies to it.
Parameters
The view to show. Usually a component(|cx| ..) whose closure creates the app's state and returns
the view tree. The closure runs once.
How the window starts: title, size, material, layer role, MCP and so on. See AppConfig.
Returns
The window closed normally, either because the user closed it or because the app called
cx.close_window().
The app failed to start or a window failed while running. An error that stops a running window
is also logged through tracing.
RunError
pub enum RunError {
Graphics(GraphicsError),
Platform(String),
Thread(std::io::Error),
}RunError is at crownui::RunError. It implements std::error::Error through thiserror, so ?
converts it into anyhow::Error.
| Variant | When |
|---|---|
Graphics | The graphics backend failed to initialize. |
Platform | The platform failed, for example when no Wayland compositor is reachable. |
Thread | The OS refused to spawn a thread, such as the render thread. |
Cargo features
run exists only when both the wayland and impeller features are on. Both are default
features. With either one off, the function is not compiled, and you drive the app yourself with
crownui::ext::Runner and the platform and backend of your choice.
Examples
With anyhow
Binaries use anyhow. RunError converts with ?:
use anyhow::{Context, Result};
use crownui::prelude::*;
fn main() -> Result<()> {
crownui::run(app(), AppConfig::new("Notes").app_id("org.example.notes"))
.context("the notes window stopped")
}
fn app() -> impl View {
component(|_cx| text("Notes"))
}Letting agents drive the app
crownui::run(app(), AppConfig::new("Notes").mcp(true))Forcing GLES
Set CROWNUI_RENDERER=gles in the environment before you start the app. run then skips Vulkan.
CROWNUI_RENDERER=gles cargo runWhy run blocks and owns main
Wayland delivers events on one connection, and CrownUI keeps all UI state on the thread that reads
it. So the thread that calls run becomes the UI thread for good. It runs your handlers, flushes
signals, computes layout and shapes text. Each window also gets its own render thread that owns the
GPU and runs animations. The two threads only talk through channels.
The cost is that run never hands the thread back while the app is open. Work that has to happen
elsewhere goes through cx.spawn, which needs the tokio feature. A task spawned while the first
view is built waits until the executor starts, so it is safe to call from a component. Do not start
a plain std::thread inside a component closure: run builds the first view before it selects the
Vulkan driver, so that thread would already be running when the environment changes.