feat(mirada): mirada-brain — orquestador de escritorio del compositor

Desktop agnóstico de GPUI/smithay: salidas, 9 escritorios virtuales,
registro de ventanas y foco. on_event(BodyEvent) -> Vec<BrainCommand>;
DesktopAction + mapa de teclas estilo tiling WM (Super). 17 tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-20 21:00:24 +00:00
parent e736587857
commit 3e2777540f
6 changed files with 572 additions and 0 deletions
@@ -0,0 +1,95 @@
//! Acciones de escritorio y su mapa de teclas por defecto.
//!
//! Una [`DesktopAction`] es una orden de alto nivel del usuario, ya
//! desligada de la tecla concreta: el [`Desktop`](crate::Desktop) las
//! aplica sin saber qué combinación las disparó.
use mirada_layout::LayoutMode;
/// Número de escritorios virtuales que mantiene el `Desktop`.
pub const WORKSPACE_COUNT: usize = 9;
/// Una orden de escritorio de alto nivel.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DesktopAction {
/// Mueve el foco a la ventana siguiente del escritorio activo.
FocusNext,
/// Mueve el foco a la ventana anterior.
FocusPrev,
/// Adelanta la ventana enfocada en el orden de teselado.
MoveForward,
/// Atrasa la ventana enfocada en el orden de teselado.
MoveBackward,
/// Cierra la ventana enfocada (cierre ordenado).
CloseFocused,
/// Pasa al siguiente modo de teselado.
CycleLayout,
/// Fija un modo de teselado concreto.
SetLayout(LayoutMode),
/// Activa el escritorio virtual `n` (índice 0-based).
SwitchWorkspace(usize),
/// Manda la ventana enfocada al escritorio virtual `n`.
SendToWorkspace(usize),
/// Apaga el compositor.
Quit,
}
/// Mapa de teclas por defecto, estilo *tiling WM* (modificador `Super`).
///
/// Las cadenas deben coincidir literalmente con las que el Cuerpo emite
/// en [`BodyEvent::Keybind`](mirada_protocol::BodyEvent::Keybind); son
/// también las que se registran con
/// [`BrainCommand::GrabKeys`](mirada_protocol::BrainCommand::GrabKeys).
pub fn default_keymap() -> Vec<(String, DesktopAction)> {
let mut map = vec![
("Super+j".into(), DesktopAction::FocusNext),
("Super+k".into(), DesktopAction::FocusPrev),
("Super+Shift+j".into(), DesktopAction::MoveForward),
("Super+Shift+k".into(), DesktopAction::MoveBackward),
("Super+q".into(), DesktopAction::CloseFocused),
("Super+space".into(), DesktopAction::CycleLayout),
("Super+t".into(), DesktopAction::SetLayout(LayoutMode::MasterStack)),
("Super+m".into(), DesktopAction::SetLayout(LayoutMode::Monocle)),
("Super+g".into(), DesktopAction::SetLayout(LayoutMode::Grid)),
("Super+c".into(), DesktopAction::SetLayout(LayoutMode::Columns)),
("Super+Shift+e".into(), DesktopAction::Quit),
];
// Un escritorio por dígito: `Super+1`..`Super+9` lo activan,
// `Super+Shift+1`.. mandan la ventana enfocada allí.
for n in 0..WORKSPACE_COUNT {
map.push((format!("Super+{}", n + 1), DesktopAction::SwitchWorkspace(n)));
map.push((
format!("Super+Shift+{}", n + 1),
DesktopAction::SendToWorkspace(n),
));
}
map
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keymap_has_no_duplicate_bindings() {
let map = default_keymap();
let mut keys: Vec<_> = map.iter().map(|(k, _)| k.clone()).collect();
keys.sort();
let unique = keys.len();
keys.dedup();
assert_eq!(keys.len(), unique, "hay un atajo repetido");
}
#[test]
fn keymap_covers_every_virtual_workspace() {
let map = default_keymap();
for n in 0..WORKSPACE_COUNT {
assert!(map
.iter()
.any(|(_, a)| *a == DesktopAction::SwitchWorkspace(n)));
assert!(map
.iter()
.any(|(_, a)| *a == DesktopAction::SendToWorkspace(n)));
}
}
}