Files
brahman/crates/modules/mirada/SDD.md
T
sergio 4719f7c9f9 feat(mirada): ventanas flotantes — toggle-float
Una ventana puede salir del teselado y flotar: conserva su propio
rectángulo y se compone por encima de las teseladas.

- Workspace guarda las flotantes en un mapa aparte; layout() tesela
  sólo las no-flotantes y añade las flotantes al final (orden de
  pintado). set_floating / is_floating.
- WindowPlacement y BodyOp::Configure llevan floating: bool. BodyState
  detecta el cambio de floating como cualquier otro reconfigure.
- DesktopAction::ToggleFloat (Super+f): saca la enfocada a un
  rectángulo centrado al 60 % de la pantalla, o la devuelve al teselado.
  En Monocle, una flotante sigue visible.
- mirada-compositor ordena las flotantes al frente de la lista
  front-to-back de elementos → se pintan encima.
- HUD de mirada marca las flotantes; mirada-ctl toggle-float.

Verificado end-to-end con headless-ctl. mirada-layout 30->32,
mirada-protocol 9->10, mirada-body 13->14, mirada-brain 41->42.

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

174 lines
9.6 KiB
Markdown

# modules/mirada/ — Compositor Wayland (carmen)
**Propósito.** Un compositor Wayland teselante. La decisión de diseño
central es partirlo en dos procesos, el **Cerebro** y el **Cuerpo**:
- **El Cuerpo** (`mirada-compositor`, sobre `smithay`) habla Wayland con
los clientes, posee el hardware (DRM/GPU/libinput) y compone las
superficies reales. Los píxeles nunca salen de él — composición
*zero-copy*.
- **El Cerebro** (la app `mirada`, GPUI) decide *dónde* va cada ventana
—pura aritmética de rectángulos— y orquesta el escritorio: layouts,
atajos, focos, escritorios virtuales.
Los dos hablan por un socket Unix con un contrato mínimo de enums. Así,
toda la lógica espacial es agnóstica de Wayland y se prueba sin un
servidor gráfico; el Cuerpo queda reducido a "habla el protocolo y
ejecuta operaciones de geometría".
## Crates
| crate | tipo | rol |
| ------------------ | -------- | ---------------------------------------------------------------------- |
| `mirada-layout` | lib | Motor de teselado: `Rect`, modos de layout, `Workspace` (ventanas, foco) |
| `mirada-protocol` | lib | Contrato Cerebro↔Cuerpo: `BrainCommand`/`BodyEvent` + marco de cable |
| `mirada-brain` | lib | Orquestador del escritorio: `Desktop`, eventos→comandos, atajos |
| `mirada-link` | lib | Transporte: el socket Unix con hilo lector + canal |
| `mirada-body` | lib | Contabilidad del Cuerpo: `BodyState`, traduce comandos a `BodyOp` |
| `mirada` (app) | bin/GPUI | El Cerebro: ventana que tesela el escritorio y manda geometría |
| `mirada-compositor`| bin/smithay | El Cuerpo: compositor Wayland real (backend `winit`, anidado) |
| `mirada-ctl` (app) | bin/CLI | Control externo del Cerebro (estilo `swaymsg`): acciones y consultas |
## Flujo
```text
mirada-layout ─► mirada-protocol ─► mirada-brain ─► [app mirada · Cerebro]
│ │
│ mirada-link
│ │
└────► mirada-body ─► [mirada-compositor · Cuerpo]
```
- El Cuerpo reporta hardware/clientes con `BodyEvent` (salida conectada,
ventana abierta, atajo pulsado…).
- El Cerebro (`Desktop::on_event`) recalcula y emite `BrainCommand`
(`Place` con la geometría completa, `Close`, `GrabKeys`…).
- El Cuerpo (`BodyState::apply`) traduce cada comando a `BodyOp`
concretas y sólo emite lo que de verdad cambia.
## Detalle por crate
- **`mirada-layout`** — `Rect` + `split` (reparto exacto de píxeles),
`LayoutMode` con 7 modos (`MasterStack`, `CenteredMaster`, `Spiral`
—espiral de Fibonacci—, `Grid`, `Columns`, `Rows`, `Monocle`) y
`LayoutMode::next()` para el ciclo, `Workspace` con foco cíclico,
reordenado y `promote_focused`. `LayoutParams` lleva `master_ratio` y
`master_count` (`nmaster`); *smart gaps* (una sola ventana va a
sangre). Determinista.
- **`mirada-protocol`** — `WindowPlacement`, los enums `BrainCommand` y
`BodyEvent`, el marco `postcard` con prefijo `u32` LE
(`write_frame`/`read_frame`, guard `MAX_FRAME`) y el puente
`placements(&Workspace, Rect)`.
- **`mirada-brain`** — `Desktop`: salidas, 9 escritorios virtuales,
registro de ventanas. `on_event(BodyEvent) -> Vec<BrainCommand>`;
`DesktopAction` + `Keymap` configurable. `set_keymap` lo cambia en
caliente y devuelve el `GrabKeys` a reenviar.
- **`mirada-link`** — `Link<Out,In>` sobre socket Unix; hilo lector de
fondo + canal `mpsc` para sondeo no bloqueante. `BrainLink`/`BodyLink`,
`connected_pair` (socketpair), `connect`/`listen` por ruta.
- **`mirada-body`** — `BodyState`: salidas + superficies; `apply` traduce
`BrainCommand``BodyOp` (idempotente), los mutadores del backend
devuelven los `BodyEvent` a mandar. Ejemplo `headless`: un Cuerpo sin
gráficos guiado por stdin para ejercitar el bucle entero.
- **`mirada` (app)** — envuelve `Desktop` y lo pinta (barra de
escritorios + modo + foco, lienzo teselado). Con `MIRADA_SOCKET`
conecta a un Cuerpo; sin él corre en **simulación** (ventanas
sintéticas, teclado de la propia ventana). Pips de escritorio y
ventanas clicables.
- **`mirada-ctl` (app)** — CLI de control: parsea la acción de los
argumentos (`DesktopAction: FromStr`) y la manda al Cerebro por el
socket de control; `windows` y `actions` para consultar.
## Atajos de teclado configurables
El keymap vive **sólo en el Cerebro** (`mirada-brain::Keymap`). El Cuerpo
nunca lo ve: recibe únicamente la lista de cadenas a interceptar en un
`GrabKeys`, hace un `Vec::contains` ciego y devuelve la combinación
pulsada como `Keybind`; es `Desktop` quien la traduce a `DesktopAction`.
Esa separación —*qué* interceptar (lista barata, Cuerpo) vs. *qué
significa* (el mapa, Cerebro)— hace innecesario cualquier candado o
`Arc`: el mapa es monohilo y la lista se reemplaza de golpe.
- **Disco** — RON de texto en `~/.config/mirada/keymap.ron`, editable a
mano y versionable. La app lo crea documentado en el primer arranque;
si está corrupto, avisa y usa el de por defecto sin pisar el archivo.
- **Cable** — sólo viaja la lista de cadenas (`GrabKeys`), vía el marco
`postcard` que ya existe. No hay formato binario de configuración.
- **Vocabulario** — la acción es una cadena estable (`"focus-next"`,
`"layout:grid"`, `"workspace:3"`): `DesktopAction: Display + FromStr`.
- **Recarga en caliente** — `Keymap::watch` (sobre `notify`) vigila el
archivo; al cambiar, el dueño del `Desktop` recarga, llama a
`set_keymap` y reenvía el `GrabKeys`. Sin reiniciar.
- **Configurador** — no hay ejecutable aparte: el editor de texto del
usuario, y la app `mirada` (que a futuro puede dibujar un editor visual
sobre el mismo API `Keymap`). `cargo run -p mirada-brain --example
keymap-default` imprime el archivo por defecto.
## API de acciones
Toda acción de escritorio converge en un único embudo:
`Desktop::apply(DesktopAction) -> Vec<BrainCommand>`. El keymap no es más
que un front-end (`Keybind``lookup``apply`); hay otros tres:
- **`DesktopAction::FocusWindow(WindowId)`** — direccionamiento directo de
una ventana (no sólo ciclar con `FocusNext`/`Prev`); si está en otro
escritorio, salta a él. Lo usan la taskbar y `mirada-ctl`.
- **Ventanas flotantes** — `ToggleFloat` (`Super+f`) saca la enfocada del
teselado a un rectángulo libre (centrado, 60 %); `Workspace` guarda las
flotantes aparte, `layout()` las pone al final y `WindowPlacement`
/`BodyOp::Configure` llevan `floating: bool` para que el Cuerpo las
componga por encima.
- **Layout y área maestra por el API** — los 7 modos se intercambian
(`SetLayout`/`CycleLayout`, `mirada-ctl layout spiral`); el área
maestra se redimensiona (`grow`/`shrink-master`, `Super+l`/`Super+h`);
`inc`/`dec-master` cambian `nmaster` (`Super+,`/`Super+.`); y
`promote-to-master` lleva la enfocada al puesto maestro (`Super+Return`
`combo_string` ya canoniza teclas con nombre: `Return`, `Tab`, `F5`…).
- **HUD interactivo** (app `mirada`) — los pips de escritorio y las
ventanas del lienzo son clicables: clic = `apply` de la acción.
- **`mirada-ctl`** — control externo por línea de comandos
(`mirada-ctl focus-next`, `workspace 3`, `windows`). Habla con el
Cerebro por un socket Unix aparte; el módulo `mirada-brain::ctl` define
`CtlRequest`/`CtlReply` (marco `postcard`), `CtlServer`/`CtlConn` y
`send_request`. El Cerebro (la app `mirada` siempre; `mirada-compositor`
sólo embebido) abre el socket y atiende en su bucle. `DesktopAction`
viaja como enum serializado: contrato tipado de punta a punta.
`cargo run -p mirada-brain --example headless-ctl` levanta un Cerebro sin
gráficos para ejercitar `mirada-ctl` en modo desatendido.
## Dependencias
- Todos los `lib` con `#![forbid(unsafe_code)]`. Cero Wayland, cero
`smithay` en los seis crates de arriba.
- El acoplamiento a Wayland/hardware vive sólo en `mirada-compositor`.
## Estado
Implementado y verde: `mirada-layout` (32 tests), `mirada-protocol`
(10), `mirada-brain` (42), `mirada-link` (7), `mirada-body` (14), las
apps `mirada` y `mirada-compositor` (compilan; verificación visual
manual) y `mirada-ctl` (CLI, probado vía el ejemplo `headless-ctl`).
El **Cuerpo** ya existe: `mirada-compositor` es un compositor Wayland
teselante real sobre `smithay`, con backend `winit` — corre **anidado**
como una ventana dentro de la sesión gráfica actual. Habla
`wl_compositor`/`xdg_shell`/`wl_shm`/`wl_seat`/`wl_data_device`, compone
las superficies de los clientes con `GlesRenderer` y aplica la geometría
del Cerebro. Reusa `mirada-body` (contabilidad) y `mirada-link` (cable).
Dos modos: **autónomo** (Cerebro `Desktop` embebido, un solo proceso) o
**enlazado** (`MIRADA_SOCKET` → la app `mirada` decide la geometría).
Compila y pasa clippy; verificación visual manual — ver
`crates/apps/mirada-compositor/README.md`.
**Pendiente** — refinamientos del Cuerpo, no verificables en modo
desatendido:
| capa pendiente | rol |
| -------------------- | -------------------------------------------------------- |
| backend DRM/libinput | de `winit` anidado a sesión nativa: superficies KMS, GPU |
| `mirada-input` | puntero/ratón completo, repetición de teclas, gestos |
| `mirada-sandbox` | aislamiento de clientes sobre `arje-incarnate` |
CRIU (congelar/restaurar ventanas) queda anotado como futuro.