feat(mirada): multi-monitor real — cada salida tesela su escritorio

El Desktop deja de teselar sólo la salida primaria. Cada Output muestra
un escritorio virtual distinto y relayout() las tesela todas en un solo
Place que cubre todas las pantallas.

- Output { id, rect, workspace }; focused_output reemplaza al índice
  global active. active_index() = el escritorio de la salida enfocada.
- OutputAdded asigna el primer escritorio libre; OutputRemoved deja sus
  ventanas en su escritorio y reajusta el foco. reflow_outputs() las
  recoloca en fila.
- SwitchWorkspace actúa sobre la salida enfocada; si el escritorio
  pedido ya lo muestra otra salida, las intercambia (invariante: un
  escritorio se ve en una salida como mucho).
- DesktopAction::FocusOutputNext (Super+o) mueve el foco entre
  monitores. El foco del teclado es único — relayout() lo unifica a la
  ventana enfocada de la salida enfocada.

Verificado end-to-end con headless-ctl (ahora 2 salidas).
mirada-brain 52->58 tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 01:18:12 +00:00
parent be61ddb6eb
commit 799dcef22e
9 changed files with 258 additions and 69 deletions
+1
View File
@@ -131,6 +131,7 @@ Acciones de mirada-ctl:
promote-to-master la ventana enfocada al puesto maestro
workspace <n> activa el escritorio n (1..9)
send-to-workspace <n> manda la enfocada al escritorio n
focus-output-next pasa el foco al siguiente monitor
quit apaga el compositor
"
);
+2
View File
@@ -25,6 +25,7 @@
//! j / k foco siguiente/anterior , / . nmaster /+
//! Shift+j / k mueve la enfocada 1..9 ir a escritorio
//! Enter promueve a maestra Ctrl+1..9 enviar a escritorio
//! o siguiente monitor
//! ```
//!
//! Los pips de escritorio y las ventanas del lienzo son **clicables**, y
@@ -299,6 +300,7 @@ impl Mirada {
"s" => self.act(DesktopAction::SetLayout(LayoutMode::Spiral)),
"h" => self.act(DesktopAction::ShrinkMaster),
"l" => self.act(DesktopAction::GrowMaster),
"o" => self.act(DesktopAction::FocusOutputNext),
"enter" => self.act(DesktopAction::PromoteToMaster),
"," => self.act(DesktopAction::IncMaster),
"." => self.act(DesktopAction::DecMaster),