Los portales NO son RPC: son un protocolo asíncrono en dos tiempos. Cada método
devuelve al instante un object path de `Request`, y el resultado llega DESPUÉS como
señal `Response` sobre ese path. `dbus-send` ya salió para entonces — por eso la URI
del FileChooser nunca se pudo capturar desde el shell.
Y hay una razón más dura: el portal ata la sesión al SENDER. CreateSession con un
dbus-send y SelectSources con otro llegan de nombres únicos distintos y el segundo se
rechaza. Hace falta UNA conexión viva durante todo el intercambio.
El path del Request se PREDICE (/…/request/<sender>/<token>, sender = nombre único
sin ':' y con '.'→'_'). Eso existe a propósito: permite suscribirse ANTES de llamar.
Esperar al path devuelto para recién ahí hacer AddMatch es una carrera real — el
portal puede haber emitido ya la señal.
En C y no con ashpd: el árbol fuente de esta receta es el repo de la propia
herramienta de build, así que ashpd significaría sumar ~150 crates al Cargo.lock de
hammer por una sonda de diagnóstico. libdbus ya está en la imagen y en el corpus
(y con `libdbus-1.a`, así que la sonda sale ESTÁTICA, sin un solo NEEDED). Sobre
todo: acá el protocolo se VE. Una sonda cuyo valor es documentar un handshake no
debería esconderlo tras una biblioteca que lo abstrae.
Cuatro modos: `version` (qué interfaces sirve y en qué versión), `screencast` (el
handshake de 4 pasos hasta el fd de pipewire), `filechooser` (captura las uris) y
`screenshot`. El impresor de valores es recursivo y NO filtra: si el portal devuelve
un campo que este programa no conoce, sale igual por pantalla — que es exactamente lo
que uno quiere de una sonda.
Validado en el host contra el portal real: compila sin warnings y lee las versiones
(Screenshot v2, FileChooser v4, Settings v2).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>