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>
This commit is contained in:
sergio
2026-05-21 02:35:06 +00:00
parent fb2f3a2d01
commit 8cd34bf173
2 changed files with 53 additions and 4 deletions
@@ -327,6 +327,14 @@ pub fn run() -> Result<(), Box<dyn Error>> {
// 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)?;
+45 -4
View File
@@ -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::<App>(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<dyn std::error::Error>> {
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);
}