Running apps

run

Run a view in one window on Wayland, rendered by Impeller, until the window closes.

FNcrownui::run
main.rs
use 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),
    )
}
Signature
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

rootimpl Viewrequired

The view to show. Usually a component(|cx| ..) whose closure creates the app's state and returns the view tree. The closure runs once.

configAppConfigrequired

How the window starts: title, size, material, layer role, MCP and so on. See AppConfig.

Returns

Ok(())()

The window closed normally, either because the user closed it or because the app called cx.close_window().

Err(RunError)RunError

The app failed to start or a window failed while running. An error that stops a running window is also logged through tracing.

RunError

Signature
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.

VariantWhen
GraphicsThe graphics backend failed to initialize.
PlatformThe platform failed, for example when no Wayland compositor is reachable.
ThreadThe 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 ?:

main.rs
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 run

Why 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.