atuq: el compositor NO era la causa — con cosmic-comp nesteado el navegador pinta
El §6.10.ter dejó dos variables cambiando a la vez (compositor y arranque real) y ninguna medición que las separe. `scripts/cosmic/atuq-en-cosmic.sh` saca la primera de encima en ~2 min por vuelta, sin QEMU y sin imagen: sway headless de andamio, cosmic-comp nesteado encima por winit, atuq adentro, y `grim` capturando del lado de sway. El `--control` corre EL MISMO binario de atuq —el del cierre hidratado para la imagen— directo sobre sway. Las dos capturas coinciden al píxel en la región de la página (586331 px magenta, mismo bbox), y no es que el navegador se escape a sway: antes de lanzarlo, la ventana de cosmic-comp ocupa 1276x637 en ese mismo rectángulo, y el MOZ_LOG del widget ve `mode output size 1276 x 637` con cosmic contra `1280 x 720` en el control. Las dos corridas enteras difieren en 1130 píxeles, todos en las barras de título. ⇒ cosmic-comp no es el que impide la ventana. Queda como variable el arranque de verdad: kms/DRM sobre virtio-gpu en vez de winit, el PID1 de arje, el seat. De paso queda anotado el camino de buffers cuando SÍ funciona —`WaylandBufferSHM`, memoria compartida y no dmabuf—, que es la línea de base contra la que comparar la traza de la imagen. Y `scripts/cosmic/atuq-en-imagen.py` para medir allá: arranca la imagen, maneja el serial, lanza el navegador con el mismo MOZ_LOG y pide capturas por QMP. La corrida del §6.10.ter fue a mano y no dejó un solo log que se pueda releer.
This commit is contained in:
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/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'
|
||||
<!doctype html><meta charset="utf-8"><title>atuq en cosmic</title>
|
||||
<body style="margin:0;background:#ff00ff">
|
||||
<h1 style="color:#000;font:40px monospace;padding:40px">ATUQ PINTA</h1>
|
||||
<script>if (window.dump) { dump("ATUQ-PAGINA cargada\n");
|
||||
setInterval(() => dump("ATUQ-PAGINA viva\n"), 10000); }</script>
|
||||
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.*"
|
||||
Executable
+210
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
"""atuq-en-imagen.py — arranca la IMAGEN DE DISCO con COSMIC y le pide al navegador que pinte.
|
||||
|
||||
scripts/cosmic/atuq-en-imagen.py # lanza atuq y fotografía la pantalla
|
||||
scripts/cosmic/atuq-en-imagen.py --solo-cosmic # control: NO lanza atuq (línea de base)
|
||||
|
||||
── POR QUÉ ────────────────────────────────────────────────────────────────────────────────────
|
||||
El §6.10.ter del SDD 26 dejó esto medido y sin explicar: en la imagen arrancada `atuq` corre —sus
|
||||
extensiones sondean, WebRender inicializa— y la ventana NO APARECE, mientras que bajo `sway` headless
|
||||
el mismo artefacto pinta. `scripts/cosmic/atuq-en-cosmic.sh` ya sacó una variable de encima:
|
||||
**con cosmic-comp NESTEADO (backend winit, GL por software) el navegador pinta**, o sea que no es el
|
||||
compositor por sí mismo. Lo que queda es el arranque de verdad: kms/DRM sobre virtio-gpu, el PID1 de
|
||||
arje, el seat. Eso sólo se mide acá adentro.
|
||||
|
||||
La corrida anterior fue A MANO por el serial y no dejó ni un log que se pueda releer. Este guion
|
||||
hace lo mismo pero REPETIBLE y con la evidencia guardada: maneja el serial, lanza el navegador con
|
||||
`MOZ_LOG` del widget de Wayland, pide capturas por QMP y saca los logs por el serial.
|
||||
|
||||
── CÓMO HABLA CON LA VM ───────────────────────────────────────────────────────────────────────
|
||||
`console-getty` corre `cosmic-start`, que tras su ventana de observación (3 min) hace `exec /bin/sh`
|
||||
CON EL ENTORNO DE LA SESIÓN puesto (WAYLAND_DISPLAY, DBUS_SESSION_BUS_ADDRESS). O sea que el shell
|
||||
del serial es exactamente desde donde un usuario lanzaría una app — y por eso no hace falta
|
||||
reconstruir el entorno a mano, que es como se inventan diferencias que después se confunden con la
|
||||
causa.
|
||||
"""
|
||||
import argparse, json, os, shutil, socket, subprocess, sys, time
|
||||
|
||||
RAIZ = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
IMG = os.environ.get("IMG", "/mnt/cosecha/takana-cosmic-qemu.img")
|
||||
OVMF_CODE = os.environ.get("OVMF_CODE", "/usr/share/edk2/x64/OVMF_CODE.4m.fd")
|
||||
OVMF_VARS = os.environ.get("OVMF_VARS", "/usr/share/edk2/x64/OVMF_VARS.4m.fd")
|
||||
MEM = os.environ.get("MEM", "2560")
|
||||
SMP = os.environ.get("SMP", "2")
|
||||
|
||||
|
||||
class Serial:
|
||||
"""El serial de la VM. Todo lo que entra queda en el .log crudo, pase lo que pase."""
|
||||
|
||||
def __init__(self, ruta, log):
|
||||
self.log = open(log, "ab", buffering=0)
|
||||
for _ in range(120):
|
||||
try:
|
||||
self.s = socket.socket(socket.AF_UNIX); self.s.connect(ruta); break
|
||||
except OSError:
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
raise SystemExit(f"no pude conectarme al serial {ruta}")
|
||||
self.s.settimeout(1.0)
|
||||
self.buf = b""
|
||||
|
||||
def leer(self, segundos):
|
||||
fin = time.time() + segundos
|
||||
while time.time() < fin:
|
||||
try:
|
||||
d = self.s.recv(65536)
|
||||
except socket.timeout:
|
||||
continue
|
||||
if not d:
|
||||
break
|
||||
self.buf += d; self.log.write(d)
|
||||
return self.buf
|
||||
|
||||
def esperar(self, marca, segundos, nombre=""):
|
||||
"""Lee hasta ver `marca`. Devuelve lo leído desde la llamada anterior."""
|
||||
vistos = len(self.buf)
|
||||
fin = time.time() + segundos
|
||||
while time.time() < fin:
|
||||
try:
|
||||
d = self.s.recv(65536)
|
||||
except socket.timeout:
|
||||
continue
|
||||
if d:
|
||||
self.buf += d; self.log.write(d)
|
||||
if marca.encode() in self.buf[vistos:]:
|
||||
return self.buf[vistos:].decode("utf-8", "replace")
|
||||
print(f" ⚠ nunca llegó «{marca}» {nombre} en {segundos}s", flush=True)
|
||||
return self.buf[vistos:].decode("utf-8", "replace")
|
||||
|
||||
def cmd(self, orden, segundos=30):
|
||||
"""Manda una orden y devuelve su salida (delimitada por una marca propia)."""
|
||||
marca = f"__FIN{int(time.time()*1000) % 100000}__"
|
||||
self.s.sendall(f"{orden}; echo {marca}\n".encode())
|
||||
sal = self.esperar(marca, segundos, nombre=f"de «{orden[:40]}»")
|
||||
return sal
|
||||
|
||||
|
||||
def qmp(ruta, ordenes):
|
||||
s = socket.socket(socket.AF_UNIX); s.settimeout(30); s.connect(ruta)
|
||||
f = s.makefile("rwb", buffering=0)
|
||||
f.readline() # greeting
|
||||
f.write(b'{"execute":"qmp_capabilities"}\n'); f.readline()
|
||||
salidas = []
|
||||
for o in ordenes:
|
||||
f.write(json.dumps(o).encode() + b"\n"); salidas.append(f.readline())
|
||||
s.close()
|
||||
return salidas
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--solo-cosmic", action="store_true", help="control: no lanza el navegador")
|
||||
ap.add_argument("--espera", type=int, default=180, help="segundos para que el navegador pinte")
|
||||
ap.add_argument("--out", default=os.path.join(RAIZ, "work/atuq-imagen"))
|
||||
a = ap.parse_args()
|
||||
|
||||
out = a.out; os.makedirs(out, exist_ok=True)
|
||||
vars_fd = os.path.join(out, "OVMF_VARS.fd")
|
||||
shutil.copy(OVMF_VARS, vars_fd)
|
||||
ser_sock, qmp_sock = os.path.join(out, "serial.sock"), os.path.join(out, "qmp.sock")
|
||||
for p in (ser_sock, qmp_sock):
|
||||
if os.path.exists(p):
|
||||
os.unlink(p)
|
||||
|
||||
qemu = [
|
||||
"nice", "-n", "10", "qemu-system-x86_64",
|
||||
"-machine", "q35", "-accel", "tcg", "-cpu", "max", "-m", MEM, "-smp", SMP,
|
||||
"-vga", "none", "-device", "virtio-gpu-pci", "-display", "none", "-nic", "none",
|
||||
"-drive", f"if=pflash,format=raw,unit=0,file={OVMF_CODE},readonly=on",
|
||||
"-drive", f"if=pflash,format=raw,unit=1,file={vars_fd}",
|
||||
"-drive", f"file={IMG},format=raw,if=virtio",
|
||||
"-serial", f"unix:{ser_sock},server,nowait",
|
||||
"-qmp", f"unix:{qmp_sock},server,nowait",
|
||||
"-no-reboot",
|
||||
]
|
||||
print("== arrancando la imagen (TCG, sin KVM — paciencia)")
|
||||
|
||||
def primero_en_la_fila():
|
||||
# El repo es COMPARTIDO y casi siempre hay una tanda de la granja compilando en esta misma
|
||||
# máquina (4 cores, 7,6 G). Si la RAM se acaba, el que tiene que morir es ESTA VM y no el
|
||||
# build de otro agente: `oom_score_adj` alto es la única forma de decirlo por adelantado.
|
||||
with open("/proc/self/oom_score_adj", "w") as f:
|
||||
f.write("900")
|
||||
|
||||
vm = subprocess.Popen(qemu, stdout=open(os.path.join(out, "qemu.log"), "wb"),
|
||||
stderr=subprocess.STDOUT, preexec_fn=primero_en_la_fila)
|
||||
try:
|
||||
ser = Serial(ser_sock, os.path.join(out, "serial.log"))
|
||||
print("== esperando a COSMIC…", flush=True)
|
||||
ser.esperar("compositor OK", 600, nombre="(arranque de cosmic-comp)")
|
||||
print(" compositor arriba; esperando el shell del serial (ventana de observación)", flush=True)
|
||||
ser.esperar("fin de la ventana de observación", 420, nombre="(fin de observación)")
|
||||
time.sleep(3)
|
||||
ser.s.sendall(b"\n")
|
||||
print(ser.cmd("echo ENTORNO: WD=$WAYLAND_DISPLAY XDG=$XDG_RUNTIME_DIR DBUS=$DBUS_SESSION_BUS_ADDRESS").strip())
|
||||
print(ser.cmd("ls -l $XDG_RUNTIME_DIR; ls /usr/lib/dri").strip())
|
||||
|
||||
qmp(qmp_sock, [{"execute": "screendump", "arguments": {"filename": os.path.join(out, "antes.ppm")}}])
|
||||
print("== captura ANTES", flush=True)
|
||||
|
||||
if a.solo_cosmic:
|
||||
print("== CONTROL: no se lanza el navegador")
|
||||
else:
|
||||
# Una página de un color imposible de confundir con el escritorio.
|
||||
ser.cmd("mkdir -p /tmp/perfil /var/log/cosmic")
|
||||
ser.cmd("printf '%s' '<!doctype html><body style=\"margin:0;background:#ff00ff\">"
|
||||
"<h1 style=\"font:40px monospace\">ATUQ PINTA</h1>' > /tmp/pagina.html")
|
||||
ser.cmd("printf '%s\\n' 'user_pref(\"browser.dom.window.dump.enabled\", true);' > /tmp/perfil/user.js")
|
||||
ser.cmd("printf '%s\\n' 'user_pref(\"browser.shell.checkDefaultBrowser\", false);' >> /tmp/perfil/user.js")
|
||||
lanz = ("MOZ_ENABLE_WAYLAND=1 GDK_BACKEND=wayland "
|
||||
"MOZ_LOG='Widget:5,WidgetWayland:5,WaylandBackend:5,timestamp' "
|
||||
"MOZ_LOG_FILE=/var/log/cosmic/moz "
|
||||
"/usr/bin/atuq --no-remote --profile /tmp/perfil file:///tmp/pagina.html "
|
||||
"> /var/log/cosmic/atuq.log 2>&1 &")
|
||||
print(ser.cmd(lanz).strip())
|
||||
print(f"== navegador lanzado; observando {a.espera}s", flush=True)
|
||||
t0 = time.time(); n = 0
|
||||
while time.time() - t0 < a.espera:
|
||||
time.sleep(min(60, max(5, a.espera / 3))); n += 1
|
||||
qmp(qmp_sock, [{"execute": "screendump",
|
||||
"arguments": {"filename": os.path.join(out, f"durante-{n}.ppm")}}])
|
||||
vivos = ser.cmd("for c in /proc/[0-9]*/comm; do cat $c 2>/dev/null; done | grep -c atuq")
|
||||
print(f" +{int(time.time()-t0)}s procesos atuq: {vivos.splitlines()[1].strip() if len(vivos.splitlines())>1 else '?'}", flush=True)
|
||||
|
||||
qmp(qmp_sock, [{"execute": "screendump", "arguments": {"filename": os.path.join(out, "despues.ppm")}}])
|
||||
print("== captura DESPUES", flush=True)
|
||||
|
||||
# La evidencia que no se puede leer de una captura: qué hizo el widget de Wayland.
|
||||
print("---- atuq.log (cola) ----")
|
||||
print(ser.cmd("tail -25 /var/log/cosmic/atuq.log", 60))
|
||||
print("---- MOZ_LOG: creación de la ventana ----")
|
||||
print(ser.cmd("grep -a -m 40 -E 'nsWindow::Create|CreateNative|Map|configure|Show|frame callback|Buffer' "
|
||||
"/var/log/cosmic/moz.moz_log | head -40", 120))
|
||||
print("---- MOZ_LOG: últimas líneas ----")
|
||||
print(ser.cmd("tail -25 /var/log/cosmic/moz.moz_log", 60))
|
||||
print("---- tamaño de los logs ----")
|
||||
print(ser.cmd("wc -l /var/log/cosmic/moz.moz_log /var/log/cosmic/atuq.log", 30))
|
||||
finally:
|
||||
try:
|
||||
qmp(qmp_sock, [{"execute": "quit"}])
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(2); vm.terminate()
|
||||
try:
|
||||
vm.wait(20)
|
||||
except Exception:
|
||||
vm.kill()
|
||||
|
||||
# Las capturas salen en PPM (lo único que sabe QMP); a PNG para poder mirarlas.
|
||||
try:
|
||||
from PIL import Image
|
||||
for f in sorted(os.listdir(out)):
|
||||
if f.endswith(".ppm"):
|
||||
Image.open(os.path.join(out, f)).save(os.path.join(out, f[:-4] + ".png"))
|
||||
print("== capturas convertidas a PNG en", out)
|
||||
except Exception as e:
|
||||
print(" ⚠ sin conversión a PNG:", e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user