feat(mirada): sesión de escritorio — autostart y conmutación de VT

Dos piezas para usar carmen como tu escritorio de verdad.

Conmutación de VT — `Ctrl+Alt+Fn` salta a otra TTY y vuelve sin romper
la sesión. El `SessionEvent` de `libseat` ahora hace trabajo de verdad:
- al ceder la VT, pausa el `DrmDevice` y suspende `libinput`; `render()`
  no vuelve a tocar la GPU mientras la sesión esté cedida (`active`).
- al recuperarla, reanuda `libinput`, reactiva el `DrmDevice`, llama a
  `DrmCompositor::reset_state` y repinta.
`DrmState` conserva ahora `drm` y un clon del contexto `libinput`.

Sesión — `~/.config/mirada/autostart` (un comando por línea, `#`
comenta) se lanza al arrancar el backend DRM, vía un `spawn_autostart`
que reusa `spawn_command`. Y `session/`: el script `mirada-session`
(fija el entorno XDG y exec del compositor) y `carmen.desktop` para
registrarlo en un gestor de login, más un `autostart.example`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 04:31:55 +00:00
parent 58e72c3d08
commit 5ede927d34
8 changed files with 172 additions and 8 deletions
+32 -2
View File
@@ -630,8 +630,8 @@ fn cursor_hotspot(surface: &WlSurface) -> (i32, i32) {
/// Lanza un comando como proceso hijo, vía `sh -c`. El hijo hereda el
/// entorno —`WAYLAND_DISPLAY` incluido—, así que el cliente que abra se
/// conecta a este compositor. Lo usan la acción `spawn:…` del keymap y
/// la variable `MIRADA_STARTUP`.
/// conecta a este compositor. Lo usan la acción `spawn:…` del keymap, la
/// variable `MIRADA_STARTUP` y el autoarranque.
fn spawn_command(cmd: &str) {
let cmd = cmd.trim();
if cmd.is_empty() {
@@ -643,6 +643,36 @@ fn spawn_command(cmd: &str) {
}
}
/// La ruta del archivo de autoarranque del usuario,
/// `~/.config/mirada/autostart` — junto al keymap y las reglas.
fn autostart_path() -> Option<std::path::PathBuf> {
Keymap::default_path().and_then(|p| p.parent().map(|d| d.join("autostart")))
}
/// Lanza los programas del archivo de autoarranque: un comando por
/// línea, `#` comenta y las líneas en blanco se saltan. Sin archivo, no
/// hace nada. Se llama una vez al arrancar, con el socket ya abierto.
fn spawn_autostart() {
let Some(path) = autostart_path() else {
return;
};
let Ok(text) = std::fs::read_to_string(&path) else {
return; // no hay archivo de autoarranque
};
let mut n = 0;
for line in text.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
spawn_command(line);
n += 1;
}
if n > 0 {
println!("mirada-compositor · autoarranque: {n} programa(s) desde {}", path.display());
}
}
/// Carga las reglas de ventana del usuario, o ninguna si no hay archivo.
fn load_user_rules() -> Rules {
match Rules::default_path() {