Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

App Lifecycle

Guido applications can programmatically quit or restart. App::run() returns an ExitReason so the caller knows why the loop exited: Quit, Restart, or Error(PlatformError) when the platform layer fails (no Wayland session, a compositor without layer-shell support such as GNOME, a lost connection, or no GPU to draw a surface with) — these conditions are reported instead of panicking.

Quitting

Call quit_app() to request a clean shutdown:

extern crate guido;
fn main() {
use guido::prelude::*;

container()
    .padding([8.0, 16.0])
    .background(Color::rgb(0.3, 0.3, 0.4))
    .when_hovered(|s| s.lighter(0.1))
    .on_click(|| quit_app())
    .child(text("Quit"))
;
}

The current App::run() loop exits and returns ExitReason::Quit.

Outliving the Last Surface

Closing the last surface also ends the loop with ExitReason::Quit — whether the app closed it, the compositor did, or its monitor was unplugged. A resident program that only sometimes has something to show — a polkit agent, a notification daemon, an on-screen display — turns that off with quit_on_last_surface(false):

extern crate guido;
use guido::prelude::*;

fn main() {
    App::new().quit_on_last_surface(false).run(|_app| {
        let asked = create_signal(false);
        let mut dialog: Option<SurfaceHandle> = None;
        create_effect(move || {
            if asked.get() {
                dialog = Some(spawn_surface(SurfaceConfig::new(), || text("Authenticate")));
            } else if let Some(open) = dialog.take() {
                open.close();
            }
        });
        // Something outside the UI — a D-Bus request, say — writes `asked`.
    });
}

With it off, the app idles when its last surface closes, exactly as one that has not spawned a surface yet does, and ends through quit_app() or a lost connection.

An app holds a GPU device only while it has something to draw. The device is made when the first surface needs one, and let go 30 seconds after the last one closes — soon enough that a resident program between windows costs no GPU memory, late enough that a dialog closed and reopened straight away does not wait for a new one.

Restarting

Call restart_app() to request a restart. The loop exits and returns ExitReason::Restart, letting the caller re-create the app:

extern crate guido;
use guido::prelude::*;
fn main() {
container()
    .on_click(|| restart_app())
    .child(text("Restart"))
;
}

Restart Loop

Use a loop in main() to support restart:

extern crate guido;
fn build_ui() -> Container { container() }
use guido::prelude::*;

fn main() {
    loop {
        let reason = App::new().run(|app| {
            app.add_surface(
                SurfaceConfig::new()
                    .height(32)
                    .anchor(Anchor::TOP | Anchor::LEFT | Anchor::RIGHT)
                    .layer(Layer::Top)
                    .namespace("my-bar")
                    .background_color(Color::rgb(0.1, 0.1, 0.15)),
                || build_ui(),
            );
        });

        match reason {
            ExitReason::Quit => break,
            ExitReason::Restart => continue,
            ExitReason::Error(e) => {
                eprintln!("guido could not run: {e}");
                break;
            }
        }
    }
}

This is useful for reloading configuration, switching themes, or resetting application state.

Calling from Background Tasks

Both quit_app() and restart_app() are Send — they work from any thread, including background services:

// not compiled: the task body is tokio's, and the book's samples are
// compiled without it.
extern crate guido;
use guido::prelude::*;
fn main() {
create_task(move |ctx| async move {
    loop {
        tokio::select! {
            _ = watch_config_file() => {
                // Config changed — trigger restart
                restart_app();
                break;
            }
            _ = tokio::time::sleep(Duration::from_millis(50)) => {
                if !ctx.is_running() { break; }
            }
        }
    }
});
;
}

API Reference

ExitReason

// not compiled: a listing of a type the crate owns — a copy of it declared
// here would compile without checking the original.
pub enum ExitReason {
    /// Normal exit: `quit_app()`, or the last surface closing.
    Quit,
    /// Restart requested. The caller should re-create `App` and run again.
    Restart,
    /// The platform layer failed: no Wayland session, a compositor without
    /// layer shell, a lost connection, or no GPU to draw a surface with.
    Error(PlatformError),
}

Functions

// not compiled: a signature listing — these declarations have no bodies.
/// Request a clean application quit.
/// App::run() will return ExitReason::Quit.
pub fn quit_app();

/// Request a clean application restart.
/// App::run() will return ExitReason::Restart.
/// Call from any thread — uses an atomic + ping to wake the event loop.
pub fn restart_app();

App::run

// not compiled: a signature listing — these declarations have no bodies.
impl App {
    /// Run the application. Returns the reason the loop exited.
    pub fn run(self, setup: impl FnOnce(&mut Self)) -> ExitReason;
}