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;
}