sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
1#!/bin/sh
2# test-portal-ficheros.sh <app> — ¿se le puede dar DIÁLOGO DE FICHEROS a la imagen de sway?
3#
4# El 2026-10-13 el arnés de ventana destapó que las apps de esta familia abren, pintan y **no pueden
5# abrir un fichero**: `photocraft` lo dice entero —`file dialog backends: Findings { libdbus: None,
6# file_chooser_portal: Some(false), zenity: false }`—. Esto MIDE la salida propuesta: montar sobre el
7# mismo rootfs de sway el `xdg-desktop-portal` (núcleo) y el backend `-cosmic` —que es Rust+wgpu y no
8# arrastra ni GTK ni Qt— más un `sway-portals.conf` que le diga al núcleo quién implementa qué.
9#
10# ⚠ El `.portal` del backend trae `UseIn=COSMIC`, así que en una sesión `sway` NO lo elegiría solo:
11# esa es exactamente la línea que el fichero de configuración existe para suplir.
12set -e
13APP=${1:-photocraft}
14ROOT=${ROOT:-/opt/takana}
15STORE=${STORE:-$ROOT/store}
16RFS=${RFS:-$ROOT/work/sway-rootfs}
17ALPINE=${ALPINE:-$ROOT/.dev-fs/alpine}
18SAL=${SAL:-$ROOT/work/portal-$APP}
19
20hash_de() { "$ROOT/target/release/takana" --store "$STORE" hash "$1" | tail -1 | sed 's/^b3://'; }
21capa() {
22 d="$STORE/$(cd "$ROOT" && hash_de "$2")-$1"
23 [ -d "$d" ] || { echo "!! falta el artefacto vigente de $1 ($d)" >&2; exit 1; }
24 echo "$d"
25}
26
27GC=$(capa "$APP" "recipes/$APP.toml")
28L18=$(capa llvm18 recipes/llvm18.toml)
29MLP=$(capa mesa-llvmpipe recipes/mesa-llvmpipe.toml)
30XDP=$(capa xdg-desktop-portal recipes/incoming-cosmic/xdg-desktop-portal.toml)
31XDPC=$(capa xdg-desktop-portal-cosmic recipes/incoming-cosmic/xdg-desktop-portal-cosmic.toml)
32# ⚠ Sin estas dos capas el NÚCLEO del portal no corre, y cada versión lo dice distinto: la 1.19.4
33# con `Error relocating … json_parser_get_root: symbol not found` y la 1.18.4 con un SIGSEGV pelado
34# después de cargar los backends. Las dos son la misma causa — `json-glib` no está en el perfil de
35# sway— y es la figura de siempre: artefacto sellado, presente, reproducible, y que no arranca.
36JG=$(capa json-glib recipes/incoming-gnome/json-glib.toml)
37FU=$(capa fuse3 recipes/fuse3.toml)
38echo "capas: alpine + sway-rootfs + llvm18 + mesa-llvmpipe + xdg-desktop-portal + -cosmic + $APP"
39
40rm -rf "$SAL"; mkdir -p "$SAL"
41
42cat > "$SAL/dentro.sh" <<EOF
43#!/bin/sh
44# OJO: este heredoc va SIN comillas (necesita expandir \$APP), asi que NADA de backticks aca dentro
45# ni en los comentarios: en un heredoc sin comillas son SUSTITUCION DE COMANDOS. Ya mordio dos veces.
46export XDG_RUNTIME_DIR=/run/user/0 HOME=/salida/hogar LANG=C XDG_DATA_DIRS=/usr/share
47export XDG_CURRENT_DESKTOP=sway
48export WLR_BACKENDS=headless WLR_HEADLESS_OUTPUTS=1 WLR_RENDERER=pixman
49export LIBGL_ALWAYS_SOFTWARE=1 LIBGL_DRIVERS_PATH=/usr/lib/dri GALLIUM_DRIVER=llvmpipe
50export RUST_BACKTRACE=1 RUST_LOG=info
51mkdir -p "\$XDG_RUNTIME_DIR" /salida/hogar; chmod 700 "\$XDG_RUNTIME_DIR"
52
53# El fichero que hace elegible al backend fuera de COSMIC. Son cuatro líneas y es TODO lo que falta
54# del lado de la configuracion.
55mkdir -p /usr/share/xdg-desktop-portal
56cat > /usr/share/xdg-desktop-portal/sway-portals.conf <<'CONF'
57[preferred]
58default=cosmic
59org.freedesktop.impl.portal.FileChooser=cosmic
60CONF
61
62echo "== inventario ==" >> /salida/log.txt
63for f in /usr/libexec/xdg-desktop-portal /usr/libexec/xdg-desktop-portal-cosmic \\
64 /usr/share/dbus-1/services/org.freedesktop.portal.Desktop.service \\
65 /usr/share/dbus-1/services/org.freedesktop.impl.portal.desktop.cosmic.service; do
66 [ -e "\$f" ] && echo " ✓ \$f" >> /salida/log.txt || echo " ✗ FALTA \$f" >> /salida/log.txt
67done
68ls /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
69
70# Bus de sesión: sin él no hay activación por D-Bus y el portal no existe para nadie.
71eval "\$(dbus-launch --sh-syntax 2>/dev/null)" || {
72 dbus-daemon --session --fork --print-address > /salida/dbusaddr 2>>/salida/log.txt
73 export DBUS_SESSION_BUS_ADDRESS="\$(cat /salida/dbusaddr)"
74}
75echo "== bus: \$DBUS_SESSION_BUS_ADDRESS" >> /salida/log.txt
76dbus-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
77
78(
79 i=0
80 while [ ! -S /run/user/0/wayland-1 ] && [ \$i -lt 40 ]; do sleep 1; i=\$((i+1)); done
81 export WAYLAND_DISPLAY=wayland-1
82 export SWAYSOCK=\$(ls /run/user/0/sway-ipc.* 2>/dev/null | head -1)
83 /usr/bin/swaybg -c '#1a4b8c' >/dev/null 2>&1 &
84 sleep 2
85 # ⚠ EL PASO QUE FALTABA, y es el que explica el SIGSEGV de la corrida anterior: un proceso
86 # ACTIVADO POR D-BUS no hereda el entorno de la sesión —ni WAYLAND_DISPLAY, ni
87 # XDG_RUNTIME_DIR, ni XDG_CURRENT_DESKTOP— salvo que alguien se lo pase al bus. Eso es
88 # dbus-update-activation-environment, y es exactamente lo que una sesión sway de verdad corre
89 # al arrancar. Sin esto el portal se activa ciego y se cae.
90 /usr/bin/dbus-update-activation-environment --all >> /salida/log.txt 2>&1 || echo " ⚠ dbus-update-activation-environment falló" >> /salida/log.txt
91 # El backend se arranca a mano (en una sesión lo lanza el compositor); el NÚCLEO se deja activar
92 # por D-Bus a propósito, que es el camino real que usa una app.
93 /usr/libexec/xdg-desktop-portal-cosmic >> /salida/portal-cosmic.log 2>&1 &
94 sleep 3
95 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
96 # ── LA PRUEBA DE VERDAD: pedirle un diálogo al portal y ver si APARECE UNA VENTANA ──────────
97 # La sonda de la app no alcanza: contesta libdbus: None y eso no distingue «no hay portal» de
98 # «no supe preguntar». Acá se llama al método REAL que usaría cualquier app (OpenFile) y la
99 # evidencia es la misma de siempre: un toplevel nuevo en el árbol de sway y una captura con
100 # colores. gdbus y no dbus-send porque el tercer argumento es un diccionario vacío.
101 timeout 25 /usr/bin/gdbus call --session --dest org.freedesktop.portal.Desktop \
102 --object-path /org/freedesktop/portal/desktop \
103 --method org.freedesktop.portal.FileChooser.OpenFile "" "Elegi un fichero" "{}" \
104 >> /salida/openfile.log 2>&1 &
105 sleep 12
106 /usr/bin/swaymsg -t get_tree -r > /salida/arbol.json 2>/dev/null
107 /usr/bin/grim /salida/captura.png 2>/dev/null
108 # Sin esto el bwrap NO vuelve: los dos portales son demonios y --unshare-pid espera a TODOS los
109 # procesos del namespace, no sólo a sway. Costó un timeout de 500 s descubrirlo.
110 pkill -f xdg-desktop-portal 2>/dev/null
111 sleep 1
112 /usr/bin/swaymsg exit 2>/dev/null
113) &
114exec /usr/bin/sway -d > /salida/sway.log 2>&1
115EOF
116
117bwrap \
118 --overlay-src "$ALPINE" --overlay-src "$RFS" \
119 --overlay-src "$L18" --overlay-src "$MLP" \
120 --overlay-src "$JG" --overlay-src "$FU" --overlay-src "$XDP" --overlay-src "$XDPC" --overlay-src "$GC" \
121 --tmp-overlay / \
122 --dev-bind /dev /dev --proc /proc --bind /sys /sys \
123 --tmpfs /tmp --tmpfs /run --unshare-pid \
124 --bind "$SAL" /salida \
125 --uid 0 --gid 0 \
126 --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \
127 /bin/sh /salida/dentro.sh || echo "(bwrap salió con $?)"
128
129echo "── ¿APARECIÓ el diálogo? ─────────────────────────────────"
130grep -c '"app_id"' "$SAL/arbol.json" 2>/dev/null | sed 's/^/ toplevels en el árbol: /'
131grep -oE '"app_id": "[^"]*"' "$SAL/arbol.json" 2>/dev/null | sort -u | sed 's/^/ /'
132cat "$SAL/openfile.log" 2>/dev/null | sed 's/^/ OpenFile: /'
133echo "── lo que vio la sonda de la app ─────────────────────────"
134grep -i "file dialog backends" "$SAL/app.log" 2>/dev/null || echo " (la app no llegó a sondear)"
135echo "── el bus y los ficheros ─────────────────────────────────"
136cat "$SAL/log.txt" 2>/dev/null | sed 's/^/ /'
137echo "── el portal dijo ────────────────────────────────────────"
138tail -6 "$SAL/portal-core.log" 2>/dev/null | sed 's/^/ core: /'
139tail -4 "$SAL/portal-cosmic.log" 2>/dev/null | sed 's/^/ cosmic: /'