Skip to main content

godspeed/
app.rs

1// SPDX-License-Identifier: Apache-2.0
2//! A program with the screen and the keyboard to itself while it runs: an `app`.
3//!
4//! An app is a utility ([`crate::program`]) that, for as long as it runs, OWNS the console: what it prints is the
5//! screen, and the keys typed are its, not the shell's. When it ends - by [`App::close`], or by dying - the
6//! console goes back to the shell, which draws a fresh prompt. That is the mechanism the built-in full-screen
7//! tools (`chaos`, `observe`) already use: the console foreground (syscall 40), released by the kernel when its
8//! holder dies, so a crashed app cannot take the keyboard with it.
9//!
10//! ```ignore
11//! use godspeed::{self as gs, ServiceContext};
12//!
13//! #[allow(unsafe_code)]
14//! #[no_mangle]
15//! pub extern "C" fn service_main(ctx: ServiceContext) -> ! {
16//!     let mut buf = [0u8; gs::program::ARGS_MAX];
17//!     let _args = gs::program::args(&ctx, &mut buf);
18//!     let app = gs::app::App::open(&ctx);
19//!     app.println("hello - press any key");
20//!     let _ = app.wait_key();
21//!     app.close(0)
22//! }
23//! ```
24//!
25//! **What it is given to do this**: the console, which the operator approves at `godspeed install` - "it reads
26//! the keyboard while it runs" - because keystrokes are commands (CLAUDE.md 6.4, SEC-2). Nothing else.
27
28use crate::ServiceContext;
29
30/// How often [`App::wait_key`] looks for a key.
31const KEY_POLL_MS: u64 = 20;
32
33/// The console, held by this program until [`App::close`].
34pub struct App<'c> {
35    ctx: &'c ServiceContext,
36}
37
38impl<'c> App<'c> {
39    /// Take the console: its keys are this program's from now on, they are not echoed, and the screen is cleared.
40    pub fn open(ctx: &'c ServiceContext) -> App<'c> {
41        ctx.claim_console_foreground();
42        ctx.console_echo(false);
43        crate::io::print(ctx, "\x1b[2J\x1b[H");
44        App { ctx }
45    }
46
47    /// Print `s` where the cursor is.
48    pub fn print(&self, s: &str) { crate::io::print(self.ctx, s); }
49
50    /// Print `s` and a newline.
51    pub fn println(&self, s: &str) { crate::io::println(self.ctx, s); }
52
53    /// Print formatted text.
54    pub fn print_fmt(&self, args: core::fmt::Arguments) { crate::io::print_fmt(self.ctx, args); }
55
56    /// Clear the screen and put the cursor at the top left.
57    pub fn clear(&self) { crate::io::print(self.ctx, "\x1b[2J\x1b[H"); }
58
59    /// A key, if one was typed: does not wait.
60    pub fn key(&self) -> Option<u8> { self.ctx.try_console_read() }
61
62    /// The next key typed: waits for it.
63    pub fn wait_key(&self) -> u8 {
64        loop {
65            if let Some(k) = self.ctx.try_console_read() { return k; }
66            crate::task::sleep_ms(self.ctx, KEY_POLL_MS);
67        }
68    }
69
70    /// Give the console back to the shell and end this program with `status`.
71    pub fn close(self, status: u32) -> ! {
72        self.ctx.console_echo(true);
73        self.ctx.release_console_foreground();
74        crate::program::exit(self.ctx, status)
75    }
76}