gioser

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…

portal de ficheros en sway: MEDIDO de punta a punta — el diálogo abre, y el núcleo no arranca sin json-glib (SIGSEGV mudo en 1.18.4)

f47b56c10406419b1238184ddae9179c9778c884 · ruwaq Sergio · (8 p'unchaw ñawpaq) · tayta c1e8257a5 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

2 qillqakuna · +177 −5

diff --git a/docs/plan-apps-usuario-final.md b/docs/plan-apps-usuario-final.md
index 7fda9487..3c235036 100644
--- a/docs/plan-apps-usuario-final.md
+++ b/docs/plan-apps-usuario-final.md
@@ -504,11 +504,44 @@ desbloqueo es el mismo que ya pedía el ScreenCast de COSMIC: **lavapipe** (`-Dv
 
 ## Tres huecos de la IMAGEN que estas apps destaparon (ninguno es de ellas)
 
-1. **No hay diálogo de ficheros.** `photocraft` lo dice entero:
-   `file dialog backends: Findings { libdbus: None, file_chooser_portal: Some(false), zenity: false }`
-   ⇒ Abrir y Guardar no tienen con qué. `xdg-desktop-portal` existe en las colas de kde, gnome y
-   cosmic —los tres perfiles lo declaran— y **no hay ninguno en `incoming-wlr` ni en el corpus**.
-   Es la receta que destraba a las doce de un solo tiro.
+1. **No hay diálogo de ficheros EN SWAY** —y esta entrada se corrigió dos veces, que es casi todo
+   lo que tiene de interesante—. Lo que `photocraft` dice es
+   `file dialog backends: Findings { libdbus: None, file_chooser_portal: Some(false), zenity: false }`,
+   y de ahí salió primero un «no hay diálogo de ficheros» a secas: **generalización de UNA imagen**.
+   Medido después: los tres escritorios grandes **sí declaran un backend con `FileChooser`**
+   (`xdg-desktop-portal-{kde,gnome,cosmic}`, los tres sellados, con
+   `org.freedesktop.impl.portal.FileChooser` en su `.portal`). El hueco es de **sway y del corpus**.
+   ⚠ Y la sonda de la app **no sirve como medida**: contesta `libdbus: None` aunque `libdbus-1.so`
+   esté presente, o sea que aplasta «no hay portal» y «no supe preguntar» en el mismo valor.
+
+   **La salida está MEDIDA de punta a punta** (`scripts/test-portal-ficheros.sh`, 2026-10-13): sobre
+   el rootfs de sway se montan `xdg-desktop-portal` + `xdg-desktop-portal-cosmic` —el backend de
+   COSMIC es Rust sobre wgpu y **no arrastra ni GTK ni Qt**, a diferencia del de GNOME (gtk4 +
+   libadwaita) y el de KDE (Qt6)— y se llama al método real:
+
+       gdbus call … org.freedesktop.portal.FileChooser.OpenFile "" "Elegi un fichero" "{}"
+       → (objectpath '/org/freedesktop/portal/desktop/request/1_5/t')
+       árbol de sway → "app_id": "com.system76.CosmicFilesDialog"   · captura: 1.098 colores
+
+   O sea: **el diálogo abre, se ve y lista ficheros**. Lo que hace falta para llevarlo a la imagen:
+   (a) que las dos recetas sean alcanzables desde `incoming-wlr` (hoy viven en `incoming-cosmic`, y
+   una cola no ve a su hermana), (b) **`json-glib`**, y (c) cuatro líneas de
+   `/usr/share/xdg-desktop-portal/sway-portals.conf`, porque el `.portal` del backend trae
+   `UseIn=COSMIC` y en una sesión `sway` no lo elegiría solo:
+
+       [preferred]
+       default=cosmic
+       org.freedesktop.impl.portal.FileChooser=cosmic
+
+   ⚠⚠ **El punto (b) es el que casi me hace publicar un veredicto falso.** Sin `json-glib` el núcleo
+   del portal NO ARRANCA, y cada versión lo disfraza distinto: la 1.19.4 de la cola gnome dice
+   `Error relocating … json_parser_get_root: symbol not found` (rc=127) y la 1.18.4 de kde/cosmic se
+   cae con **SIGSEGV pelado** después de listar los backends, sin nombrar nada. Llegué a escribir que
+   el artefacto estaba roto; con la capa de `json-glib` montada, **las dos corren**. Es la figura de
+   siempre —sellado, presente, reproducible y sin arrancar— y la lección de método es que un arnés al
+   que le falta la clausura de RUNTIME de lo que mide produce un veredicto que parece del artefacto.
+   En las tres imágenes de escritorio esto no muerde porque `json-glib` entra por ser dep declarada
+   del propio portal; en sway no entra nada de esto.
 2. **OCHO de las doce se quedan sin portapapeles**: su `arboard` no trae `wayland-data-control`,
    intenta X11 y expira (`cadcraft`, `deckcraft`, `designcraft`, `effectcraft`, `filmcraft`,
    `lightcraft`, `pdfcraft`, `soundcraft`). Las cuatro que sí lo traen: `gridcraft`, `photocraft`,
diff --git a/scripts/test-portal-ficheros.sh b/scripts/test-portal-ficheros.sh
new file mode 100644
index 00000000..acd0ec56
--- /dev/null
+++ b/scripts/test-portal-ficheros.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+# test-portal-ficheros.sh <app> — ¿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" <<EOF
+#!/bin/sh
+# OJO: este heredoc va SIN comillas (necesita expandir \$APP), asi que NADA de backticks aca dentro
+# ni en los comentarios: en un heredoc sin comillas son SUSTITUCION DE COMANDOS. Ya mordio dos veces.
+export XDG_RUNTIME_DIR=/run/user/0 HOME=/salida/hogar LANG=C XDG_DATA_DIRS=/usr/share
+export XDG_CURRENT_DESKTOP=sway
+export WLR_BACKENDS=headless WLR_HEADLESS_OUTPUTS=1 WLR_RENDERER=pixman
+export LIBGL_ALWAYS_SOFTWARE=1 LIBGL_DRIVERS_PATH=/usr/lib/dri GALLIUM_DRIVER=llvmpipe
+export RUST_BACKTRACE=1 RUST_LOG=info
+mkdir -p "\$XDG_RUNTIME_DIR" /salida/hogar; chmod 700 "\$XDG_RUNTIME_DIR"
+
+# El fichero que hace elegible al backend fuera de COSMIC. Son cuatro líneas y es TODO lo que falta
+# del lado de la configuracion.
+mkdir -p /usr/share/xdg-desktop-portal
+cat > /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: /'