Skip to main content

scxtui/
main.rs

1// SPDX-License-Identifier: GPL-2.0
2
3#![deny(unsafe_code)]
4
5mod app;
6mod backend;
7mod kernel;
8mod logs;
9mod model;
10mod system;
11mod ui;
12mod watch;
13
14use anyhow::{Result, bail};
15
16use app::App;
17use backend::scxcli::ScxcliBackend;
18
19const USAGE: &str = "\
20scxtui - TUI for sched_ext schedulers, through scxcli and scx.service
21
22USAGE:
23    scxtui
24
25OPTIONS:
26    -h, --help       Show this help
27    -V, --version    Show the version
28
29scxtui runs as a normal user. Starting a scheduler goes through
30`pkexec scxcli start`, which with the shipped policy needs no password;
31starting one with options asks for an administrator's. For scripts, use
32scxcli directly.
33";
34
35/// scxctl-style commands the removed `--cli` used to take, mapped to
36/// the scxcli command that replaces each. Printed instead of acting, for
37/// one release, so an old script fails with a pointer rather than a
38/// parse error.
39fn cli_pointer(args: &[String]) -> String {
40    let command = args.first().map(String::as_str).unwrap_or("");
41    let replacement = match command {
42        "get" => "scxcli get [--json]",
43        "list" | "modes" => "scxcli list [all] [--json]",
44        "start" | "switch" => {
45            "scxcli start SCHED, or scxcli run SCHED [ARGS...] for a scheduler with options"
46        }
47        "stop" => "scxcli stop (keeps the selection), or scxcli start default",
48        "restart" => {
49            "scxcli start SCHED (starting restarts the unit), or systemctl restart scx.service"
50        }
51        "restore" => "scxcli start SCHED",
52        _ => "scxcli --help",
53    };
54    format!(
55        "scxtui --cli was removed; schedulers are managed by scxcli. For `{}` use: {replacement}",
56        args.join(" ")
57    )
58}
59
60fn main() -> Result<()> {
61    let args: Vec<String> = std::env::args().skip(1).collect();
62    match args.first().map(String::as_str) {
63        None => {}
64        Some("-h" | "--help") => {
65            print!("{USAGE}");
66            return Ok(());
67        }
68        Some("-V" | "--version") => {
69            println!("scxtui {}", env!("CARGO_PKG_VERSION"));
70            return Ok(());
71        }
72        Some("--cli") => {
73            eprintln!("{}", cli_pointer(&args[1..]));
74            std::process::exit(2);
75        }
76        Some(other) if other.starts_with("--export-profiles") => {
77            bail!(
78                "--export-profiles was removed; the catalog is /usr/share/scx/catalog.json (or \
79/etc/scx/catalog.json), and `scxcli list --json` prints it resolved"
80            );
81        }
82        Some(other) => bail!("unknown argument: {other}\n\n{USAGE}"),
83    }
84
85    // Backend resolution happens before entering raw mode, so failures
86    // print as normal error messages instead of garbling the terminal.
87    let backend = ScxcliBackend::connect()?;
88    let warning = backend.policy_mismatch();
89    let mut app = App::new(Box::new(backend));
90    if let Some(text) = warning {
91        app.warn(&text);
92    }
93
94    let terminal = ratatui::init();
95    let result = app.run(terminal);
96    ratatui::restore();
97    result
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn the_cli_pointer_names_the_replacement() {
106        let args = |list: &[&str]| list.iter().map(|s| (*s).to_owned()).collect::<Vec<_>>();
107        assert!(cli_pointer(&args(&["get", "--json"])).contains("scxcli get"));
108        assert!(cli_pointer(&args(&["switch", "-s", "lavd"])).contains("scxcli start SCHED"));
109        assert!(cli_pointer(&args(&["stop"])).contains("start default"));
110        assert!(cli_pointer(&args(&[])).contains("scxcli --help"));
111    }
112}