Commit Graph

15 Commits

Author SHA1 Message Date
sergio 751416252f feat(mirada): marco de ventana — distinguir y resaltar el foco
Sin decoración, las ventanas se confundían entre sí. Ahora el backend
DRM dibuja un marco fino alrededor de cada ventana: azul la que tiene
el foco del teclado, gris las demás.

- `ManagedWindow` gana `focused: bool` (lo fija `exec_op` al atender
  `BodyOp::Focus`/`Unfocus`) y `borders: [SolidColorBuffer; 4]` — un
  búfer por lado, cada uno con su `Id` estable para el seguimiento de
  daño; `SolidColorBuffer` sube su contador sólo si tamaño o color
  cambian, así un marco quieto no fuerza recomposición.
- El enum `Frame` pasa de `Cursor` a `Solid`: una variante de color
  sólido que sirve para el cursor y para los marcos (dos variantes con
  el mismo tipo chocarían en el `From` que genera `render_elements!`).
- `render()` en dos pasos: refresca los búferes (tamaño = contenido,
  color = foco) y luego arma los elementos. El marco va metido hacia
  adentro, sobre el borde de la superficie, así no pisa al vecino.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 04:10:32 +00:00
sergio fb3091d995 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>
2026-05-21 03:59:37 +00:00
sergio 90bffec3f1 feat(mirada): mover/redimensionar ventanas con el ratón
`Super`+arrastre interactivo en el backend DRM: botón izquierdo mueve
la ventana, botón derecho la redimensiona. Al arrastrarla, la ventana
pasa a flotar — comportamiento estilo dwm.

La verdad geométrica vive en el Cerebro, así que el arrastre viaja
hasta él:

- `mirada-protocol`: nuevo `BodyEvent::WindowFloatTo { id, rect }`.
- `mirada-brain`: `Desktop::on_event` lo atiende — busca el escritorio
  de la ventana y la hace flotar en ese rectángulo
  (`Workspace::set_floating`). Dos tests nuevos.
- `mirada-compositor`: `DragGrab`/`DragMode` en `App`; `handle_input`
  arranca el arrastre con `Super`+botón sobre una ventana
  (`keyboard.modifier_state().logo`), traga los botones mientras dura y
  lo cierra al soltar. `drag_update` recalcula el rectángulo (mover =
  esquina sigue al puntero; redimensionar = esquina inferior-derecha,
  con un mínimo de 120 px) y emite `WindowFloatTo`. Durante el arrastre
  el puntero no llega al cliente.

De paso, arregla un test de `mirada-link` que construía un
`WindowPlacement` sin los campos `floating`/`fullscreen`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:46:03 +00:00
sergio b4ddab9c06 feat(mirada): puntero/ratón en el backend DRM del compositor
El backend DRM del Cuerpo deja de ser sólo-teclado: `libinput` ahora
mueve un cursor de software y reenvía clics y rueda a los clientes.

- Enum `Frame` (vía `render_elements!`) que mezcla superficies de
  cliente y un `SolidColorRenderElement` para el cursor, marcado
  `Kind::Cursor` y compuesto encima de todo.
- `handle_input` atiende `PointerMotion`/`PointerMotionAbsolute`/
  `PointerButton`/`PointerAxis`; el puntero se acota a la salida.
- Foco-sigue-ratón: `window_at` hace el test de impacto (flotantes
  sobre teseladas, contra el rectángulo real de la superficie) y, al
  cambiar de ventana, emite `BodyEvent::PointerEntered`.
- `surface_px_size` en main.rs — tamaño presentado de una superficie,
  reusado por el test de impacto.

Compila + clippy limpio; pendiente de verificar en hardware.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:24:14 +00:00
sergio 5230d42b11 feat(mirada-compositor): centrar la ventana en su celda si el cliente no la llena
Un cliente que presenta una superficie más pequeña que su celda (p. ej.
un terminal que redondea a celdas de texto enteras) dejaba el hueco todo
a un lado. Ahora ManagedWindow recuerda el tamaño de la celda y
render_loc() centra la superficie en el sobrante. Lo usan los dos
backends (winit y DRM).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:12:44 +00:00
sergio 84f94574f1 chore(mirada-compositor): backend DRM — quitar la instrumentación de depuración
El backend DRM funciona en hardware (sesión, render, teclado, atajos,
clientes, salida limpia). Diagnosticado: la franja al pie con `foot` es
que `foot` redondea su superficie a celdas de texto enteras (1920×1040
de 1080) — comportamiento del cliente, no del compositor.

Se retira la instrumentación: log por tecla, censo de dispositivos y
volcado de tamaños de superficie cada 2 s. El tope de tiempo
(MIRADA_DRM_TIMEOUT) pasa a estar desactivado por defecto — Super+Shift+e
y Ctrl+C son la salida normal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:07:26 +00:00
sergio 97a10aa173 chore(mirada-compositor): backend DRM — log del tamaño de las superficies
El panel sólo ofrece modos 1920×1080, así que la franja negra no es del
modo. Para localizarla, el bucle DRM registra cada ~2 s la posición y el
tamaño real de cada superficie — así se ve si el cliente llena la
pantalla o se queda corto.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:58:52 +00:00
sergio 30669f970b fix(mirada-compositor): backend DRM — modo de mayor área + log de modos
El borde negro seguía: la marca PREFERRED del panel no es fiable (a
veces apunta a un modo menor). Ahora se elige el modo de mayor área
(a igualdad, mayor refresco), y se registran todos los modos del
conector para diagnóstico.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:50:49 +00:00
sergio 97a725b870 fix(mirada-compositor): backend DRM — elegir el modo nativo del monitor
El backend DRM cogía conn.modes()[0], que no es el modo preferido —
en un panel 16:10 (1920×1200) suele ser un 1920×1080, dejando una
franja negra abajo.

Ahora elige el modo marcado PREFERRED (el nativo) y, si ninguno lo
está, el de mayor área.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:40:33 +00:00
sergio 8cd34bf173 fix(mirada-compositor): anunciar un wl_output — los clientes lo exigen
foot (y casi todo cliente Wayland) aborta con «no monitors available»
si el compositor no anuncia ningún wl_output. carmen no lo hacía.

- OutputHandler para App + delegate_output!.
- announce_output(): crea un Output, lo publica como global wl_output y
  le fija el modo. Helper compartido por los dos backends.
- winit y DRM lo llaman con su tamaño/modo real.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:35:06 +00:00
sergio fb2f3a2d01 feat(mirada-compositor): backend DRM — app de arranque + traza de ventanas
Primera prueba 2b en hardware: sesión, render, teclado y atajos
funcionan — Super+Shift+e cierra limpio. Faltaba ver una ventana de
cliente.

- MIRADA_STARTUP: si trae un comando, el backend DRM lo lanza como hijo
  al arrancar (hereda WAYLAND_DISPLAY) — así se prueba un cliente sin
  saltar de VT.
- Logs: cada cliente Wayland que se conecta, y el nº de ventanas en
  pantalla cuando cambia — para confirmar la cadena cliente→ventana.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:21:11 +00:00
sergio 782244b743 fix(mirada-compositor): backend DRM — salida garantizada + logs de teclado
Tras la primera prueba en hardware (el bucle arranca y compone el fondo,
pero el teclado no responde y no había forma de salir):

- Salida garantizada: el backend DRM se cierra solo a los 60 s (env
  MIRADA_DRM_TIMEOUT, 0 lo desactiva). Así una prueba nunca deja la
  pantalla atrapada aunque el teclado falle.
- handle_input instrumentado: registra cada dispositivo de entrada que
  libinput descubre y cada tecla con su combo y si es un atajo — para
  diagnosticar por qué no llega la entrada.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:14:50 +00:00
sergio fe221869d2 feat(mirada-compositor): backend DRM — fase 2b, bucle Wayland
Última fase del backend DRM: el bucle Wayland completo. Con esto
`mirada-compositor --drm` es un escritorio funcionando sobre una TTY,
sin sesión anfitriona.

main.rs: el armado del estado se extrae a build_app() -> Setup, que
comparten los dos backends (winit intacto).

drm_backend.rs — fase 2b sobre el pipeline de la 2a:
- DrmState: el estado que comparten los callbacks de calloop.
- bucle calloop con cinco fuentes: VBlank (DrmDeviceNotifier),
  teclado (libinput), clientes Wayland nuevos (ListeningSocket),
  peticiones de los clientes (poll fd del Display) y un timer ~60 Hz.
- render(): compone las ventanas de App en el DrmCompositor, encola el
  page-flip y reparte los frame-callbacks; el VBlank libera el flip.
- handle_input(): teclado libinput → interceptación de atajos (misma
  combo_string que winit) → keybind al Cerebro.
- tick(): Cerebro enlazado, recarga de keymap, mirada-ctl, composición.
- registra la salida con el modo del monitor; abre el socket Wayland.

Compila y pasa clippy aquí; se ejecuta y depura en hardware por logs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:01:29 +00:00
sergio f8cb80d867 feat(mirada-compositor): backend DRM — fase 2a, pipeline de render
Sobre el bring-up de la fase 1, drm_backend.rs monta ahora el pipeline
gráfico completo y lo prueba:

- elige la salida conectada (conector + CRTC + modo)
- GBM + EGL + GlesRenderer
- GbmAllocator + GbmFramebufferExporter + DrmCompositor para esa salida
- bucle calloop sincronizado al VBlank (DrmDeviceNotifier): pinta la
  pantalla de colores ~6 s y para (con tope de 10 s anti-cuelgue)

Es un test de hardware: si la pantalla cambia de color, EGL, GBM, el
modeset y el page-flip funcionan. Compila y pasa clippy aquí; se
ejecuta y depura en la máquina con GPU por logs. La fase 2b será el
bucle Wayland completo (clientes + libinput + composición de ventanas).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:53:04 +00:00
sergio 6c3a86fbec feat(mirada-compositor): backend DRM — fase 1, bring-up
mirada-compositor gana un segundo backend para correr sobre una TTY
pelada, sin sesión gráfica anfitriona. main() elige: --winit / --drm,
o automático (con DISPLAY/WAYLAND_DISPLAY → winit anidado; sin ellos →
DRM). run() pasa a llamarse run_winit().

drm_backend.rs — fase 1 (bring-up), construida para verificarse en
hardware real por etapas:
- abre la sesión con libseat (acceso a DRM/input sin root)
- localiza la GPU primaria (udev::primary_gpu)
- abre el dispositivo DRM por la sesión
- enumera los conectores y sus modos

Todo instrumentado con logs para diagnosticar sin el hardware delante.
La composición (GBM + EGL + GlesRenderer + DrmCompositor + libinput +
bucle calloop) es la fase 2. El backend winit queda intacto.

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