From 8cd34bf17311d89f73e89de51c8a61ce203ca453 Mon Sep 17 00:00:00 2001 From: sergio Date: Thu, 21 May 2026 02:35:06 +0000 Subject: [PATCH] =?UTF-8?q?fix(mirada-compositor):=20anunciar=20un=20wl=5F?= =?UTF-8?q?output=20=E2=80=94=20los=20clientes=20lo=20exigen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../apps/mirada-compositor/src/drm_backend.rs | 8 +++ crates/apps/mirada-compositor/src/main.rs | 49 +++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/crates/apps/mirada-compositor/src/drm_backend.rs b/crates/apps/mirada-compositor/src/drm_backend.rs index 18eda47..8946c52 100644 --- a/crates/apps/mirada-compositor/src/drm_backend.rs +++ b/crates/apps/mirada-compositor/src/drm_backend.rs @@ -327,6 +327,14 @@ pub fn run() -> Result<(), Box> { // 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); + // Anuncia el monitor en el protocolo Wayland — los clientes lo exigen. + let _wl_output = crate::announce_output( + &display.handle(), + &out_name, + mode_w as i32, + mode_h as i32, + mode.vrefresh() as i32 * 1000, + ); // El socket Wayland por el que se conectan los clientes. let listener = ListeningSocket::bind_auto("wayland", 1..32)?; diff --git a/crates/apps/mirada-compositor/src/main.rs b/crates/apps/mirada-compositor/src/main.rs index 7e64689..9340ea3 100644 --- a/crates/apps/mirada-compositor/src/main.rs +++ b/crates/apps/mirada-compositor/src/main.rs @@ -37,7 +37,7 @@ use smithay::reexports::wayland_server::protocol::wl_buffer; use smithay::reexports::wayland_server::protocol::wl_output; use smithay::reexports::wayland_server::protocol::wl_seat; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::reexports::wayland_server::{Client, Display, ListeningSocket}; +use smithay::reexports::wayland_server::{Client, Display, DisplayHandle, ListeningSocket}; use smithay::reexports::winit::platform::pump_events::PumpStatus; use smithay::utils::{Rectangle, SERIAL_COUNTER}; use smithay::utils::{Serial, Transform}; @@ -54,9 +54,11 @@ use smithay::wayland::shell::xdg::{ PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState, XdgToplevelSurfaceData, }; +use smithay::wayland::output::OutputHandler; use smithay::wayland::shm::{ShmHandler, ShmState}; use smithay::{ - delegate_compositor, delegate_data_device, delegate_seat, delegate_shm, delegate_xdg_shell, + delegate_compositor, delegate_data_device, delegate_output, delegate_seat, delegate_shm, + delegate_xdg_shell, }; use mirada_body::{BodyOp, BodyState}; @@ -411,11 +413,16 @@ impl SeatHandler for App { } } +/// El protocolo `wl_output` no necesita estado propio — basta con +/// anunciar el global para que los clientes vean que hay un monitor. +impl OutputHandler for App {} + delegate_compositor!(App); delegate_xdg_shell!(App); delegate_shm!(App); delegate_seat!(App); delegate_data_device!(App); +delegate_output!(App); // --------------------------------------------------------------------- // Datos por cliente @@ -513,6 +520,39 @@ fn load_user_rules() -> Rules { } } +/// Crea y anuncia un `wl_output` (un monitor) en el protocolo Wayland — +/// muchos clientes (`foot` entre ellos) se niegan a arrancar sin uno. +/// Devuelve el [`Output`](smithay::output::Output); hay que mantenerlo +/// vivo mientras el compositor corra. +fn announce_output( + dh: &DisplayHandle, + name: &str, + width: i32, + height: i32, + refresh_mhz: i32, +) -> smithay::output::Output { + use smithay::output::{Mode, Output, PhysicalProperties, Scale, Subpixel}; + let output = Output::new( + name.to_string(), + PhysicalProperties { + size: (0, 0).into(), + subpixel: Subpixel::Unknown, + make: "mirada".into(), + model: name.to_string(), + }, + ); + output.create_global::(dh); + let mode = Mode { size: (width, height).into(), refresh: refresh_mhz }; + output.change_current_state( + Some(mode), + Some(Transform::Normal), + Some(Scale::Integer(1)), + Some((0, 0).into()), + ); + output.set_preferred(mode); + output +} + /// 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 { @@ -668,9 +708,10 @@ fn run_winit() -> Result<(), Box> { let mut clients = Vec::new(); // 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); { - let size = backend.window_size(); - let ev = state.body.add_output(0, size.w, size.h); + let ev = state.body.add_output(0, win_size.w, win_size.h); state.brain_feed(ev); }