diff --git a/docs/26-atuq-envoltorio-gecko.md b/docs/26-atuq-envoltorio-gecko.md index 4da0c379..c19d37cf 100644 --- a/docs/26-atuq-envoltorio-gecko.md +++ b/docs/26-atuq-envoltorio-gecko.md @@ -1671,6 +1671,48 @@ Lo que queda como variable —y es donde sigue la caza— es el arranque de verd 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, que es exactamente lo que hace falta para comparar. +#### 6.10.quinquies En la imagen, el navegador SÍ pinta — y la segunda pantalla no era (2026-09-14) + +`scripts/cosmic/atuq-en-imagen.py` arranca la imagen del §6.10.ter, maneja el serial, lanza el +navegador con `MOZ_LOG` del widget de Wayland y pide capturas por QMP. La corrida de aquel día fue a +mano y no dejó un log que se pueda releer; ésta deja todo en `work/atuq-imagen-*`. + +**La sospecha que se fue a probar** salía del propio serial: aquel arranque veía DOS dispositivos DRM +y éste uno solo. + + == cosmic :: kernel 6.16.12 · drm: card0 card1 renderD128 ← §6.10.ter (sin `-vga none`) + == cosmic :: kernel 6.16.12 · drm: card0 renderD128 ← con `-vga none` + +Sin `-vga none`, QEMU agrega una VGA estándar ADEMÁS del virtio-gpu, OVMF pinta su GOP ahí y el +kernel levanta `simpledrm` encima. Un compositor con dos tarjetas puede componer en la que el +`screendump` no muestra, y eso se vería exactamente como «la ventana no aparece». + +**Medido, con las dos configuraciones y el mismo lanzamiento** (perfil nuevo, `MOZ_ENABLE_WAYLAND=1`, +página de un solo color): + +| vídeo de QEMU | DRM en el guest | magenta en pantalla | +|---|---|---| +| `-vga none` | `card0` | **703 766 px** | +| `VGA=1` (como el §6.10.ter) | `card0 card1` | **703 766 px** | + +El mismo número en las dos, al píxel. La ventana aparece —panel de COSMIC, dock y el navegador con su +barra lateral—, `nsWindow::Create() Toplevel` sale en el `MOZ_LOG`, la superficie se mapea y +`NotifyOcclusionState() mIsFullyOccluded 0`. El camino de buffers es el mismo que bajo sway: +`WaylandBufferSHM`, 1280×696. + +⇒ **la segunda pantalla NO es la causa**, y el síntoma del §6.10.ter no se reproduce así. Eso deja una +sola diferencia entre aquella corrida y ésta, y es la que sigue: **cómo se lanzó el navegador**. Acá +va con perfil nuevo y las variables puestas; allá fue un `atuq` pelado, con su perfil por defecto —el +que trae la barra lateral y la IA— tecleado en el serial. Por eso el guion tiene `--como-usuario`: +lanza exactamente eso. + +⚠ **Y la trampa del método que costó una corrida entera**, escrita en el guion para que no se repita: +el terminal hace **eco** de lo que se le escribe, así que una marca de fin escrita literal aparece en +el serial ANTES de que el shell ejecute nada — el arnés la lee en el eco, da la orden por terminada y +manda la siguiente encima. Y `cmd & ; echo …` es **error de sintaxis** en ash: el navegador no se +lanzó y la corrida siguió como si todo fuera bien, con capturas de un escritorio vacío que se leían +igual que el fallo que se estaba investigando. + ### 6.11 Y ahora la pregunta que faltaba: ¿REPRODUCE? (2026-09-07) El §6.10 preguntó «¿qué NO puede hacer?» y contestó leyendo cadenas. Media pregunta. La otra mitad diff --git a/scripts/cosmic/atuq-en-imagen.py b/scripts/cosmic/atuq-en-imagen.py index 2c6e2eb1..b4932f31 100755 --- a/scripts/cosmic/atuq-en-imagen.py +++ b/scripts/cosmic/atuq-en-imagen.py @@ -25,6 +25,10 @@ causa. """ import argparse, json, os, shutil, socket, subprocess, sys, time +# Sin esto la salida sale por tandas cuando se redirige a un fichero, y una corrida de 20 minutos se +# lee como si estuviera colgada. +sys.stdout.reconfigure(line_buffering=True) + 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") @@ -32,6 +36,18 @@ 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") +# ⚠ `VGA=1` reproduce el arranque del §6.10.ter, y la diferencia NO es cosmética: sin `-vga none`, +# QEMU agrega una VGA estándar ADEMÁS del virtio-gpu, OVMF pinta su GOP ahí y el kernel levanta +# `simpledrm` encima ⇒ el guest ve DOS dispositivos DRM. En el serial se lee tal cual: +# +# == cosmic :: kernel 6.16.12 · drm: card0 card1 renderD128 (corrida del §6.10.ter) +# == cosmic :: kernel 6.16.12 · drm: card0 renderD128 (con -vga none) +# +# Un compositor con dos tarjetas puede componer en la que el `screendump` NO muestra. Por eso el +# default acá es UNA sola (-vga none) y la reproducción del caso original es una opción explícita: +# son dos mediciones distintas y hay que poder pedir cada una por su nombre. +VGA_ARGS = [] if os.environ.get("VGA") == "1" else ["-vga", "none"] + class Serial: """El serial de la VM. Todo lo que entra queda en el .log crudo, pase lo que pase.""" @@ -76,12 +92,27 @@ class Serial: 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 cmd(self, orden, segundos=30, fondo=False): + """Manda una orden y devuelve su salida (delimitada por una marca propia). + + ⚠ DOS TRAMPAS MEDIDAS, las dos hacen que la orden NO CORRA y el guion siga como si nada: + + · **el terminal hace ECO de lo que se le escribe**, así que una marca escrita literal en la + línea aparece en el serial ANTES de que el shell ejecute nada: el `esperar` la ve en el eco + y vuelve al instante, y las órdenes se pisan unas a otras. Por eso la marca se ARMA EN EL + GUEST partida en dos (`'FIN''-123'`): el eco muestra las comillas y sólo la salida trae la + marca entera. + · **`cmd & ; echo …` es un error de sintaxis** en ash: pegarle un `;` a un `&` no lanza nada + y el shell contesta «syntax error». Lo de fondo va en su propia línea (`fondo=True`). + """ + n = int(time.time() * 1000) % 100000 + marca = f"FIN-{n}" + if fondo: + self.s.sendall(f"{orden}\n".encode()); time.sleep(0.5) + self.s.sendall(f"echo 'FIN''-{n}'\n".encode()) + else: + self.s.sendall(f"{orden}; echo 'FIN''-{n}'\n".encode()) + return self.esperar(marca, segundos, nombre=f"de «{orden[:40]}»") def qmp(ruta, ordenes): @@ -99,6 +130,8 @@ def qmp(ruta, ordenes): def main(): ap = argparse.ArgumentParser() ap.add_argument("--solo-cosmic", action="store_true", help="control: no lanza el navegador") + ap.add_argument("--como-usuario", action="store_true", + help="lanza `atuq` a secas —perfil por defecto, sin MOZ_ENABLE_WAYLAND— como se hizo a mano en el §6.10.ter") 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() @@ -114,7 +147,7 @@ def main(): 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", + *VGA_ARGS, "-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", @@ -156,12 +189,23 @@ def main(): "