feat(mirada): acción spawn — lanzar programas desde el compositor

Un escritorio sin forma de abrir una terminal no es usable. Ahora el
keymap puede lanzar programas:

- `mirada-protocol`: nuevo `BrainCommand::Spawn(String)`.
- `mirada-brain`: `DesktopAction::Spawn(String)` con forma textual
  `spawn:<comando>` (`Display`/`FromStr`); `Desktop::apply` la traduce
  a `BrainCommand::Spawn`. El keymap por defecto trae
  `Super+Shift+Return` → `spawn:foot`. `DesktopAction` deja de ser
  `Copy` (lleva el comando) — `Keymap::lookup` clona en vez de copiar.
- `mirada-body`: `BodyOp::Spawn(String)`.
- `mirada-compositor`: `exec_op` ejecuta el spawn con un helper
  `spawn_command` (`sh -c`, hereda `WAYLAND_DISPLAY`), que también
  recoge el lanzamiento de `MIRADA_STARTUP` — antes duplicado.

`spawn:foot --title x` también funciona desde `mirada-ctl`. Tests
nuevos del round-trip textual y del flujo atajo→comando.

Nota: un keymap.ron ya existente no recibe el atajo nuevo; hay que
añadir la línea a mano o borrar el archivo para regenerarlo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 03:59:37 +00:00
parent 90bffec3f1
commit fb3091d995
10 changed files with 76 additions and 16 deletions
+6 -5
View File
@@ -78,11 +78,12 @@ WAYLAND_DISPLAY=wayland-1 foot # o weston-terminal, alacritty, …
```
Las ventanas se teselan solas. El teclado, con la ventana del compositor
enfocada, maneja el escritorio con atajos `Super+…`: foco `Super+j/k`,
los 7 layouts en `Super+t/m/g/c/r/d/s` (o ciclar con `Super+space`), área
maestra `Super+h/l`, `nmaster` `Super+,/.`, promover a maestra
`Super+Return`, escritorios `Super+1..9`, cerrar `Super+q`. Cierra la
ventana del compositor para salir.
enfocada, maneja el escritorio con atajos `Super+…`: lanzar una terminal
`Super+Shift+Return`, foco `Super+j/k`, los 7 layouts en
`Super+t/m/g/c/r/d/s` (o ciclar con `Super+space`), área maestra
`Super+h/l`, `nmaster` `Super+,/.`, promover a maestra `Super+Return`,
escritorios `Super+1..9`, cerrar `Super+q`. Cierra la ventana del
compositor para salir.
## Atajos de teclado
@@ -609,12 +609,7 @@ pub fn run() -> Result<(), Box<dyn Error>> {
// App de arranque: si `MIRADA_STARTUP` trae un comando, se lanza como
// hijo (hereda `WAYLAND_DISPLAY`) — cómodo para probar sin saltar de VT.
if let Ok(cmd) = std::env::var("MIRADA_STARTUP") {
if !cmd.trim().is_empty() {
match std::process::Command::new("sh").arg("-c").arg(&cmd).spawn() {
Ok(child) => println!(" app de arranque lanzada (pid {}): {cmd}", child.id()),
Err(e) => eprintln!(" no pude lanzar «{cmd}»: {e}"),
}
}
crate::spawn_command(&cmd);
}
// 8 · El bucle `calloop`: VBlank, teclado, clientes y un timer.
+16
View File
@@ -262,6 +262,7 @@ impl App {
}
BodyOp::SetGrabs(keys) => self.grabs = keys,
BodyOp::SetCursor(_) => {}
BodyOp::Spawn(cmd) => spawn_command(&cmd),
BodyOp::Shutdown => self.running = false,
}
}
@@ -596,6 +597,21 @@ fn surface_px_size(w: &ManagedWindow) -> Option<(i32, i32)> {
.map(|s| (s.w, s.h))
}
/// Lanza un comando como proceso hijo, vía `sh -c`. El hijo hereda el
/// entorno —`WAYLAND_DISPLAY` incluido—, así que el cliente que abra se
/// conecta a este compositor. Lo usan la acción `spawn:…` del keymap y
/// la variable `MIRADA_STARTUP`.
fn spawn_command(cmd: &str) {
let cmd = cmd.trim();
if cmd.is_empty() {
return;
}
match std::process::Command::new("sh").arg("-c").arg(cmd).spawn() {
Ok(child) => println!("mirada-compositor · lanzado (pid {}): {cmd}", child.id()),
Err(e) => eprintln!("mirada-compositor · no pude lanzar «{cmd}»: {e}"),
}
}
/// Carga las reglas de ventana del usuario, o ninguna si no hay archivo.
fn load_user_rules() -> Rules {
match Rules::default_path() {