#!/bin/sh # test-portal-ficheros.sh — ¿se le puede dar DIÁLOGO DE FICHEROS a la imagen de sway? # # El 2026-10-13 el arnés de ventana destapó que las apps de esta familia abren, pintan y **no pueden # abrir un fichero**: `photocraft` lo dice entero —`file dialog backends: Findings { libdbus: None, # file_chooser_portal: Some(false), zenity: false }`—. Esto MIDE la salida propuesta: montar sobre el # mismo rootfs de sway el `xdg-desktop-portal` (núcleo) y el backend `-cosmic` —que es Rust+wgpu y no # arrastra ni GTK ni Qt— más un `sway-portals.conf` que le diga al núcleo quién implementa qué. # # ⚠ El `.portal` del backend trae `UseIn=COSMIC`, así que en una sesión `sway` NO lo elegiría solo: # esa es exactamente la línea que el fichero de configuración existe para suplir. set -e APP=${1:-photocraft} ROOT=${ROOT:-/opt/takana} STORE=${STORE:-$ROOT/store} RFS=${RFS:-$ROOT/work/sway-rootfs} ALPINE=${ALPINE:-$ROOT/.dev-fs/alpine} SAL=${SAL:-$ROOT/work/portal-$APP} hash_de() { "$ROOT/target/release/takana" --store "$STORE" hash "$1" | tail -1 | sed 's/^b3://'; } capa() { d="$STORE/$(cd "$ROOT" && hash_de "$2")-$1" [ -d "$d" ] || { echo "!! falta el artefacto vigente de $1 ($d)" >&2; exit 1; } echo "$d" } GC=$(capa "$APP" "recipes/$APP.toml") L18=$(capa llvm18 recipes/llvm18.toml) MLP=$(capa mesa-llvmpipe recipes/mesa-llvmpipe.toml) XDP=$(capa xdg-desktop-portal recipes/incoming-cosmic/xdg-desktop-portal.toml) XDPC=$(capa xdg-desktop-portal-cosmic recipes/incoming-cosmic/xdg-desktop-portal-cosmic.toml) # ⚠ Sin estas dos capas el NÚCLEO del portal no corre, y cada versión lo dice distinto: la 1.19.4 # con `Error relocating … json_parser_get_root: symbol not found` y la 1.18.4 con un SIGSEGV pelado # después de cargar los backends. Las dos son la misma causa — `json-glib` no está en el perfil de # sway— y es la figura de siempre: artefacto sellado, presente, reproducible, y que no arranca. JG=$(capa json-glib recipes/incoming-gnome/json-glib.toml) FU=$(capa fuse3 recipes/fuse3.toml) echo "capas: alpine + sway-rootfs + llvm18 + mesa-llvmpipe + xdg-desktop-portal + -cosmic + $APP" rm -rf "$SAL"; mkdir -p "$SAL" cat > "$SAL/dentro.sh" < /usr/share/xdg-desktop-portal/sway-portals.conf <<'CONF' [preferred] default=cosmic org.freedesktop.impl.portal.FileChooser=cosmic CONF echo "== inventario ==" >> /salida/log.txt for f in /usr/libexec/xdg-desktop-portal /usr/libexec/xdg-desktop-portal-cosmic \\ /usr/share/dbus-1/services/org.freedesktop.portal.Desktop.service \\ /usr/share/dbus-1/services/org.freedesktop.impl.portal.desktop.cosmic.service; do [ -e "\$f" ] && echo " ✓ \$f" >> /salida/log.txt || echo " ✗ FALTA \$f" >> /salida/log.txt done ls /usr/lib/libdbus-1.so* >/dev/null 2>&1 && echo " ✓ libdbus-1.so presente" >> /salida/log.txt || echo " ✗ NO hay libdbus-1.so (el corpus sólo saca .a)" >> /salida/log.txt # Bus de sesión: sin él no hay activación por D-Bus y el portal no existe para nadie. eval "\$(dbus-launch --sh-syntax 2>/dev/null)" || { dbus-daemon --session --fork --print-address > /salida/dbusaddr 2>>/salida/log.txt export DBUS_SESSION_BUS_ADDRESS="\$(cat /salida/dbusaddr)" } echo "== bus: \$DBUS_SESSION_BUS_ADDRESS" >> /salida/log.txt dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListActivatableNames 2>/dev/null | grep -i portal >> /salida/log.txt || echo " (ningún nombre de portal activable)" >> /salida/log.txt ( i=0 while [ ! -S /run/user/0/wayland-1 ] && [ \$i -lt 40 ]; do sleep 1; i=\$((i+1)); done export WAYLAND_DISPLAY=wayland-1 export SWAYSOCK=\$(ls /run/user/0/sway-ipc.* 2>/dev/null | head -1) /usr/bin/swaybg -c '#1a4b8c' >/dev/null 2>&1 & sleep 2 # ⚠ EL PASO QUE FALTABA, y es el que explica el SIGSEGV de la corrida anterior: un proceso # ACTIVADO POR D-BUS no hereda el entorno de la sesión —ni WAYLAND_DISPLAY, ni # XDG_RUNTIME_DIR, ni XDG_CURRENT_DESKTOP— salvo que alguien se lo pase al bus. Eso es # dbus-update-activation-environment, y es exactamente lo que una sesión sway de verdad corre # al arrancar. Sin esto el portal se activa ciego y se cae. /usr/bin/dbus-update-activation-environment --all >> /salida/log.txt 2>&1 || echo " ⚠ dbus-update-activation-environment falló" >> /salida/log.txt # El backend se arranca a mano (en una sesión lo lanza el compositor); el NÚCLEO se deja activar # por D-Bus a propósito, que es el camino real que usa una app. /usr/libexec/xdg-desktop-portal-cosmic >> /salida/portal-cosmic.log 2>&1 & sleep 3 dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2>/dev/null | grep -i portal >> /salida/log.txt || echo " (ningún portal EN el bus)" >> /salida/log.txt # ── LA PRUEBA DE VERDAD: pedirle un diálogo al portal y ver si APARECE UNA VENTANA ────────── # La sonda de la app no alcanza: contesta libdbus: None y eso no distingue «no hay portal» de # «no supe preguntar». Acá se llama al método REAL que usaría cualquier app (OpenFile) y la # evidencia es la misma de siempre: un toplevel nuevo en el árbol de sway y una captura con # colores. gdbus y no dbus-send porque el tercer argumento es un diccionario vacío. timeout 25 /usr/bin/gdbus call --session --dest org.freedesktop.portal.Desktop \ --object-path /org/freedesktop/portal/desktop \ --method org.freedesktop.portal.FileChooser.OpenFile "" "Elegi un fichero" "{}" \ >> /salida/openfile.log 2>&1 & sleep 12 /usr/bin/swaymsg -t get_tree -r > /salida/arbol.json 2>/dev/null /usr/bin/grim /salida/captura.png 2>/dev/null # Sin esto el bwrap NO vuelve: los dos portales son demonios y --unshare-pid espera a TODOS los # procesos del namespace, no sólo a sway. Costó un timeout de 500 s descubrirlo. pkill -f xdg-desktop-portal 2>/dev/null sleep 1 /usr/bin/swaymsg exit 2>/dev/null ) & exec /usr/bin/sway -d > /salida/sway.log 2>&1 EOF bwrap \ --overlay-src "$ALPINE" --overlay-src "$RFS" \ --overlay-src "$L18" --overlay-src "$MLP" \ --overlay-src "$JG" --overlay-src "$FU" --overlay-src "$XDP" --overlay-src "$XDPC" --overlay-src "$GC" \ --tmp-overlay / \ --dev-bind /dev /dev --proc /proc --bind /sys /sys \ --tmpfs /tmp --tmpfs /run --unshare-pid \ --bind "$SAL" /salida \ --uid 0 --gid 0 \ --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \ /bin/sh /salida/dentro.sh || echo "(bwrap salió con $?)" echo "── ¿APARECIÓ el diálogo? ─────────────────────────────────" grep -c '"app_id"' "$SAL/arbol.json" 2>/dev/null | sed 's/^/ toplevels en el árbol: /' grep -oE '"app_id": "[^"]*"' "$SAL/arbol.json" 2>/dev/null | sort -u | sed 's/^/ /' cat "$SAL/openfile.log" 2>/dev/null | sed 's/^/ OpenFile: /' echo "── lo que vio la sonda de la app ─────────────────────────" grep -i "file dialog backends" "$SAL/app.log" 2>/dev/null || echo " (la app no llegó a sondear)" echo "── el bus y los ficheros ─────────────────────────────────" cat "$SAL/log.txt" 2>/dev/null | sed 's/^/ /' echo "── el portal dijo ────────────────────────────────────────" tail -6 "$SAL/portal-core.log" 2>/dev/null | sed 's/^/ core: /' tail -4 "$SAL/portal-cosmic.log" 2>/dev/null | sed 's/^/ cosmic: /'