#!/bin/sh # atuq-en-cosmic.sh — ¿el navegador PINTA SU VENTANA bajo `cosmic-comp`? (SDD 26 §6.10.ter) # # scripts/cosmic/atuq-en-cosmic.sh # atuq dentro de cosmic-comp # scripts/cosmic/atuq-en-cosmic.sh --control # el MISMO atuq, pero directo sobre sway # # ── POR QUÉ EXISTE ──────────────────────────────────────────────────────────────────────────── # El 2026-09-14 se arrancó por primera vez una IMAGEN DE DISCO con COSMIC y `atuq` adentro: el # navegador arranca, sus extensiones corren, WebRender inicializa… y la ventana no aparece nunca. # Bajo `sway` headless el mismo artefacto pinta perfecto. Entre los dos casos cambian DOS cosas a la # vez —el compositor (cosmic-comp+llvmpipe contra sway+pixman) y el arranque real contra `bwrap`—, # así que ninguna medición de las que había separa una de la otra. # # Este arnés cambia UNA sola: el compositor. Todo lo demás —el mismo cierre `escritorio-cosmic` # hidratado, el mismo binario de `atuq`, la misma jaula— queda fijo, y `--control` corre ese mismo # `atuq` sobre sway sin tocar nada más. Cuesta ~2 min por vuelta en vez de armar una imagen de 12 G # y arrancarla en QEMU sin KVM. # # ── LA CADENA ───────────────────────────────────────────────────────────────────────────────── # sway (headless, pixman) → cosmic-comp (winit, cliente de sway) → atuq # `grim` captura del lado de SWAY, o sea que fotografía la ventana de cosmic-comp con lo que haya # dentro. sway es andamio: no lo prueba nada, sólo le da a cosmic-comp una pantalla donde vivir. # # ── LA EVIDENCIA ES UN DIFF DE PÍXELES, Y ADEMÁS SE MIRA EL ÁRBOL ───────────────────────────── # Se captura ANTES de lanzar el navegador y DESPUÉS. Un log en verde es compatible con una pantalla # vacía —es exactamente lo que pasó en la imagen— así que el veredicto es cuántos píxeles cambiaron. # Y se guarda el `MOZ_LOG` del widget: si la ventana se crea pero nunca recibe su `configure`, eso # sólo se ve ahí. # # env: RFS_COSMIC (def /mnt/cosecha/cosmic-rootfs) cierre de escritorio-cosmic # RFS_SWAY (def /mnt/cosecha/escritorios/sway-rootfs) el andamio # MESA (def el artefacto vigente de mesa-llvmpipe) GL por software para cosmic-comp # ESPERA (def 60) segundos para que el navegador pinte # OUT (def un temporal) dónde quedan capturas y logs set -eu ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT" RFS_COSMIC="${RFS_COSMIC:-/mnt/cosecha/cosmic-rootfs}" RFS_SWAY="${RFS_SWAY:-/mnt/cosecha/escritorios/sway-rootfs}" ALPINE="$ROOT/.dev-fs/alpine" ESPERA="${ESPERA:-60}" CONTROL=0 for a in "$@"; do case "$a" in --control) CONTROL=1 ;; *) echo "✗ opción desconocida: $a" >&2; exit 1 ;; esac done # mesa-llvmpipe por HASH VIGENTE, no por el más reciente del store (con dos artefactos del mismo # paquete conviviendo, la fecha miente). Es el mismo criterio de cosmic-nested.sh. if [ -z "${MESA+x}" ]; then MH=$(./target/release/takana --store /mnt/cosecha/store hash recipes/mesa-llvmpipe.toml 2>/dev/null | tr -d '\n' | sed 's/b3://') MESA=$(ls -d /mnt/cosecha/store/"$MH"-mesa-llvmpipe 2>/dev/null || true) fi [ -d "$RFS_COSMIC" ] || { echo "✗ falta el cierre de cosmic en $RFS_COSMIC" >&2 echo " scripts/hydrate-profile.py escritorio-cosmic --store /mnt/cosecha/store --into $RFS_COSMIC" >&2; exit 1; } [ -d "$RFS_SWAY" ] || { echo "✗ falta el rootfs de sway en $RFS_SWAY" >&2; exit 1; } [ -x "$RFS_COSMIC/usr/bin/atuq" ] || { echo "✗ el cierre no trae /usr/bin/atuq" >&2; exit 1; } [ -n "${MESA:-}" ] || echo " ⚠ sin mesa-llvmpipe sellado: cosmic-comp no va a poder iniciar EGL" >&2 OUT="${OUT:-$(mktemp -d)}"; mkdir -p "$OUT" echo "== salida: $OUT" # Una página de UN SOLO COLOR imposible de confundir con nada del escritorio: si aparece, apareció # la ventana del navegador y no un repintado de cosmic. cat > "$OUT/pagina.html" <<'HTML' atuq en cosmic

ATUQ PINTA

HTML mkdir -p "$OUT/perfil" cat > "$OUT/perfil/user.js" <<'JS' user_pref("browser.dom.window.dump.enabled", true); user_pref("browser.shell.checkDefaultBrowser", false); JS cat > "$OUT/dentro.sh" <<'ADENTRO' #!/bin/sh export XDG_RUNTIME_DIR=/run/user/0 HOME=/root LANG=C XDG_DATA_DIRS=/usr/share export XDG_CURRENT_DESKTOP=COSMIC XDG_SESSION_TYPE=wayland RUST_BACKTRACE=1 export LIBGL_ALWAYS_SOFTWARE=1 LIBGL_DRIVERS_PATH=/usr/lib/dri mkdir -p "$XDG_RUNTIME_DIR" /root /dev/shm /etc; chmod 700 "$XDG_RUNTIME_DIR" [ -s /etc/passwd ] || echo "root:x:0:0:root:/root:/bin/sh" > /etc/passwd [ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id lanzar_atuq() { # $1 = WAYLAND_DISPLAY donde tiene que pintar WAYLAND_DISPLAY="$1" \ MOZ_ENABLE_WAYLAND=1 GDK_BACKEND=wayland \ MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1 \ MOZ_DISABLE_GMP_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS_SANDBOX=1 \ MOZ_LOG='Widget:5,WidgetWayland:5,WaylandBackend:5,timestamp' \ MOZ_LOG_FILE=/salida/moz \ /usr/bin/atuq --no-remote --profile /salida/perfil file:///salida/pagina.html \ > /salida/atuq.log 2>&1 & echo "@@ atuq lanzado (pid $!) contra $1" } ( i=0 while [ ! -S /run/user/0/wayland-1 ] && [ $i -lt 60 ]; do sleep 1; i=$((i+1)); done [ -S /run/user/0/wayland-1 ] || { echo "@@ sin socket de sway"; exit 1; } export WAYLAND_DISPLAY=wayland-1 /usr/bin/swaybg -c '#1a4b8c' >/dev/null 2>&1 & sleep 2 export DBUS_SESSION_BUS_ADDRESS=$(/usr/bin/dbus-daemon --session --fork --print-address 2>/dev/null) echo "@@ dbus=${DBUS_SESSION_BUS_ADDRESS:-VACIO}" if [ "${CONTROL:-0}" = "1" ]; then echo "@@ CONTROL: el navegador va directo sobre sway (sin cosmic-comp)" DEST=wayland-1 else echo "@@ arrancando cosmic-comp nesteado sobre sway…" cosmic-comp >/salida/cosmic.log 2>&1 & CC=$! i=0; WD="" while [ $i -lt 60 ]; do WD=$(ls /run/user/0/wayland-* 2>/dev/null | grep -v lock | grep -v 'wayland-1$' | head -1) [ -n "$WD" ] && break kill -0 $CC 2>/dev/null || { echo "@@ cosmic-comp murió antes del socket:"; tail -25 /salida/cosmic.log; exit 1; } i=$((i+1)); sleep 1 done [ -n "$WD" ] || { echo "@@ cosmic-comp sin socket en 60 s:"; tail -25 /salida/cosmic.log; exit 1; } DEST=$(basename "$WD") echo "@@ cosmic-comp OK ($DEST)" sleep 5 fi /usr/bin/grim /salida/antes.png && echo "@@ captura ANTES" lanzar_atuq "$DEST" sleep "${ESPERA:-60}" /usr/bin/grim /salida/despues.png && echo "@@ captura DESPUES" echo "@@ procesos atuq: $(ps -o comm= 2>/dev/null | grep -c atuq)" sleep 1 /usr/bin/swaymsg exit 2>/dev/null ) & export WLR_BACKENDS=headless WLR_HEADLESS_OUTPUTS=1 WLR_RENDERER=pixman exec /usr/bin/sway -d 2>&1 ADENTRO # El ORDEN de los overlays manda: alpine trae la libc, sway el andamio, el cierre de cosmic pisa lo # que comparta con él, y mesa-llvmpipe va ÚLTIMO porque el cierre trae la mesa de Intel real # (iris_dri) y en una máquina sin GPU eso no inicia EGL. TIEMPO=$((ESPERA + 180)) timeout "$TIEMPO" bwrap \ --overlay-src "$ALPINE" --overlay-src "$RFS_SWAY" --overlay-src "$RFS_COSMIC" \ ${MESA:+--overlay-src "$MESA"} --tmp-overlay / \ --bind "$OUT" /salida --dev /dev --proc /proc --tmpfs /run --tmpfs /tmp \ --dev-bind-try /dev/dri /dev/dri \ --unshare-pid --unshare-uts --hostname atuq-en-cosmic \ --uid 0 --gid 0 --die-with-parent \ --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \ --setenv CONTROL "$CONTROL" --setenv ESPERA "$ESPERA" \ /bin/sh /salida/dentro.sh > "$OUT/log.txt" 2>&1 || true grep -a '@@' "$OUT/log.txt" || true [ -f "$OUT/antes.png" ] && [ -f "$OUT/despues.png" ] || { echo "✗ no hubo capturas — mirá $OUT/log.txt" >&2; tail -20 "$OUT/log.txt" >&2; exit 1; } python3 - "$OUT/antes.png" "$OUT/despues.png" <<'PY' import sys from PIL import Image, ImageChops a, b = (Image.open(p).convert("RGB") for p in sys.argv[1:3]) d = ImageChops.difference(a, b).convert("L") cambiados = d.width * d.height - d.histogram()[0] magenta = sum(n for n, c in b.getcolors(maxcolors=1 << 24) if c == (255, 0, 255)) print(f" píxeles cambiados: {cambiados} magenta de la página: {magenta}") PY echo " capturas: $OUT/antes.png $OUT/despues.png" echo " logs: $OUT/atuq.log $OUT/cosmic.log $OUT/moz.*"