gioser

tawasuyu/brahman

Rust 472TOML 187Markdown 49JSON 176515 Definiciones

feat(mirada-compositor): backend DRM — fase 1, bring-up

mirada-compositor gana un segundo backend para correr sobre una TTY
pelada, sin sesión gráfica anfitriona. main() elige: --winit / --drm,
o automático (con DISPLAY/WAYLAND_DISPLAY → winit anidado; sin ellos →
DRM). run() pasa a llamarse run_winit().

drm_backend.rs — fase 1 (bring-up), construida para verificarse en
hardware real por etapas:
- abre la sesión con libseat (acceso a DRM/input sin root)
- localiza la GPU primaria (udev::primary_gpu)
- abre el dispositivo DRM por la sesión
- enumera los conectores y sus modos

Todo instrumentado con logs para diagnosticar sin el hardware delante.
La composición (GBM + EGL + GlesRenderer + DrmCompositor + libinput +
bucle calloop) es la fase 2. El backend winit queda intacto.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

6c3a86fbec09254e7e637d9870d08ea3cab6ef12 · por sergio · (hace 5 meses) · padre f9c4bf594 · ver el árbol en este commit

Delta semántico · 5

crates/apps/mirada-compositor/src/drm_backend.rs

  • nueva fn run pub

crates/apps/mirada-compositor/src/main.rs

2 archivos · +147 −2

diff --git a/crates/apps/mirada-compositor/src/drm_backend.rs b/crates/apps/mirada-compositor/src/drm_backend.rs
new file mode 100644
index 0000000..f5e4ae3
--- /dev/null
+++ b/crates/apps/mirada-compositor/src/drm_backend.rs
@@ -0,0 +1,120 @@
+//! `drm_backend` — el Cuerpo del compositor sobre **DRM/KMS**, sin
+//! sesión gráfica anfitriona: corre directo sobre una TTY, como tu
+//! escritorio de verdad.
+//!
+//! Se construye por fases para poder verificarlo en hardware real
+//! paso a paso:
+//!
+//! - **Fase 1 — bring-up** (esto): abre la sesión (`libseat`), encuentra
+//!   la GPU, abre el dispositivo DRM y enumera las salidas físicas. No
+//!   compone nada todavía; sólo comprueba —y registra en el log— que la
+//!   ruta de hardware funciona en tu máquina.
+//! - **Fase 2** (siguiente): GBM + EGL + `GlesRenderer`, un
+//!   `DrmCompositor` por salida, `libinput` para el teclado, y el bucle
+//!   `calloop` que compone de verdad.
+//!
+//! Todo va instrumentado con `println!`/`eprintln!` para que, al
+//! correrlo, se pueda copiar la salida y diagnosticar sin tener el
+//! hardware delante.
+
+use std::error::Error;
+
+use smithay::backend::drm::{DrmDevice, DrmDeviceFd};
+use smithay::backend::session::libseat::LibSeatSession;
+use smithay::backend::session::Session;
+use smithay::backend::udev;
+use smithay::reexports::drm::control::connector::State as ConnectorState;
+use smithay::reexports::drm::control::Device as ControlDevice;
+use smithay::reexports::rustix::fs::OFlags;
+use smithay::utils::DeviceFd;
+
+/// Arranca el Cuerpo sobre DRM/KMS — **fase 1: bring-up**.
+///
+/// Abre la sesión, localiza la GPU, abre el dispositivo DRM y enumera
+/// las salidas, dejando constancia de todo en el log. La composición
+/// real es la fase 2.
+pub fn run() -> Result<(), Box<dyn Error>> {
+    println!("mirada-compositor · backend DRM — fase 1 (bring-up).");
+    println!("──────────────────────────────────────────────────");
+
+    // 1 · La sesión. `libseat` nos da acceso a DRM y a los dispositivos
+    //     de entrada sin ser root — habla con `seatd` o con `logind`.
+    println!("[1/4] abriendo la sesión (libseat) …");
+    let (mut session, _notifier) = LibSeatSession::new().map_err(|e| {
+        format!(
+            "no pude abrir la sesión libseat: {e}\n       \
+             ¿estás en una TTY de verdad (Ctrl+Alt+F3), con `seatd` o \
+             `logind` corriendo?"
+        )
+    })?;
+    let seat_name = session.seat();
+    println!("      sesión abierta · seat «{seat_name}»");
+
+    // 2 · La GPU primaria del seat.
+    println!("[2/4] buscando la GPU primaria …");
+    let gpu = udev::primary_gpu(&seat_name)
+        .map_err(|e| format!("error consultando udev: {e}"))?
+        .ok_or("no encontré ninguna GPU — ¿existe algún /dev/dri/card*?")?;
+    println!("      GPU primaria: {}", gpu.display());
+
+    // 3 · Abrir el dispositivo DRM a través de la sesión.
+    println!("[3/4] abriendo el dispositivo DRM …");
+    let fd = session
+        .open(&gpu, OFlags::RDWR | OFlags::CLOEXEC | OFlags::NONBLOCK)
+        .map_err(|e| format!("no pude abrir {}: {e}", gpu.display()))?;
+    let drm_fd = DrmDeviceFd::new(DeviceFd::from(fd));
+    let (drm, _drm_notifier) =
+        DrmDevice::new(drm_fd, true).map_err(|e| format!("DrmDevice::new falló: {e}"))?;
+    println!("      dispositivo DRM listo.");
+
+    // 4 · Enumerar conectores: cada uno conectado es una salida física.
+    println!("[4/4] enumerando salidas …");
+    let resources = drm
+        .resource_handles()
+        .map_err(|e| format!("no pude leer los recursos DRM: {e}"))?;
+    let mut connected = 0usize;
+    for &handle in resources.connectors() {
+        let info = match drm.get_connector(handle, false) {
+            Ok(info) => info,
+            Err(e) => {
+                eprintln!("      conector {handle:?}: error al leerlo: {e}");
+                continue;
+            }
+        };
+        let name = format!("{:?}-{}", info.interface(), info.interface_id());
+        match info.state() {
+            ConnectorState::Connected => {
+                connected += 1;
+                match info.modes().first() {
+                    Some(mode) => {
+                        let (w, h) = mode.size();
+                        println!(
+                            "      · «{name}» CONECTADA — modo preferido {w}×{h} \
+                             @ {} Hz  ({} modos)",
+                            mode.vrefresh(),
+                            info.modes().len(),
+                        );
+                    }
+                    None => println!("      · «{name}» CONECTADA — sin modos anunciados"),
+                }
+            }
+            ConnectorState::Disconnected => println!("      · «{name}» desconectada"),
+            other => println!("      · «{name}» — estado {other:?}"),
+        }
+    }
+    println!("      {connected} salida(s) conectada(s).");
+
+    println!("──────────────────────────────────────────────────");
+    if connected == 0 {
+        eprintln!("mirada-compositor · bring-up OK, pero no hay ninguna salida");
+        eprintln!("   conectada — sin pantalla no hay nada que componer.");
+    } else {
+        println!("mirada-compositor · bring-up DRM completado correctamente.");
+    }
+    println!(
+        "   La composición sobre DRM es la fase 2. Copia estos logs y\n   \
+         seguimos. Mientras, el backend winit ya funciona dentro de un\n   \
+         escritorio:  mirada-compositor --winit"
+    );
+    Ok(())
+}
diff --git a/crates/apps/mirada-compositor/src/main.rs b/crates/apps/mirada-compositor/src/main.rs
index 27d8120..ac7c338 100644
--- a/crates/apps/mirada-compositor/src/main.rs
+++ b/crates/apps/mirada-compositor/src/main.rs
@@ -65,6 +65,8 @@ use mirada_brain::{
 };
 use mirada_link::BodyLink;
 
+mod drm_backend;
+
 // ---------------------------------------------------------------------
 // Estado
 // ---------------------------------------------------------------------
@@ -511,7 +513,8 @@ fn load_user_rules() -> Rules {
     }
 }
 
-fn run() -> Result<(), Box<dyn std::error::Error>> {
+/// El backend `winit`: corre anidado dentro de una sesión gráfica.
+fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
     let mut display: Display<App> = Display::new()?;
     let dh = display.handle();
 
@@ -785,7 +788,29 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
 }
 
 fn main() {
-    if let Err(e) = run() {
+    let arg = std::env::args().nth(1);
+    let result = match arg.as_deref() {
+        Some("--drm") => drm_backend::run(),
+        Some("--winit") => run_winit(),
+        Some(other) => {
+            eprintln!("mirada-compositor: opción desconocida «{other}» — usa --drm o --winit");
+            std::process::exit(2);
+        }
+        None => {
+            // Auto: con sesión gráfica anfitriona → winit (anidado);
+            // sin ella (una TTY pelada) → backend DRM.
+            let nested = std::env::var_os("WAYLAND_DISPLAY").is_some()
+                || std::env::var_os("DISPLAY").is_some();
+            if nested {
+                println!("mirada-compositor · sesión gráfica detectada → backend winit.");
+                run_winit()
+            } else {
+                println!("mirada-compositor · sin sesión gráfica → backend DRM.");
+                drm_backend::run()
+            }
+        }
+    };
+    if let Err(e) = result {
         eprintln!("mirada-compositor · error: {e}");
         std::process::exit(1);
     }