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>
This commit is contained in:
@@ -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(())
|
||||||
|
}
|
||||||
@@ -65,6 +65,8 @@ use mirada_brain::{
|
|||||||
};
|
};
|
||||||
use mirada_link::BodyLink;
|
use mirada_link::BodyLink;
|
||||||
|
|
||||||
|
mod drm_backend;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Estado
|
// 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 mut display: Display<App> = Display::new()?;
|
||||||
let dh = display.handle();
|
let dh = display.handle();
|
||||||
|
|
||||||
@@ -785,7 +788,29 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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}");
|
eprintln!("mirada-compositor · error: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user