⎇ 13d2ae71fb8d56689e3906ab11012dbdb6f4b0df
crates/apps/mirada-ctl/src/main.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat(mirada): scratchpad — ventana desplegable estilo terminal quake
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}