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>
This commit is contained in:
sergio
2026-05-21 03:46:03 +00:00
parent ae81399857
commit 90bffec3f1
8 changed files with 169 additions and 8 deletions
@@ -91,6 +91,10 @@ pub enum BodyEvent {
/// Un cliente pidió pantalla completa para su ventana (`true`), o la
/// soltó (`false`) — `xdg_toplevel.set_fullscreen`.
FullscreenRequest { id: WindowId, fullscreen: bool },
/// El usuario arrastró una ventana con el ratón a un rectángulo nuevo
/// (mover o redimensionar interactivos). El Cerebro la hace flotar
/// ahí; si estaba teselada, deja de estarlo.
WindowFloatTo { id: WindowId, rect: Rect },
}
/// Tamaño máximo de un marco, en bytes. Acota el búfer de [`read_frame`]