gioser

tawasuyu/brahman

Rust 476TOML 190Markdown 50JSON 176602 Definiciones

⎇ 5ede927d3432c0c895e863b39ca7eb5f73b78213 crates/modules/mirada/SDD.md Markdown · 234 líneas · 13.3 KB Historia crudo

feat(mirada): sesión de escritorio — autostart y conmutación de VT 5ede927d3 · sergio ·

1# modules/mirada/ — Compositor Wayland (carmen)
2
3**Propósito.** Un compositor Wayland teselante. La decisión de diseño
4central es partirlo en dos procesos, el **Cerebro** y el **Cuerpo**:
5
6- **El Cuerpo** (`mirada-compositor`, sobre `smithay`) habla Wayland con
7  los clientes, posee el hardware (DRM/GPU/libinput) y compone las
8  superficies reales. Los píxeles nunca salen de él — composición
9  *zero-copy*.
10- **El Cerebro** (la app `mirada`, GPUI) decide *dónde* va cada ventana
11  —pura aritmética de rectángulos— y orquesta el escritorio: layouts,
12  atajos, focos, escritorios virtuales.
13
14Los dos hablan por un socket Unix con un contrato mínimo de enums. Así,
15toda la lógica espacial es agnóstica de Wayland y se prueba sin un
16servidor gráfico; el Cuerpo queda reducido a "habla el protocolo y
17ejecuta operaciones de geometría".
18
19## Crates
20
21| crate              | tipo     | rol                                                                    |
22| ------------------ | -------- | ---------------------------------------------------------------------- |
23| `mirada-layout`    | lib      | Motor de teselado: `Rect`, modos de layout, `Workspace` (ventanas, foco) |
24| `mirada-protocol`  | lib      | Contrato Cerebro↔Cuerpo: `BrainCommand`/`BodyEvent` + marco de cable   |
25| `mirada-brain`     | lib      | Orquestador del escritorio: `Desktop`, eventos→comandos, atajos        |
26| `mirada-link`      | lib      | Transporte: el socket Unix con hilo lector + canal                     |
27| `mirada-body`      | lib      | Contabilidad del Cuerpo: `BodyState`, traduce comandos a `BodyOp`      |
28| `mirada` (app)     | bin/GPUI | El Cerebro: ventana que tesela el escritorio y manda geometría         |
29| `mirada-compositor`| bin/smithay | El Cuerpo: compositor Wayland real (backend `winit`, anidado)        |
30| `mirada-ctl` (app) | bin/CLI  | Control externo del Cerebro (estilo `swaymsg`): acciones y consultas   |
31
32## Flujo
33
34```text
35  mirada-layout ─► mirada-protocol ─► mirada-brain ─► [app mirada · Cerebro]
36                          │                                  │
37                          │                            mirada-link
38                          │                                  │
39                          └────► mirada-body ─► [mirada-compositor · Cuerpo]
40```
41
42- El Cuerpo reporta hardware/clientes con `BodyEvent` (salida conectada,
43  ventana abierta, atajo pulsado…).
44- El Cerebro (`Desktop::on_event`) recalcula y emite `BrainCommand`
45  (`Place` con la geometría completa, `Close`, `GrabKeys`…).
46- El Cuerpo (`BodyState::apply`) traduce cada comando a `BodyOp`
47  concretas y sólo emite lo que de verdad cambia.
48
49## Detalle por crate
50
51- **`mirada-layout`** — `Rect` + `split` (reparto exacto de píxeles),
52  `LayoutMode` con 7 modos (`MasterStack`, `CenteredMaster`, `Spiral`
53  —espiral de Fibonacci—, `Grid`, `Columns`, `Rows`, `Monocle`) y
54  `LayoutMode::next()` para el ciclo, `Workspace` con foco cíclico,
55  reordenado y `promote_focused`. `LayoutParams` lleva `master_ratio` y
56  `master_count` (`nmaster`); *smart gaps* (una sola ventana va a
57  sangre). Determinista.
58- **`mirada-protocol`** — `WindowPlacement`, los enums `BrainCommand` y
59  `BodyEvent`, el marco `postcard` con prefijo `u32` LE
60  (`write_frame`/`read_frame`, guard `MAX_FRAME`) y el puente
61  `placements(&Workspace, Rect)`.
62- **`mirada-brain`** — `Desktop`: salidas, 9 escritorios virtuales,
63  registro de ventanas. `on_event(BodyEvent) -> Vec<BrainCommand>`;
64  `DesktopAction` + `Keymap` configurable (`set_keymap` en caliente) +
65  `Rules` de ventana. **Multi-monitor**: cada `Output` muestra un
66  escritorio; `relayout()` tesela todas las salidas en un solo `Place`.
67- **`mirada-link`** — `Link<Out,In>` sobre socket Unix; hilo lector de
68  fondo + canal `mpsc` para sondeo no bloqueante. `BrainLink`/`BodyLink`,
69  `connected_pair` (socketpair), `connect`/`listen` por ruta.
70- **`mirada-body`** — `BodyState`: salidas + superficies; `apply` traduce
71  `BrainCommand`→`BodyOp` (idempotente), los mutadores del backend
72  devuelven los `BodyEvent` a mandar. Ejemplo `headless`: un Cuerpo sin
73  gráficos guiado por stdin para ejercitar el bucle entero.
74- **`mirada` (app)** — envuelve `Desktop` y lo pinta (barra de
75  escritorios + modo + foco, lienzo teselado). El lienzo dibuja **todas
76  las salidas a escala**, cada una con su marco. Con `MIRADA_SOCKET`
77  conecta a un Cuerpo; sin él corre en **simulación** (ventanas
78  sintéticas, teclado de la propia ventana). Pips de escritorio y
79  ventanas clicables.
80- **`mirada-ctl` (app)** — CLI de control: parsea la acción de los
81  argumentos (`DesktopAction: FromStr`) y la manda al Cerebro por el
82  socket de control; `windows` y `actions` para consultar.
83
84## Atajos de teclado configurables
85
86El keymap vive **sólo en el Cerebro** (`mirada-brain::Keymap`). El Cuerpo
87nunca lo ve: recibe únicamente la lista de cadenas a interceptar en un
88`GrabKeys`, hace un `Vec::contains` ciego y devuelve la combinación
89pulsada como `Keybind`; es `Desktop` quien la traduce a `DesktopAction`.
90Esa separación —*qué* interceptar (lista barata, Cuerpo) vs. *qué
91significa* (el mapa, Cerebro)— hace innecesario cualquier candado o
92`Arc`: el mapa es monohilo y la lista se reemplaza de golpe.
93
94- **Disco** — RON de texto en `~/.config/mirada/keymap.ron`, editable a
95  mano y versionable. La app lo crea documentado en el primer arranque;
96  si está corrupto, avisa y usa el de por defecto sin pisar el archivo.
97- **Cable** — sólo viaja la lista de cadenas (`GrabKeys`), vía el marco
98  `postcard` que ya existe. No hay formato binario de configuración.
99- **Vocabulario** — la acción es una cadena estable (`"focus-next"`,
100  `"layout:grid"`, `"workspace:3"`): `DesktopAction: Display + FromStr`.
101- **Recarga en caliente** — `Keymap::watch` (sobre `notify`) vigila el
102  archivo; al cambiar, el dueño del `Desktop` recarga, llama a
103  `set_keymap` y reenvía el `GrabKeys`. Sin reiniciar.
104- **Configurador** — no hay ejecutable aparte: el editor de texto del
105  usuario, y la app `mirada` (que a futuro puede dibujar un editor visual
106  sobre el mismo API `Keymap`). `cargo run -p mirada-brain --example
107  keymap-default` imprime el archivo por defecto.
108
109## API de acciones
110
111Toda acción de escritorio converge en un único embudo:
112`Desktop::apply(DesktopAction) -> Vec<BrainCommand>`. El keymap no es más
113que un front-end (`Keybind` → `lookup` → `apply`); hay otros tres:
114
115- **`DesktopAction::FocusWindow(WindowId)`** — direccionamiento directo de
116  una ventana (no sólo ciclar con `FocusNext`/`Prev`); si está en otro
117  escritorio, salta a él. Lo usan la taskbar y `mirada-ctl`.
118- **Ventanas flotantes** — `ToggleFloat` (`Super+f`) saca la enfocada del
119  teselado a un rectángulo libre (centrado, 60 %); `Workspace` guarda las
120  flotantes aparte, `layout()` las pone al final y `WindowPlacement`
121  /`BodyOp::Configure` llevan `floating: bool` para que el Cuerpo las
122  componga por encima.
123- **Pantalla completa** — `ToggleFullscreen` (`Super+Shift+f`): la ventana
124  cubre toda la salida (sin gap), oculta al resto y se lleva el foco;
125  `Workspace.fullscreen: Option<WindowId>`, y el Cuerpo le fija el estado
126  `xdg_toplevel Fullscreen`. También atiende la petición del propio
127  cliente (`xdg set_fullscreen` → `BodyEvent::FullscreenRequest`), así
128  que un reproductor o un juego entran a pantalla completa solos.
129- **Multi-monitor** — cada `Output` muestra un escritorio distinto;
130  `SwitchWorkspace` actúa sobre la salida enfocada (y la intercambia si
131  el escritorio pedido ya lo muestra otra salida); `FocusOutputNext`
132  (`Super+o`) mueve el foco entre monitores. El foco del teclado es
133  único — sólo la ventana enfocada de la salida enfocada.
134- **Scratchpad** — `SendToScratchpad` guarda la ventana enfocada (sale
135  del teselado, en ningún escritorio); `ToggleScratchpad` (`` Super+` ``)
136  la invoca flotando y centrada en el escritorio actual, o la oculta —
137  estilo terminal desplegable. `Desktop.scratchpad: Vec<WindowId>`;
138  `mirada-ctl windows` la lista como `esc scratch`.
139- **Layout y área maestra por el API** — los 7 modos se intercambian
140  (`SetLayout`/`CycleLayout`, `mirada-ctl layout spiral`); el área
141  maestra se redimensiona (`grow`/`shrink-master`, `Super+l`/`Super+h`);
142  `inc`/`dec-master` cambian `nmaster` (`Super+,`/`Super+.`); y
143  `promote-to-master` lleva la enfocada al puesto maestro (`Super+Return`
144  — `combo_string` ya canoniza teclas con nombre: `Return`, `Tab`, `F5`…).
145- **Lanzar programas** — `DesktopAction::Spawn(String)` (forma textual
146  `spawn:<comando>`, `Super+Shift+Return` → `spawn:foot` por defecto)
147  produce un `BrainCommand::Spawn`; el Cuerpo lo ejecuta con `sh -c`, y
148  el hijo hereda `WAYLAND_DISPLAY`. `DesktopAction` deja de ser `Copy`
149  por llevar el comando.
150- **HUD interactivo** (app `mirada`) — los pips de escritorio y las
151  ventanas del lienzo son clicables: clic = `apply` de la acción.
152- **`mirada-ctl`** — control externo por línea de comandos
153  (`mirada-ctl focus-next`, `workspace 3`, `windows`). Habla con el
154  Cerebro por un socket Unix aparte; el módulo `mirada-brain::ctl` define
155  `CtlRequest`/`CtlReply` (marco `postcard`), `CtlServer`/`CtlConn` y
156  `send_request`. El Cerebro (la app `mirada` siempre; `mirada-compositor`
157  sólo embebido) abre el socket y atiende en su bucle. `DesktopAction`
158  viaja como enum serializado: contrato tipado de punta a punta.
159
160`cargo run -p mirada-brain --example headless-ctl` levanta un Cerebro sin
161gráficos para ejercitar `mirada-ctl` en modo desatendido.
162
163## Reglas de ventana
164
165`mirada-brain::rules` decide qué hacer con una ventana **al abrirse**:
166config declarativa en RON (`~/.config/mirada/rules.ron`), mismo patrón
167que el keymap. Cada `Rule` casa por subcadena de `app_id` y/o `title`
168(sin distinguir mayúsculas; vacío = cualquiera) y aplica un destino:
169`workspace` (1..9) y/o `floating`. Gana la primera regla que case.
170
171El `Desktop` consulta `Rules::resolve` en cada `WindowOpened` — el evento
172ya trae `app_id`/`title` — y manda la ventana a su escritorio, flotando
173si toca. Se carga al arrancar (la primera vez se escribe una plantilla
174con ejemplos comentados); las reglas afectan a las ventanas futuras, no
175a las ya abiertas.
176
177## Dependencias
178
179- Todos los `lib` con `#![forbid(unsafe_code)]`. Cero Wayland, cero
180  `smithay` en los seis crates de arriba.
181- El acoplamiento a Wayland/hardware vive sólo en `mirada-compositor`.
182
183## Estado
184
185Implementado y verde: `mirada-layout` (32 tests), `mirada-protocol`
186(11), `mirada-brain` (65), `mirada-link` (7), `mirada-body` (14), las
187apps `mirada` y `mirada-compositor` (compilan; verificación visual
188manual) y `mirada-ctl` (CLI, probado vía el ejemplo `headless-ctl`).
189
190El **Cuerpo** ya existe: `mirada-compositor` es un compositor Wayland
191teselante real sobre `smithay`. Habla `wl_compositor`/`xdg_shell`/
192`wl_shm`/`wl_seat`/`wl_output`/`wl_data_device`/`xdg-decoration`, compone
193las superficies de los clientes con `GlesRenderer` y aplica la geometría
194del Cerebro. Fuerza decoración `ServerSide` y no dibuja ninguna: las
195ventanas teseladas van sin marco (nada de barras de título de cliente).
196Reusa `mirada-body` (contabilidad) y `mirada-link` (cable). Dos modos de
197Cerebro: **autónomo** (`Desktop` embebido) o **enlazado** (`MIRADA_SOCKET`
198→ la app `mirada`).
199
200**Dos backends gráficos** (`main()` elige; `--winit`/`--drm` lo fuerzan):
201
202- **`winit`** — corre anidado, una ventana en la sesión gráfica actual.
203- **`drm`** (`drm_backend.rs`) — corre nativo sobre una TTY, sin sesión
204  anfitriona: `libseat` (sesión), `udev` (GPU), `DrmDevice` + GBM + EGL +
205  `DrmCompositor`, `libinput` (teclado y ratón), bucle `calloop`.
206  Verificado en hardware: sesión, render, teclado, atajos, clientes,
207  salida limpia. El ratón: el cursor toma la superficie que pide el
208  cliente (`wl_pointer.set_cursor` → `cursor_image`) y cae a un cuadrado
209  (`SolidColorRenderElement` `Kind::Cursor`) por defecto; el foco sigue
210  al puntero (`BodyEvent::PointerEntered`) y clics y rueda van a la
211  ventana debajo. Todo en un enum `Frame` de elementos de render.
212  `Super`+arrastre mueve/redimensiona: el Cuerpo calcula el rectángulo y
213  emite `BodyEvent::WindowFloatTo { id, rect }`; el Cerebro hace flotar
214  la ventana ahí (`Workspace::set_floating`). Cada ventana lleva un marco
215  de 2 px (4 `SolidColorBuffer` por ventana, `Id` estable): azul si tiene
216  el foco, gris si no.
217- **Conmutación de VT** — `Ctrl+Alt+Fn` salta a otra TTY: el
218  `SessionEvent` de `libseat` pausa el `DrmDevice` y suspende `libinput`;
219  al volver, los reactiva, llama a `DrmCompositor::reset_state` y
220  repinta. Mientras está cedida, `render()` no toca la GPU.
221- **Sesión** — `~/.config/mirada/autostart` (un comando por línea) se
222  lanza al arrancar el backend DRM; el script `session/mirada-session` y
223  `session/carmen.desktop` integran carmen con un gestor de login.
224
225**Pendiente** — refinamientos del Cuerpo:
226
227| capa pendiente   | rol                                                          |
228| ---------------- | ------------------------------------------------------------ |
229| puntero en `winit` | ratón en el backend anidado (hoy sólo el backend DRM)      |
230| `mirada-input`   | repetición de teclas, gestos; hotplug de monitores           |
231| barra de estado  | `wlr-layer-shell` + un cliente que dibuje la barra           |
232| `mirada-sandbox` | aislamiento de clientes sobre `arje-incarnate`               |
233
234CRIU (congelar/restaurar ventanas) queda anotado como futuro.

SDD.md

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:

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

cratetiporol
mirada-layoutlibMotor de teselado: Rect, modos de layout, Workspace (ventanas, foco)
mirada-protocollibContrato Cerebro↔Cuerpo: BrainCommand/BodyEvent + marco de cable
mirada-brainlibOrquestador del escritorio: Desktop, eventos→comandos, atajos
mirada-linklibTransporte: el socket Unix con hilo lector + canal
mirada-bodylibContabilidad del Cuerpo: BodyState, traduce comandos a BodyOp
mirada (app)bin/GPUIEl Cerebro: ventana que tesela el escritorio y manda geometría
mirada-compositorbin/smithayEl Cuerpo: compositor Wayland real (backend winit, anidado)
mirada-ctl (app)bin/CLIControl externo del Cerebro (estilo swaymsg): acciones y consultas

Flujo

  mirada-layout ─► mirada-protocol ─► mirada-brain ─► [app mirada · Cerebro]
                          │                                  │
                          │                            mirada-link
                          │                                  │
                          └────► mirada-body ─► [mirada-compositor · Cuerpo]

Detalle por crate

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.

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:

cargo run -p mirada-brain --example headless-ctl levanta un Cerebro sin gráficos para ejercitar mirada-ctl en modo desatendido.

Reglas de ventana

mirada-brain::rules decide qué hacer con una ventana al abrirse: config declarativa en RON (~/.config/mirada/rules.ron), mismo patrón que el keymap. Cada Rule casa por subcadena de app_id y/o title (sin distinguir mayúsculas; vacío = cualquiera) y aplica un destino: workspace (1..9) y/o floating. Gana la primera regla que case.

El Desktop consulta Rules::resolve en cada WindowOpened — el evento ya trae app_id/title — y manda la ventana a su escritorio, flotando si toca. Se carga al arrancar (la primera vez se escribe una plantilla con ejemplos comentados); las reglas afectan a las ventanas futuras, no a las ya abiertas.

Dependencias

Estado

Implementado y verde: mirada-layout (32 tests), mirada-protocol (11), mirada-brain (65), 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. Habla wl_compositor/xdg_shell/ wl_shm/wl_seat/wl_output/wl_data_device/xdg-decoration, compone las superficies de los clientes con GlesRenderer y aplica la geometría del Cerebro. Fuerza decoración ServerSide y no dibuja ninguna: las ventanas teseladas van sin marco (nada de barras de título de cliente). Reusa mirada-body (contabilidad) y mirada-link (cable). Dos modos de Cerebro: autónomo (Desktop embebido) o enlazado (MIRADA_SOCKET → la app mirada).

Dos backends gráficos (main() elige; --winit/--drm lo fuerzan):

Pendiente — refinamientos del Cuerpo:

capa pendienterol
puntero en winitratón en el backend anidado (hoy sólo el backend DRM)
mirada-inputrepetición de teclas, gestos; hotplug de monitores
barra de estadowlr-layer-shell + un cliente que dibuje la barra
mirada-sandboxaislamiento de clientes sobre arje-incarnate

CRIU (congelar/restaurar ventanas) queda anotado como futuro.