8821d34bd5
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>
96 lines
3.6 KiB
Rust
96 lines
3.6 KiB
Rust
//! 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(),
|
||
);
|
||
}
|