#!/usr/bin/env python3 """Run actions and editable internal controls on a private X11/AT-SPI desktop.""" import json import os from pathlib import Path import subprocess import sys import tempfile import time def run_inside(): root = Path(__file__).resolve().parents[1] environment = dict(os.environ, GOCACHE="/tmp/cthulhu-cache", CTHULHU_NATIVE_X11="1", CTHULHU_NATIVE_BROWSER="1") probe = subprocess.Popen(["xev", "-name", "Internal UI background", "-event", "keyboard"], stdout=subprocess.DEVNULL) try: deadline = time.monotonic() + 5 while True: found = subprocess.run(["xdotool", "search", "--onlyvisible", "--name", "^Internal UI background$"], capture_output=True, text=True) if found.returncode == 0: background = found.stdout.strip().splitlines()[0] environment["CTHULHU_UI_BACKGROUND"] = background subprocess.run(["xdotool", "windowfocus", background], check=True) break if time.monotonic() >= deadline: raise RuntimeError("keyboard probe did not open") time.sleep(0.02) subprocess.run(["go", "test", "./internal/platform/xorg", "-count=1", "-timeout=60s", "-run", "^TestNative(InternalUICapturesAndReleases|LearnModeCapturesKeysAndRestoresCommands|CustomBindingsUpdateAndRelease|CommandGrabSuspensionAndRestoration|SecureInputDropsKeysAndRestoresGrabs|BrowseKeyTransitions)$"], cwd=root, env=environment, check=True) finally: probe.terminate() probe.wait(timeout=5) with tempfile.TemporaryDirectory(prefix="cthulhu-internal-ui-") as temporary: marker = Path(temporary) / "state.json" environment.update(CTHULHU_UI_MARKER=str(marker), CTHULHU_NATIVE_INTERNAL_UI="1") fixture = subprocess.Popen([sys.executable, __file__, "--fixture"], env=environment) try: deadline = time.monotonic() + 10 window = "" while time.monotonic() < deadline: found = subprocess.run(["xdotool", "search", "--onlyvisible", "--name", "^Internal UI action probe$"], capture_output=True, text=True) if found.returncode == 0: window = found.stdout.strip().splitlines()[0] break time.sleep(0.02) if not window: raise RuntimeError("GTK fixture did not open") subprocess.run(["xdotool", "windowfocus", "--sync", window], check=True) subprocess.run(["go", "test", "./internal/app", "-count=1", "-timeout=30s", "-run", "^TestNative(InternalUIActionsAndEntry|TentacleClipboardTargetsAndTemporaryImage)$"], cwd=root, env=environment, check=True) finally: fixture.terminate() fixture.wait(timeout=5) def run_fixture(): import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk window = Gtk.Window(title="Internal UI action probe") box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) button = Gtk.Button(label="Run action") entry = Gtk.Entry() entry.get_accessible().set_name("Application text") box.pack_start(button, False, False, 0) box.pack_start(entry, False, False, 0) lines = Gtk.TextView() lines.get_accessible().set_name("Transfer text lines") lines.get_buffer().set_text("First line\nSecond line") lines.get_buffer().place_cursor(lines.get_buffer().get_start_iter()) box.pack_start(lines, False, False, 0) password = Gtk.Entry() password.set_visibility(False) password.set_text("private fixture password") password.get_accessible().set_name("Protected transfer text") box.pack_start(password, False, False, 0) window.add(box) state = {"clicks": 0, "text": ""} marker = Path(os.environ["CTHULHU_UI_MARKER"]) def save(): replacement = marker.with_suffix(".tmp") replacement.write_text(json.dumps(state)) replacement.replace(marker) def clicked(_button): state["clicks"] += 1 save() def changed(_entry): state["text"] = entry.get_text() save() button.connect("clicked", clicked) entry.connect("changed", changed) window.connect("destroy", Gtk.main_quit) window.show_all() button.grab_focus() save() Gtk.main() if __name__ == "__main__": if "--fixture" in sys.argv: run_fixture() elif "--inside" in sys.argv: run_inside() else: subprocess.run(["xvfb-run", "-a", "dbus-run-session", "--", sys.executable, __file__, "--inside"], check=True, env=dict(os.environ, GTK_MODULES="gail:atk-bridge", NO_AT_BRIDGE="0"))