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…
atuq: el compositor NO era la causa — con cosmic-comp nesteado el navegador pinta
1#!/bin/sh
2# atuq-en-cosmic.sh — ¿el navegador PINTA SU VENTANA bajo `cosmic-comp`? (SDD 26 §6.10.ter)
3#
4# scripts/cosmic/atuq-en-cosmic.sh # atuq dentro de cosmic-comp
5# scripts/cosmic/atuq-en-cosmic.sh --control # el MISMO atuq, pero directo sobre sway
6#
7# ── POR QUÉ EXISTE ────────────────────────────────────────────────────────────────────────────
8# El 2026-09-14 se arrancó por primera vez una IMAGEN DE DISCO con COSMIC y `atuq` adentro: el
9# navegador arranca, sus extensiones corren, WebRender inicializa… y la ventana no aparece nunca.
10# Bajo `sway` headless el mismo artefacto pinta perfecto. Entre los dos casos cambian DOS cosas a la
11# vez —el compositor (cosmic-comp+llvmpipe contra sway+pixman) y el arranque real contra `bwrap`—,
12# así que ninguna medición de las que había separa una de la otra.
13#
14# Este arnés cambia UNA sola: el compositor. Todo lo demás —el mismo cierre `escritorio-cosmic`
15# hidratado, el mismo binario de `atuq`, la misma jaula— queda fijo, y `--control` corre ese mismo
16# `atuq` sobre sway sin tocar nada más. Cuesta ~2 min por vuelta en vez de armar una imagen de 12 G
17# y arrancarla en QEMU sin KVM.
18#
19# ── LA CADENA ─────────────────────────────────────────────────────────────────────────────────
20# sway (headless, pixman) → cosmic-comp (winit, cliente de sway) → atuq
21# `grim` captura del lado de SWAY, o sea que fotografía la ventana de cosmic-comp con lo que haya
22# dentro. sway es andamio: no lo prueba nada, sólo le da a cosmic-comp una pantalla donde vivir.
23#
24# ── LA EVIDENCIA ES UN DIFF DE PÍXELES, Y ADEMÁS SE MIRA EL ÁRBOL ─────────────────────────────
25# Se captura ANTES de lanzar el navegador y DESPUÉS. Un log en verde es compatible con una pantalla
26# vacía —es exactamente lo que pasó en la imagen— así que el veredicto es cuántos píxeles cambiaron.
27# Y se guarda el `MOZ_LOG` del widget: si la ventana se crea pero nunca recibe su `configure`, eso
28# sólo se ve ahí.
29#
30# env: RFS_COSMIC (def /mnt/cosecha/cosmic-rootfs) cierre de escritorio-cosmic
31# RFS_SWAY (def /mnt/cosecha/escritorios/sway-rootfs) el andamio
32# MESA (def el artefacto vigente de mesa-llvmpipe) GL por software para cosmic-comp
33# ESPERA (def 60) segundos para que el navegador pinte
34# OUT (def un temporal) dónde quedan capturas y logs
35set -eu
36ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
37RFS_COSMIC="${RFS_COSMIC:-/mnt/cosecha/cosmic-rootfs}"
38RFS_SWAY="${RFS_SWAY:-/mnt/cosecha/escritorios/sway-rootfs}"
39ALPINE="$ROOT/.dev-fs/alpine"
40ESPERA="${ESPERA:-60}"
41CONTROL=0
42for a in "$@"; do
43 case "$a" in
44 --control) CONTROL=1 ;;
45 *) echo "✗ opción desconocida: $a" >&2; exit 1 ;;
46 esac
47done
48
49# mesa-llvmpipe por HASH VIGENTE, no por el más reciente del store (con dos artefactos del mismo
50# paquete conviviendo, la fecha miente). Es el mismo criterio de cosmic-nested.sh.
51if [ -z "${MESA+x}" ]; then
52 MH=$(./target/release/takana --store /mnt/cosecha/store hash recipes/mesa-llvmpipe.toml 2>/dev/null | tr -d '\n' | sed 's/b3://')
53 MESA=$(ls -d /mnt/cosecha/store/"$MH"-mesa-llvmpipe 2>/dev/null || true)
54fi
55[ -d "$RFS_COSMIC" ] || { echo "✗ falta el cierre de cosmic en $RFS_COSMIC" >&2
56 echo " scripts/hydrate-profile.py escritorio-cosmic --store /mnt/cosecha/store --into $RFS_COSMIC" >&2; exit 1; }
57[ -d "$RFS_SWAY" ] || { echo "✗ falta el rootfs de sway en $RFS_SWAY" >&2; exit 1; }
58[ -x "$RFS_COSMIC/usr/bin/atuq" ] || { echo "✗ el cierre no trae /usr/bin/atuq" >&2; exit 1; }
59[ -n "${MESA:-}" ] || echo " ⚠ sin mesa-llvmpipe sellado: cosmic-comp no va a poder iniciar EGL" >&2
60
61OUT="${OUT:-$(mktemp -d)}"; mkdir -p "$OUT"
62echo "== salida: $OUT"
63
64# Una página de UN SOLO COLOR imposible de confundir con nada del escritorio: si aparece, apareció
65# la ventana del navegador y no un repintado de cosmic.
66cat > "$OUT/pagina.html" <<'HTML'
67<!doctype html><meta charset="utf-8"><title>atuq en cosmic</title>
68<body style="margin:0;background:#ff00ff">
69<h1 style="color:#000;font:40px monospace;padding:40px">ATUQ PINTA</h1>
70<script>if (window.dump) { dump("ATUQ-PAGINA cargada\n");
71 setInterval(() => dump("ATUQ-PAGINA viva\n"), 10000); }</script>
72HTML
73mkdir -p "$OUT/perfil"
74cat > "$OUT/perfil/user.js" <<'JS'
75user_pref("browser.dom.window.dump.enabled", true);
76user_pref("browser.shell.checkDefaultBrowser", false);
77JS
78
79cat > "$OUT/dentro.sh" <<'ADENTRO'
80#!/bin/sh
81export XDG_RUNTIME_DIR=/run/user/0 HOME=/root LANG=C XDG_DATA_DIRS=/usr/share
82export XDG_CURRENT_DESKTOP=COSMIC XDG_SESSION_TYPE=wayland RUST_BACKTRACE=1
83export LIBGL_ALWAYS_SOFTWARE=1 LIBGL_DRIVERS_PATH=/usr/lib/dri
84mkdir -p "$XDG_RUNTIME_DIR" /root /dev/shm /etc; chmod 700 "$XDG_RUNTIME_DIR"
85[ -s /etc/passwd ] || echo "root:x:0:0:root:/root:/bin/sh" > /etc/passwd
86[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
87
88lanzar_atuq() { # $1 = WAYLAND_DISPLAY donde tiene que pintar
89 WAYLAND_DISPLAY="$1" \
90 MOZ_ENABLE_WAYLAND=1 GDK_BACKEND=wayland \
91 MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_UTILITY_SANDBOX=1 \
92 MOZ_DISABLE_GMP_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS_SANDBOX=1 \
93 MOZ_LOG='Widget:5,WidgetWayland:5,WaylandBackend:5,timestamp' \
94 MOZ_LOG_FILE=/salida/moz \
95 /usr/bin/atuq --no-remote --profile /salida/perfil file:///salida/pagina.html \
96 > /salida/atuq.log 2>&1 &
97 echo "@@ atuq lanzado (pid $!) contra $1"
98}
99
100(
101 i=0
102 while [ ! -S /run/user/0/wayland-1 ] && [ $i -lt 60 ]; do sleep 1; i=$((i+1)); done
103 [ -S /run/user/0/wayland-1 ] || { echo "@@ sin socket de sway"; exit 1; }
104 export WAYLAND_DISPLAY=wayland-1
105 /usr/bin/swaybg -c '#1a4b8c' >/dev/null 2>&1 &
106 sleep 2
107 export DBUS_SESSION_BUS_ADDRESS=$(/usr/bin/dbus-daemon --session --fork --print-address 2>/dev/null)
108 echo "@@ dbus=${DBUS_SESSION_BUS_ADDRESS:-VACIO}"
109
110 if [ "${CONTROL:-0}" = "1" ]; then
111 echo "@@ CONTROL: el navegador va directo sobre sway (sin cosmic-comp)"
112 DEST=wayland-1
113 else
114 echo "@@ arrancando cosmic-comp nesteado sobre sway…"
115 cosmic-comp >/salida/cosmic.log 2>&1 &
116 CC=$!
117 i=0; WD=""
118 while [ $i -lt 60 ]; do
119 WD=$(ls /run/user/0/wayland-* 2>/dev/null | grep -v lock | grep -v 'wayland-1$' | head -1)
120 [ -n "$WD" ] && break
121 kill -0 $CC 2>/dev/null || { echo "@@ cosmic-comp murió antes del socket:"; tail -25 /salida/cosmic.log; exit 1; }
122 i=$((i+1)); sleep 1
123 done
124 [ -n "$WD" ] || { echo "@@ cosmic-comp sin socket en 60 s:"; tail -25 /salida/cosmic.log; exit 1; }
125 DEST=$(basename "$WD")
126 echo "@@ cosmic-comp OK ($DEST)"
127 sleep 5
128 fi
129
130 /usr/bin/grim /salida/antes.png && echo "@@ captura ANTES"
131 lanzar_atuq "$DEST"
132 sleep "${ESPERA:-60}"
133 /usr/bin/grim /salida/despues.png && echo "@@ captura DESPUES"
134 echo "@@ procesos atuq: $(ps -o comm= 2>/dev/null | grep -c atuq)"
135 sleep 1
136 /usr/bin/swaymsg exit 2>/dev/null
137) &
138
139export WLR_BACKENDS=headless WLR_HEADLESS_OUTPUTS=1 WLR_RENDERER=pixman
140exec /usr/bin/sway -d 2>&1
141ADENTRO
142
143# El ORDEN de los overlays manda: alpine trae la libc, sway el andamio, el cierre de cosmic pisa lo
144# que comparta con él, y mesa-llvmpipe va ÚLTIMO porque el cierre trae la mesa de Intel real
145# (iris_dri) y en una máquina sin GPU eso no inicia EGL.
146TIEMPO=$((ESPERA + 180))
147timeout "$TIEMPO" bwrap \
148 --overlay-src "$ALPINE" --overlay-src "$RFS_SWAY" --overlay-src "$RFS_COSMIC" \
149 ${MESA:+--overlay-src "$MESA"} --tmp-overlay / \
150 --bind "$OUT" /salida --dev /dev --proc /proc --tmpfs /run --tmpfs /tmp \
151 --dev-bind-try /dev/dri /dev/dri \
152 --unshare-pid --unshare-uts --hostname atuq-en-cosmic \
153 --uid 0 --gid 0 --die-with-parent \
154 --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \
155 --setenv CONTROL "$CONTROL" --setenv ESPERA "$ESPERA" \
156 /bin/sh /salida/dentro.sh > "$OUT/log.txt" 2>&1 || true
157
158grep -a '@@' "$OUT/log.txt" || true
159[ -f "$OUT/antes.png" ] && [ -f "$OUT/despues.png" ] || {
160 echo "✗ no hubo capturas — mirá $OUT/log.txt" >&2; tail -20 "$OUT/log.txt" >&2; exit 1; }
161
162python3 - "$OUT/antes.png" "$OUT/despues.png" <<'PY'
163import sys
164from PIL import Image, ImageChops
165a, b = (Image.open(p).convert("RGB") for p in sys.argv[1:3])
166d = ImageChops.difference(a, b).convert("L")
167cambiados = d.width * d.height - d.histogram()[0]
168magenta = sum(n for n, c in b.getcolors(maxcolors=1 << 24) if c == (255, 0, 255))
169print(f" píxeles cambiados: {cambiados} magenta de la página: {magenta}")
170PY
171echo " capturas: $OUT/antes.png $OUT/despues.png"
172echo " logs: $OUT/atuq.log $OUT/cosmic.log $OUT/moz.*"