Files
brahman/crates/modules/mirada/mirada-brain/examples/headless-ctl.rs
T
sergio 8821d34bd5 feat(mirada): 3 layouts nuevos + redimensionar el área maestra
mirada-layout pasa de 4 a 7 modos de teselado, todos intercambiables
por el API (SetLayout / CycleLayout / mirada-ctl layout <modo>):

- Rows: filas horizontales de igual alto (complemento de Columns).
- Spiral: espiral de Fibonacci — cada ventana parte por la mitad el
  espacio restante, alternando el sentido del corte.
- CenteredMaster: maestra centrada + pila a ambos lados (monitores
  anchos).

LayoutMode::ALL + next() definen el ciclo. Añade dos acciones,
GrowMaster/ShrinkMaster (Super+l / Super+h), que ajustan master_ratio
en caliente — ese parámetro existía pero no había forma de tocarlo.

Cableado completo: tile(), cycle, slugs Display/FromStr, keymap por
defecto (Super+r/d/s), HUD de mirada, mirada-ctl actions. El ejemplo
headless-ctl ahora imprime la geometría para verificar los layouts.

mirada-layout 22->26 tests, mirada-brain 37->39.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:37:16 +00:00

96 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Un Cerebro *headless* para probar el API de control sin gráficos.
//!
//! Abre el socket de `mirada-ctl`, arranca un [`Desktop`] con una pantalla
//! y unas ventanas de muestra, y atiende peticiones en bucle, imprimiendo
//! el estado tras cada una. Útil para ejercitar `mirada-ctl` en modo
//! desatendido.
//!
//! ```sh
//! cargo run -p mirada-brain --example headless-ctl # terminal 1
//! mirada-ctl windows # terminal 2
//! mirada-ctl focus-next
//! mirada-ctl focus-window 2
//! ```
use std::thread;
use std::time::Duration;
use mirada_brain::ctl::{self, CtlReply, CtlRequest, CtlServer};
use mirada_brain::{BodyEvent, BrainCommand, Desktop};
fn main() {
let path = ctl::default_socket_path();
let server = match CtlServer::bind(&path) {
Ok(s) => s,
Err(e) => {
eprintln!("Cerebro headless · no pude abrir el control: {e}");
std::process::exit(1);
}
};
eprintln!("Cerebro headless · control en {}", path.display());
// Una pantalla y tres ventanas de muestra.
let mut desktop = Desktop::new();
desktop.on_event(BodyEvent::OutputAdded { id: 0, width: 1920, height: 1080 });
for id in 1..=3 {
desktop.on_event(BodyEvent::WindowOpened {
id,
app_id: format!("org.brahman.app{id}"),
title: format!("ventana {id}"),
});
}
print_state(&desktop);
eprintln!(" esperando a mirada-ctl …");
loop {
if let Some(mut conn) = server.poll() {
if let Ok(Some(req)) = conn.read_request() {
let reply = match req {
CtlRequest::Do(action) => {
eprintln!("· {action}");
for cmd in desktop.apply(action) {
match cmd {
// La geometría que el Cerebro mandaría al Cuerpo.
BrainCommand::Place(places) => {
for p in places {
eprintln!(
" win {}{:>5}×{:<4} @ ({:>5},{:>4}){}",
p.id,
p.rect.w,
p.rect.h,
p.rect.x,
p.rect.y,
if p.focused { " *" } else { "" },
);
}
}
// Sin Cuerpo: simulamos nosotros el cierre.
BrainCommand::Close(id) | BrainCommand::Kill(id) => {
desktop.on_event(BodyEvent::WindowClosed { id });
}
_ => {}
}
}
print_state(&desktop);
CtlReply::Ok
}
CtlRequest::ListWindows => CtlReply::Windows(desktop.window_lines()),
};
let _ = conn.reply(&reply);
}
}
thread::sleep(Duration::from_millis(16));
}
}
fn print_state(d: &Desktop) {
let ws = d.active_workspace();
eprintln!(
" escritorio {} · {:?} (maestra {:.0}%) · foco {:?}",
d.active_index() + 1,
ws.params().mode,
ws.params().master_ratio * 100.0,
d.focused_window(),
);
}