#!/usr/bin/env python3 """Does (B) leave the main menu -- and does the menu self-return to the title? HANDOFF downgraded "(B) on the main menu returns to the title" to authored, because the corpus also carries "an ~8-10 s idle returns to the title" and one unrecorded observation cannot separate the two causes. This separates them by ordering: hold the menu UNTOUCHED for an idle window several times longer than the claimed 8-10 s and timestamp what happens, THEN press (B) and timestamp again. If the idle window passes with the menu still up, the idle cause is gone and the (B) observation is unambiguous. Screen identity comes from screen_match.py, whose control includes the movie frames that broke the statistics-based oracle. Usage: menu_b_probe.py IDLE_SECONDS AFTER_SECONDS """ import os, subprocess, sys, time import numpy as np from PIL import Image SD = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, SD) from screen_match import classify_array W, H = 1280, 720 IDLE = float(sys.argv[1]) if len(sys.argv) > 1 else 60.0 AFTER = float(sys.argv[2]) if len(sys.argv) > 2 else 30.0 OUT = "/sylph-home/re/ringcap" def stream(): return subprocess.Popen( ["ffmpeg", "-loglevel", "error", "-f", "x11grab", "-draw_mouse", "0", "-video_size", f"{W}x{H}", "-i", ":98", "-r", "4", "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], stdout=subprocess.PIPE, bufsize=W * H * 3 * 2) def main(): p = stream(); n = W * H * 3 t0 = time.time(); seg = t0; last = None; prev = None phase = "IDLE"; pressed_at = None log = [] while True: el = time.time() - t0 if phase == "IDLE" and el >= IDLE: subprocess.run(["python3", f"{SD}/pad.py", "tap", "B", "0.3"], check=False) pressed_at = time.time() - t0 print(f"t={pressed_at:6.2f}s >>> (B) PRESSED", flush=True) phase = "AFTER" if phase == "AFTER" and el >= IDLE + AFTER: break if time.time() - seg > 25: p.kill(); p = stream(); seg = time.time() b = p.stdout.read(n) if len(b) < n: p.kill(); p = stream(); seg = time.time(); continue a = np.frombuffer(b, np.uint8).reshape(H, W, 3) last = a c, sc = classify_array(a) log.append((el, c, sc["title"], sc["menu"])) if c != prev: print(f"t={el:6.2f}s screen={c:<6} title={sc['title']:+.3f} " f"menu={sc['menu']:+.3f}", flush=True) Image.fromarray(a).save(f"{OUT}/b-{el:06.2f}-{c}.png") prev = c p.kill() with open(f"{OUT}/menu-b-trace.tsv", "w") as f: f.write("t_s\tscreen\tcorr_title\tcorr_menu\n") for r in log: f.write(f"{r[0]:.3f}\t{r[1]}\t{r[2]:.4f}\t{r[3]:.4f}\n") idle = [r for r in log if r[0] < IDLE] aft = [r for r in log if pressed_at and r[0] > pressed_at + 1.0] print(f"\nIDLE phase : {len(idle)} samples over {IDLE:.0f}s, " f"screens seen = {sorted(set(r[1] for r in idle))}") print(f"AFTER (B) : {len(aft)} samples, " f"screens seen = {sorted(set(r[1] for r in aft))}") first_title = next((r[0] for r in aft if r[1] == "title"), None) if first_title: print(f" first 'title' at t={first_title:.2f}s = " f"{first_title - pressed_at:.2f}s after the (B) press") print(f"trace: {OUT}/menu-b-trace.tsv") return 0 if __name__ == "__main__": sys.exit(main())