`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>
Dos remates de la tanda WM.
Fullscreen del cliente:
- BodyEvent::FullscreenRequest { id, fullscreen }. mirada-compositor
implementa XdgShellHandler::fullscreen_request / unfullscreen_request
y avisa al Cerebro; Desktop::on_event fija el fullscreen en el
escritorio que tiene la ventana. Así un reproductor o un juego que
llama a xdg set_fullscreen entra a pantalla completa solo.
HUD multi-salida (app mirada):
- El lienzo dibuja todas las salidas a escala (encaja su caja
envolvente en el lienzo fijo; con una salida, 1:1), cada una con su
marco y su número/escritorio. En simulación, Shift+n añade un monitor.
mirada-brain 63->65 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ToggleFullscreen (Super+Shift+f) lleva la ventana enfocada a pantalla
completa: cubre toda la salida sin gap, oculta al resto y se lleva el
foco. Distinto del modo Monocle (un modo de teselado): es un estado
por ventana que ignora el layout.
- Workspace.fullscreen: Option<WindowId>; set_fullscreen / fullscreen();
remove() lo limpia si se cierra esa ventana.
- placements() da a la fullscreen el rect completo y marca al resto
visible: false. WindowPlacement y BodyOp::Configure llevan
fullscreen: bool.
- mirada-compositor fija el estado xdg_toplevel::Fullscreen en la
superficie, para que el cliente lo sepa.
- Cableado en keymap, HUD de mirada y mirada-ctl.
Verificado end-to-end con headless-ctl. mirada-protocol 10->11,
mirada-brain 51->52.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
BrainCommand/BodyEvent + WindowPlacement, marco postcard con prefijo u32
LE (write_frame/read_frame, guard MAX_FRAME) y el puente placements()
desde un Workspace de mirada-layout. 9 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>