Files
DeeJanuz--frametop/steam/ft-steam
T
DeeJanuzandClaude Opus 5.5 2487351556 ft-steam: open Steam's menu through Steam's own UI
steam/ft-steam menu opens the SteamVR dashboard on Steam's menu, or closes
the dashboard if it's up, without pointer mode: it asks Steam's UI over its
debugging port to show its dashboard overlay (ShowVROverlay, what Steam
calls itself) and focus the Steam frame's left menu (MenuStore.OpenMainMenu).
ft-steam check says whether those calls still exist, and update-check.py
runs it, since a Steam client update can rename them.

The CDP client moves from display-settings/steam_settings.py to
steam/steamui.py, so both use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 17:06:30 -06:00

67 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""ft-steam: Steam's menu and checks, through Steam's own UI (steamui.py).
ft-steam menu open the SteamVR dashboard on Steam's menu, or close the dashboard if it's
open (the Steam button's job, as a shortcut: the input relay's steam_menu)
ft-steam check do Steam's UI calls that the above needs still exist (update-check.py)
No pointer mode needed: it asks Steam to show its dashboard overlay (window.ShowVROverlay, what
Steam itself calls) and to focus the Steam frame's left menu (MenuStore.OpenMainMenu, which in
VR invokes the frame's focus_left_frame_menu_action). SteamClient.OpenVR.VROverlay says
whether the dashboard is up and hides it. These are Steam's UI internals, not an API, so a
Steam client update can rename them: `ft-steam check` says so.
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from steamui import SteamUnreachable, evaluate # noqa: E402
WINDOW = "SteamUIStore.WindowStore.VRGamepadUIMainWindowInstance"
MENU = f"""(async () => {{
const vr = SteamClient.OpenVR.VROverlay;
if (await vr.IsDashboardVisible()) {{ vr.HideDashboard(); return "closed"; }}
const w = {WINDOW};
if (!w) throw new Error("Steam's VR dashboard window isn't open");
ShowVROverlay(w.GetVROverlayKey());
// The frame's menu takes focus only once the dashboard is up.
for (let i = 0; i < 40 && !(await vr.IsDashboardVisible()); i++) await new Promise(r => setTimeout(r, 25));
w.MenuStore.OpenMainMenu();
return "opened";
}})()"""
CHECK = f"""(() => {{
const missing = [];
const vr = SteamClient.OpenVR && SteamClient.OpenVR.VROverlay;
for (const f of ["IsDashboardVisible", "HideDashboard"]) if (!vr || typeof vr[f] !== "function") missing.push("SteamClient.OpenVR.VROverlay." + f);
if (typeof window.ShowVROverlay !== "function") missing.push("ShowVROverlay");
const w = {WINDOW};
if (!w) missing.push("{WINDOW}");
else {{
if (typeof w.GetVROverlayKey !== "function") missing.push("GetVROverlayKey");
if (!w.MenuStore || typeof w.MenuStore.OpenMainMenu !== "function") missing.push("MenuStore.OpenMainMenu");
}}
return missing;
}})()"""
def main():
cmd = sys.argv[1] if len(sys.argv) == 2 else ""
if cmd not in ("menu", "check"):
sys.exit(__doc__)
try:
result = evaluate(MENU if cmd == "menu" else CHECK)
except SteamUnreachable as e:
sys.exit(f"ft-steam: {e}")
if cmd == "check":
if result:
sys.exit("Steam's UI lacks " + ", ".join(result))
print("ok")
else:
print(result)
if __name__ == "__main__":
main()