🎥 cosmic: pipewire ARRANCA, y el portal ya figura como cliente suyo

El bloqueo decisivo de ScreenCast no era una receta que faltara: `/usr/bin/pipewire`
estaba en la imagen desde que entró como dep de `cosmic-settings-daemon`, pero NADIE
lo lanzaba. En una distro con systemd de usuario eso lo hace `pipewire.socket`; acá
no hay tal cosa, así que es tarea de `cosmic-start` — el mismo patrón que el bus de
sistema y que login1.

Va justo después de asegurar XDG_RUNTIME_DIR y NO junto al bus de sesión, a
propósito: pipewire no necesita D-Bus para arrancar, sólo el runtime dir donde abre
su socket. Ahí sirve para los dos modos (bare y session) sin duplicar el bloque. Y se
espera EL SOCKET, no el pid: un demonio que muere al instante deja el pid existiendo
un rato, y esperar por él da un OK falso.

Medido dentro de la VM, cada paso una pregunta distinta:

  pipewire OK (socket pipewire-0, pid 189)      ¿abre socket?
  pw-cli info 0  → Core/4, 1.2.7, core.daemon   ¿SIRVE a un cliente?
  pw-cli ls Factory → "client-node"             ¿tiene lo que ScreenCast usa?
  pw-cli ls Client  → "xdg-desktop-portal"      ¡el portal YA está conectado!

La última línea es el veredicto: el frontend del portal aparece como cliente del
demonio, una conexión que antes no podía existir y que es justo la que `Start`
necesita para negociar el nodo. `client-node` es la factory con que el backend lo crea.

Sin regresión, con la cadena entera: `cosmic-screenshot` levanta la UI INTERACTIVA
del portal (región/ventana/pantalla + Capture + Save to Pictures) y el click deja un
PNG de 53.590 bytes. Esa UI no estaba documentada — el registro anterior sólo probaba
la ruta no-interactiva. Escritorio completo: 2414 colores (el panel mutilado da 130).

Lo que sigue faltando son DOS cosas distintas, y ninguna es un one-liner:
  · el handshake de punta a punta pide un cliente `ashpd` PERSISTENTE — el portal
    asocia la sesión al *sender*, así que CreateSession con un dbus-send y
    SelectSources con otro se rechazan por venir de nombres únicos distintos;
  · wireplumber, que separa "hay stream de video" de "hay audio". Ni construido está.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-05 09:46:21 -04:00
co-authored by Claude Opus 5
parent 2e26a45060
commit e43ce95786
4 changed files with 94 additions and 9 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

+55 -3
View File
@@ -966,6 +966,57 @@ aunque se escribiera el cliente `ashpd`, `Start` no tendría con quién negociar
Arreglo: lanzarlo desde `cosmic-start`. (Para AUDIO además falta `wireplumber`, que ni siquiera está
construido — ver la decisión de audio del proyecto.)
### ✅ HECHO: el demonio arranca y el portal se conecta a él (2026-08-05)
`cosmic-start` lanza `/usr/bin/pipewire` justo después de asegurar `XDG_RUNTIME_DIR`, y espera **el
socket, no el pid** — la misma disciplina que el compositor, y por el mismo motivo: un demonio que
muere al instante deja el pid «existiendo» un rato y esperar por él da un OK falso.
Va en ese punto del script a propósito: pipewire **no necesita D-Bus** para arrancar, sólo el
runtime dir donde abre su socket. Ponerlo ahí lo hace válido para los dos modos —`bare` y
`session`— sin duplicar el bloque.
Lo medido dentro de la VM, en este orden, porque cada paso responde una pregunta distinta:
```
== cosmic-qemu :: pipewire OK (socket pipewire-0, pid 189) ← ¿abre socket?
$ pw-cli info 0
type: PipeWire:Interface:Core/4 version: "1.2.7" ← ¿SIRVE a un cliente?
core.daemon = "true" core.name = "pipewire-0"
$ pw-cli ls Factory | grep factory.name
… "client-node" … ← ¿tiene lo que ScreenCast usa?
$ pw-cli ls Client | grep application.name
application.name = "pw-cli"
application.name = "xdg-desktop-portal" ← ¡EL PORTAL YA ESTÁ CONECTADO!
```
La última línea es el veredicto. **El frontend del portal aparece como cliente del demonio de
pipewire** — una conexión que antes no podía existir, y que es exactamente la que `Start` necesita
para negociar el nodo del stream. `client-node` es la factory con la que el backend lo crea.
**Y sin regresión, medido con la cadena entera**: `cosmic-screenshot` desde `cosmic-term` levanta la
**UI interactiva del portal** —un toolbar con los tres modos (región, ventana, pantalla), `Capture` y
`Save to Pictures`—, y el click en `Capture` deja un PNG de 53.590 bytes en
`/root/Pictures/Screenshots/`. Evidencia: `docs/evidencia/cosmic-portal-ui-captura-2026-08-05.png`.
Esa UI no estaba documentada: el registro anterior sólo probaba la ruta no-interactiva.
El escritorio completo con pipewire corriendo:
`docs/evidencia/cosmic-escritorio-con-pipewire-2026-08-05.png` (2414 colores distintos — el panel
mutilado da 130).
**Lo que sigue faltando, y son dos cosas separadas:**
- **El handshake de ScreenCast de punta a punta** necesita un cliente `ashpd` **persistente**, y no
hay atajo: el portal asocia la sesión al *sender* de D-Bus, así que `CreateSession` con un
`dbus-send` y `SelectSources` con otro se rechazan por venir de nombres únicos distintos. Un
cliente de prueba es una receta nueva, no un one-liner.
- **`wireplumber`**, que es lo que separa «hay stream de video» de «hay audio». Sin gestor de sesión
pipewire arranca y sirve, pero no descubre tarjetas ni enruta. Para ScreenCast alcanza; para sonido
no. Ni siquiera está construido.
**Y de paso se corrigió un comentario desactualizado de `cosmic-start`** que decía que
`cosmic-settings-daemon` «todavía no se puede construir acá (arrastra pipewire)». Es falso hace
tiempo: ese daemon está sellado y corre. Un comentario viejo manda a buscar un bloqueo que ya no
@@ -973,9 +1024,10 @@ existe, y eso cuesta igual que un bug.
## Lo que falta, en orden
1. **Arrancar el demonio de pipewire desde `cosmic-start`**, que es lo que destraba ScreenCast de
verdad; después, un cliente `ashpd` para ejercer el handshake y, de paso, capturar la URI del
`FileChooser` que `dbus-send` no puede ver.
1. ~~Arrancar el demonio de pipewire desde `cosmic-start`~~ **✅ hecho (2026-08-05)**, y el portal ya
figura como cliente suyo. Queda el **cliente `ashpd` persistente** para ejercer el handshake de
ScreenCast y, de paso, capturar la URI del `FileChooser` que `dbus-send` no puede ver — es una
receta, no un one-liner, por lo del *sender* de D-Bus explicado arriba. Y `wireplumber` para audio.
2. **Decidir `gvfs`** (ver la corrección de arriba: las piezas ya están selladas, lo que falta es la
decisión sobre la segunda glib). Destrabaría los montajes remotos de cosmic-files y su applet.
3. **Enchufar la tienda a `.swm`**: servir `org.freedesktop.PackageKit` y encender la feature
+39 -6
View File
@@ -134,6 +134,42 @@ export XDG_DATA_DIRS=/usr/share XDG_CONFIG_DIRS=/etc/xdg
mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
[ -w "$XDG_RUNTIME_DIR" ] || say "!! XDG_RUNTIME_DIR se perdió entre el arranque y ahora"
# ── EL DEMONIO DE PIPEWIRE ──────────────────────────────────────────────────────────────────────
# Faltaba, y el hueco no se veía: `/usr/bin/pipewire` está en la imagen desde que entró como dep de
# `cosmic-settings-daemon`, pero NADIE lo arrancaba. En una distro con systemd de usuario lo levanta
# `pipewire.socket`; acá no hay tal cosa, así que es tarea de este script — el mismo patrón que el
# bus de sistema y que login1.
#
# La consecuencia medida de que faltara: `org.freedesktop.portal.ScreenCast.CreateSession` ACEPTA y
# devuelve su objeto Request —o sea que la cadena del portal está bien—, pero no puede haber stream,
# porque el backend negocia el nodo contra un demonio que no existe. Un fallo que se manifiesta como
# «no pasa nada» tres capas más arriba de la causa.
#
# Va ACÁ y no junto al bus de sesión a propósito: pipewire no necesita D-Bus para arrancar, sólo
# XDG_RUNTIME_DIR (es donde abre su socket). Ponerlo justo después de asegurar ese directorio lo hace
# válido para los DOS modos —bare y session— sin duplicar el bloque.
#
# ⚠ Sin `wireplumber` (el gestor de sesión, que ni siquiera está construido) pipewire arranca y sirve,
# pero no gestiona dispositivos: no descubre tarjetas ni enruta audio. Para SCREENCAST eso alcanza —
# el backend del portal crea su propio nodo de video— y para AUDIO no. Son dos preguntas distintas y
# esta pieza sólo contesta la primera.
if [ -x /usr/bin/pipewire ]; then
/usr/bin/pipewire >/tmp/pipewire.log 2>&1 &
PW_PID=$!
# El socket, no el pid: misma disciplina que el compositor. `pipewire-0` es el nombre por defecto
# (`core.name` en pipewire.conf); si el demonio muere al instante, el pid sigue "existiendo" un
# rato y esperar por él daría un OK falso.
i=0
while [ $i -lt 20 ]; do
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && { say "pipewire OK (socket pipewire-0, pid $PW_PID)"; break; }
alive $PW_PID || { say "!! pipewire MURIÓ antes de abrir el socket:"; dump /tmp/pipewire.log; break; }
i=$((i+1)); sleep 0.5
done
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] || { say "!! pipewire sin socket en $((i/2))s:"; dump /tmp/pipewire.log; }
else
say "!! sin /usr/bin/pipewire — ScreenCast no va a poder crear stream"
fi
# ── FONDO: color liso, porque la imagen por defecto NO SE PUEDE EMPAQUETAR ────────────────────
# El default de cosmic-bg apunta a `/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg`,
# que vive en el repo `cosmic-wallpapers`… en **git-lfs**: el tarball de GitHub pesa 20 KB y trae
@@ -180,12 +216,9 @@ fi
# sellado y corre, y `pipewire` está en la imagen. Se deja el rastro porque el comentario viejo
# mandaba a buscar un bloqueo que ya no existe.
#
# Lo que SÍ falta, y es otra cosa: **nadie ARRANCA el demonio de pipewire**. `/usr/bin/pipewire` y
# `pipewire-pulse` están instalados, pero `ps ax | grep pipewire` en la sesión no devuelve nada, y
# este script no lo lanza. Consecuencia medida: el portal acepta
# `org.freedesktop.portal.ScreenCast.CreateSession` y devuelve su objeto Request, pero **no puede
# haber stream** porque no hay demonio con quien negociarlo. Sin systemd de usuario, lanzarlo es
# tarea de este script. (Para audio además falta `wireplumber`, que ni siquiera está construido.)
# Lo que SÍ faltaba, y era otra cosa, **ya está**: nadie arrancaba el demonio de pipewire. Se
# corrigió arriba (bloque «EL DEMONIO DE PIPEWIRE», justo después de XDG_RUNTIME_DIR). Queda
# `wireplumber` sin construir, que es lo que separa «hay stream de video» de «hay audio».
#
# El modo `bare` es la otra mitad que upstream ya soporta (`make install-bare-session`): el
# compositor se lanza SOLO y los clientes se levantan a mano contra su WAYLAND_DISPLAY. Sirve para lo