feat(mirada): zwp_linux_dmabuf — clientes que pintan por GPU
Fase 1 del plan «shell»: para que carmen pueda hospedar a `shuma-shell` (y a cualquier app GPUI o navegador acelerado) hace falta que los clientes con GPU puedan compartir su búfer de vídeo. carmen sólo hablaba `wl_shm` (búferes de software) — por eso `foot` corría pero las apps GPUI salían en negro. - `App` lleva un `DmabufState`; `impl DmabufHandler` con `dmabuf_imported` que acepta el búfer (el `GlesRenderer` ya importa DMA-BUF al componer, vía `ImportAll`, así que la validación real ocurre al pintar). - `delegate_dmabuf!(App)`. - `announce_dmabuf` crea el global con los formatos de `dmabuf_formats()` del renderer — se llama en ambos backends una vez creado el renderer. Pendiente del plan: Fase 2 (`wlr-layer-shell`) y Fase 3 (modo launcher de `shuma-shell` — barra + input + cajón de resultados). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -169,10 +169,12 @@ En modo enlazado el socket de control lo abre el Cerebro (la app
|
||||
|
||||
`wl_compositor`, `xdg_shell` (toplevels y popups), `wl_shm`, `wl_seat`
|
||||
(teclado, y ratón en el backend DRM), `wl_output`, `wl_data_device`
|
||||
(selección) y `xdg-decoration` — fuerza decoración del servidor y no
|
||||
dibuja ninguna, así las ventanas van sin barra de título. Composición
|
||||
con `GlesRenderer` — en `winit` sobre la ventana, en `drm` con un
|
||||
`DrmCompositor` por salida.
|
||||
(selección), `xdg-decoration` — fuerza decoración del servidor y no
|
||||
dibuja ninguna, así las ventanas van sin barra de título — y
|
||||
`zwp_linux_dmabuf`, que deja conectarse a los clientes que pintan por
|
||||
GPU (apps GPUI, navegadores acelerados). Composición con `GlesRenderer`
|
||||
— en `winit` sobre la ventana, en `drm` con un `DrmCompositor` por
|
||||
salida.
|
||||
|
||||
Reusa `mirada-body` para la contabilidad de salidas y superficies, y
|
||||
`mirada-link` para el cable hacia un Cerebro externo. Toda la lógica
|
||||
|
||||
@@ -710,6 +710,9 @@ pub fn run() -> Result<(), Box<dyn Error>> {
|
||||
// 7 · El estado Wayland (Cerebro, teclado, keymap, control).
|
||||
println!("[7/8] armando el estado Wayland …");
|
||||
let Setup { mut display, mut app, keymap_path, keymap_watch, ctl } = crate::build_app()?;
|
||||
// Con el renderer ya creado, anuncia dmabuf — sin esto las apps que
|
||||
// pintan por GPU (GPUI, navegadores acelerados) no pueden conectarse.
|
||||
crate::announce_dmabuf(&mut app, &display.handle(), &renderer);
|
||||
// La salida del Cerebro = el modo del monitor.
|
||||
let ev = app.body.add_output(0, mode_w as i32, mode_h as i32);
|
||||
app.brain_feed(ev);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::input::{InputEvent, KeyState, KeyboardKeyEvent};
|
||||
use smithay::backend::renderer::element::surface::{
|
||||
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
|
||||
@@ -30,7 +31,7 @@ use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::utils::{
|
||||
draw_render_elements, on_commit_buffer_handler, with_renderer_surface_state,
|
||||
};
|
||||
use smithay::backend::renderer::{Color32F, Frame, Renderer};
|
||||
use smithay::backend::renderer::{Color32F, Frame, ImportDma, Renderer};
|
||||
use smithay::backend::winit::{self, WinitEvent};
|
||||
use smithay::input::keyboard::{xkb, FilterResult, KeyboardHandle, Keysym, ModifiersState};
|
||||
use smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData, PointerHandle};
|
||||
@@ -47,6 +48,7 @@ use smithay::reexports::winit::platform::pump_events::PumpStatus;
|
||||
use smithay::utils::{Rectangle, SERIAL_COUNTER};
|
||||
use smithay::utils::{Serial, Transform};
|
||||
use smithay::wayland::buffer::BufferHandler;
|
||||
use smithay::wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier};
|
||||
use smithay::wayland::compositor::{
|
||||
with_states, with_surface_tree_downward, CompositorClientState, CompositorHandler,
|
||||
CompositorState, SurfaceAttributes, TraversalAction,
|
||||
@@ -63,8 +65,8 @@ use smithay::wayland::shell::xdg::{
|
||||
use smithay::wayland::output::OutputHandler;
|
||||
use smithay::wayland::shm::{ShmHandler, ShmState};
|
||||
use smithay::{
|
||||
delegate_compositor, delegate_data_device, delegate_output, delegate_seat, delegate_shm,
|
||||
delegate_xdg_decoration, delegate_xdg_shell,
|
||||
delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat,
|
||||
delegate_shm, delegate_xdg_decoration, delegate_xdg_shell,
|
||||
};
|
||||
|
||||
use mirada_body::{BodyOp, BodyState};
|
||||
@@ -133,6 +135,9 @@ struct App {
|
||||
compositor_state: CompositorState,
|
||||
xdg_shell_state: XdgShellState,
|
||||
shm_state: ShmState,
|
||||
/// Estado de `zwp_linux_dmabuf` — deja que los clientes con GPU
|
||||
/// (apps GPUI, navegadores acelerados) compartan búferes de vídeo.
|
||||
dmabuf_state: DmabufState,
|
||||
seat_state: SeatState<Self>,
|
||||
data_device_state: DataDeviceState,
|
||||
seat: Seat<Self>,
|
||||
@@ -340,6 +345,24 @@ impl BufferHandler for App {
|
||||
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
|
||||
}
|
||||
|
||||
impl DmabufHandler for App {
|
||||
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
||||
&mut self.dmabuf_state
|
||||
}
|
||||
|
||||
/// Un cliente importó un DMA-BUF. El `GlesRenderer` lo importará de
|
||||
/// verdad al componer; aquí basta con aceptarlo — un búfer inválido
|
||||
/// sólo dejará en blanco ese cuadro de esa ventana.
|
||||
fn dmabuf_imported(
|
||||
&mut self,
|
||||
_global: &DmabufGlobal,
|
||||
_dmabuf: Dmabuf,
|
||||
notifier: ImportNotifier,
|
||||
) {
|
||||
let _ = notifier.successful::<App>();
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmHandler for App {
|
||||
fn shm_state(&self) -> &ShmState {
|
||||
&self.shm_state
|
||||
@@ -494,6 +517,7 @@ impl OutputHandler for App {}
|
||||
delegate_compositor!(App);
|
||||
delegate_xdg_shell!(App);
|
||||
delegate_xdg_decoration!(App);
|
||||
delegate_dmabuf!(App);
|
||||
delegate_shm!(App);
|
||||
delegate_seat!(App);
|
||||
delegate_data_device!(App);
|
||||
@@ -714,6 +738,19 @@ fn announce_output(
|
||||
output
|
||||
}
|
||||
|
||||
/// Anuncia el global `zwp_linux_dmabuf` con los formatos que el
|
||||
/// `GlesRenderer` admite. Hay que llamarlo una vez creado el renderer
|
||||
/// (no antes: los formatos salen de él) — así las apps que pintan por
|
||||
/// GPU (GPUI, navegadores acelerados) pueden ser clientes del compositor.
|
||||
fn announce_dmabuf(app: &mut App, dh: &DisplayHandle, renderer: &GlesRenderer) {
|
||||
let formats: Vec<_> = renderer.dmabuf_formats().into_iter().collect();
|
||||
println!(
|
||||
"mirada-compositor · dmabuf: {} formato(s) anunciado(s).",
|
||||
formats.len()
|
||||
);
|
||||
app.dmabuf_state.create_global::<App>(dh, formats);
|
||||
}
|
||||
|
||||
/// Lo que comparten los dos backends gráficos: el `Display` de Wayland,
|
||||
/// el `App` ya armado y la maquinaria de keymap y control.
|
||||
struct Setup {
|
||||
@@ -767,6 +804,7 @@ fn build_app() -> Result<Setup, Box<dyn std::error::Error>> {
|
||||
compositor_state: CompositorState::new::<App>(&dh),
|
||||
xdg_shell_state: XdgShellState::new::<App>(&dh),
|
||||
shm_state: ShmState::new::<App>(&dh, Vec::new()),
|
||||
dmabuf_state: DmabufState::new(),
|
||||
seat_state,
|
||||
data_device_state: DataDeviceState::new::<App>(&dh),
|
||||
seat,
|
||||
@@ -878,6 +916,9 @@ fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let start = Instant::now();
|
||||
let mut clients = Vec::new();
|
||||
|
||||
// Con el renderer ya creado, anuncia dmabuf (clientes con GPU).
|
||||
announce_dmabuf(&mut state, &display.handle(), backend.renderer());
|
||||
|
||||
// Salida inicial = el tamaño de la ventana winit.
|
||||
let win_size = backend.window_size();
|
||||
let _wl_output = announce_output(&display.handle(), "winit", win_size.w, win_size.h, 60_000);
|
||||
|
||||
@@ -193,10 +193,13 @@ 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).
|
||||
`wl_shm`/`wl_seat`/`wl_output`/`wl_data_device`/`xdg-decoration`/
|
||||
`zwp_linux_dmabuf`, 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). Con `zwp_linux_dmabuf` los
|
||||
clientes que pintan por GPU (apps GPUI, navegadores acelerados) pueden
|
||||
conectarse — el `GlesRenderer` importa sus búferes DMA-BUF al componer.
|
||||
Reusa `mirada-body` (contabilidad) y `mirada-link` (cable). Dos modos de
|
||||
Cerebro: **autónomo** (`Desktop` embebido) o **enlazado** (`MIRADA_SOCKET`
|
||||
→ la app `mirada`).
|
||||
|
||||
@@ -1000,7 +1000,8 @@
|
||||
WAYLAND_DISPLAY=wayland-1 foot # lanza clientes contra él (imprime su WAYLAND_DISPLAY al arrancar)
|
||||
MIRADA_SOCKET=/tmp/mirada.sock cargo run -p mirada-compositor # enlazado: la app mirada (Cerebro GPUI) decide la geometría
|
||||
cargo run -p mirada-compositor -- --drm # nativo sobre TTY (MIRADA_STARTUP=foot lanza un cliente al arrancar)
|
||||
Habla wl_compositor/xdg_shell/wl_shm/wl_seat/wl_data_device; compone con GlesRenderer. Reusa mirada-body y mirada-link.
|
||||
Habla wl_compositor/xdg_shell/wl_shm/wl_seat/wl_data_device/xdg-decoration/zwp_linux_dmabuf; compone con GlesRenderer.
|
||||
dmabuf permite clientes que pintan por GPU (apps GPUI, navegadores acelerados). Reusa mirada-body y mirada-link.
|
||||
En --drm el foco sigue al puntero y clics/rueda van a la ventana debajo; el cursor toma la forma del cliente.
|
||||
Cada ventana lleva un marco fino: azul la enfocada, gris las demás.
|
||||
Super+arrastre mueve la ventana (botón izq.) o la redimensiona (der.) — al arrastrarla pasa a flotar.
|
||||
|
||||
Reference in New Issue
Block a user