1#![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
35fn 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 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}