gioser

tawasuyu/brahman

Rust 296TOML 105JSON 14Markdown 73751 Sutichaykuna

⎇ 13d2ae71fb8d56689e3906ab11012dbdb6f4b0df crates/apps/mirada-ctl/src/main.rs Rust · 143 siq'ikuna · 5.3 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat(mirada): scratchpad — ventana desplegable estilo terminal quake 13d2ae71f · sergio ·

1//! `mirada-ctl` — el control del compositor carmen por línea de comandos.
2//!
3//! Al estilo de `swaymsg` / `hyprctl`: dispara una acción de escritorio o
4//! consulta el estado, hablando con el Cerebro por su socket de control
5//! ([`mirada_brain::ctl`]). El Cerebro es la app `mirada`, o
6//! `mirada-compositor` cuando lleva el Cerebro embebido.
7//!
8//! ```sh
9//! mirada-ctl focus-next            # cambia el foco
10//! mirada-ctl focus-window 5        # enfoca una ventana concreta
11//! mirada-ctl workspace 3           # va al escritorio 3
12//! mirada-ctl layout grid           # fija el modo de teselado
13//! mirada-ctl windows               # lista las ventanas
14//! mirada-ctl actions               # lista las acciones
15//! ```
16
17use std::process::ExitCode;
18
19use mirada_brain::ctl::{self, CtlReply, CtlRequest, WindowLine};
20use mirada_brain::DesktopAction;
21
22fn main() -> ExitCode {
23    let args: Vec<String> = std::env::args().skip(1).collect();
24    match run(&args) {
25        Ok(()) => ExitCode::SUCCESS,
26        Err(msg) => {
27            eprintln!("mirada-ctl: {msg}");
28            ExitCode::FAILURE
29        }
30    }
31}
32
33fn run(args: &[String]) -> Result<(), String> {
34    match args.first().map(String::as_str) {
35        None | Some("-h" | "--help" | "help") => {
36            print_help();
37            Ok(())
38        }
39        Some("actions") => {
40            print_actions();
41            Ok(())
42        }
43        Some("windows") => match request(CtlRequest::ListWindows)? {
44            CtlReply::Windows(ws) => {
45                print_windows(&ws);
46                Ok(())
47            }
48            CtlReply::Error(e) => Err(e),
49            CtlReply::Ok => Err("respuesta inesperada del Cerebro".into()),
50        },
51        // Todo lo demás es una acción. `focus-window 5` y `workspace 3`
52        // se unen con `:` a la forma canónica (`focus-window:5`).
53        Some(_) => {
54            let spec = args.join(":");
55            let action: DesktopAction = spec
56                .parse()
57                .map_err(|e| format!("{e}\n  lista de acciones:  mirada-ctl actions"))?;
58            match request(CtlRequest::Do(action))? {
59                CtlReply::Ok => Ok(()),
60                CtlReply::Error(e) => Err(e),
61                CtlReply::Windows(_) => Err("respuesta inesperada del Cerebro".into()),
62            }
63        }
64    }
65}
66
67/// Manda una petición al Cerebro y devuelve su respuesta.
68fn request(req: CtlRequest) -> Result<CtlReply, String> {
69    let path = ctl::default_socket_path();
70    ctl::send_request(&path, &req).map_err(|e| {
71        format!(
72            "no pude hablar con el Cerebro en {} ({e})\n  \
73             ¿está corriendo `mirada` o `mirada-compositor`?",
74            path.display()
75        )
76    })
77}
78
79/// Imprime la lista de ventanas, marcando la enfocada con `*`.
80fn print_windows(windows: &[WindowLine]) {
81    if windows.is_empty() {
82        println!("(no hay ventanas)");
83        return;
84    }
85    for w in windows {
86        let mark = if w.focused { '*' } else { ' ' };
87        // El escritorio 0 es el scratchpad (ventana guardada).
88        let ws = if w.workspace == 0 {
89            "scratch".to_string()
90        } else {
91            w.workspace.to_string()
92        };
93        println!("{mark} id {:<4} esc {:<7} {:<24} {}", w.id, ws, w.app_id, w.title);
94    }
95}
96
97fn print_help() {
98    println!(
99        "mirada-ctl — control del compositor carmen\n\
100         \n\
101         USO:\n  \
102           mirada-ctl <acción>      aplica una acción de escritorio\n  \
103           mirada-ctl windows       lista las ventanas\n  \
104           mirada-ctl actions       lista las acciones disponibles\n\
105         \n\
106         EJEMPLOS:\n  \
107           mirada-ctl focus-next\n  \
108           mirada-ctl focus-window 5\n  \
109           mirada-ctl workspace 3\n  \
110           mirada-ctl layout grid"
111    );
112}
113
114fn print_actions() {
115    // Cadena multilínea literal: la indentación de cada línea es la que
116    // se imprime (el `\` tras la comilla se come sólo el primer salto).
117    print!(
118        "\
119Acciones de mirada-ctl:
120  focus-next                 mueve el foco a la siguiente ventana
121  focus-prev                 mueve el foco a la anterior
122  focus-window <id>          enfoca la ventana <id>  (ver: mirada-ctl windows)
123  move-forward               adelanta la ventana enfocada en el teselado
124  move-backward              la atrasa
125  close-focused              cierra la ventana enfocada
126  toggle-float               alterna flotante / teselada la enfocada
127  toggle-fullscreen          alterna pantalla completa en la enfocada
128  send-to-scratchpad         guarda la ventana enfocada en el scratchpad
129  toggle-scratchpad          invoca u oculta la ventana del scratchpad
130  cycle-layout               pasa al siguiente modo de teselado
131  layout <modo>              master-stack · centered-master · spiral
132                             grid · columns · rows · monocle
133  grow-master                agranda el área de la ventana maestra
134  shrink-master              la encoge
135  inc-master / dec-master    nº de ventanas en el área maestra (nmaster)
136  promote-to-master          la ventana enfocada al puesto maestro
137  workspace <n>              activa el escritorio n (1..9)
138  send-to-workspace <n>      manda la enfocada al escritorio n
139  focus-output-next          pasa el foco al siguiente monitor
140  quit                       apaga el compositor
141"
142    );
143}