# 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:
```rust
# 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)`:
```rust,no_run
# 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:
```rust
# 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:
```rust,no_run
# 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:
```rust,ignore
# // 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
```rust,ignore
# // 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
```rust,ignore
# // 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
```rust,ignore
# // 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;
}
```