mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 03:00:18 +02:00
Dropping a game's .zip, folder or .exe on Send to Frame now adds it to the headset's Steam library through Valve's SteamOS Devkit title path, with the runtime picked from the program's header: Windows PE -> Proton Experimental (steam_play=1), aarch64 ELF -> SteamLinuxRuntime_4-arm64, x86-64 ELF -> SteamLinuxRuntime_4 (through FEX). Other architectures are refused. - frame/devkit-utils: Valve's devkit-utils vendored unmodified (MIT, steamos-devkit v0.20260925.1), synced to ~/devkit-utils by stamp, bundled in the app and compiled in CI. - ui/frame_titles.py: inspect (safe unzip, ELF/PE classification, launch target ranking), install(path, name=None, exe=None, runtime=None, progress=None), list, launch, remove, plus a CLI. - ui/server.py: /api/titles (inspect/install/discard/launch/remove), /api/titles/job progress, and an upload mode 'title'. - ui/index.html: confirm dialog (name, launch target, runtime), install progress, and a Sideloaded titles list with Launch and Remove. The app's preload passes a dropped folder's path. - tests and docs/sideloading.md. Device-side behaviour is inferred from Valve's source; the headset was offline, so none of it has been checked on a Frame yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import os
|
|
import shutil
|
|
import logging
|
|
import argparse
|
|
import subprocess
|
|
|
|
import devkit_utils.resolve
|
|
|
|
logging.basicConfig(format='%(message)s', level=logging.DEBUG)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DEVKIT_TOOL_FOLDER = os.path.expanduser('~/devkit-game')
|
|
|
|
def session_select_command():
|
|
if shutil.which('holo-session-select'):
|
|
return 'holo-session-select'
|
|
return 'steamos-session-select'
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--verbose', required=False, action='store_true')
|
|
parser.add_argument('--delete-title', required=False, action='store', help='Delete a devkit title by name')
|
|
parser.add_argument('--delete-all-titles', required=False, action='store_true', default=False, help='Delete all devkit titles uploaded')
|
|
parser.add_argument('--reset-steam-client', required=False, action='store_true', default=False, help='Reset Steam client and delete all local Steam content')
|
|
conf = parser.parse_args()
|
|
|
|
if conf.verbose:
|
|
logger.setLevel(logging.DEBUG)
|
|
else:
|
|
logger.setLevel(logging.INFO)
|
|
|
|
if conf.delete_all_titles:
|
|
subprocess.check_call('rm -rf ~/devkit-game/*', shell=True)
|
|
elif conf.delete_title:
|
|
gamepath = os.path.expanduser( os.path.join( '~/devkit-game', conf.delete_title ) )
|
|
if not os.path.isdir(gamepath):
|
|
print(f'Not found: {gamepath}')
|
|
else:
|
|
subprocess.check_call(f'rm -r {gamepath}', shell=True)
|
|
|
|
# synchronize the Steam client's view of the devkit games with the on disk state
|
|
try:
|
|
devkit_utils.resolve.resolve_shortcuts()
|
|
except Exception as e:
|
|
logger.warning(f'Steam client sync of devkit games failed: {e}')
|
|
|
|
if conf.reset_steam_client:
|
|
# first make sure any sideloaded trampoline has been deleted
|
|
devkit_steam_trampoline_path = os.path.join(DEVKIT_TOOL_FOLDER, 'devkit-steam')
|
|
if os.path.exists(devkit_steam_trampoline_path):
|
|
os.unlink(devkit_steam_trampoline_path)
|
|
|
|
# wipe the local Steam install
|
|
subprocess.check_call(f'rm -rf ~/.local/share/Steam', shell=True)
|
|
|
|
# restart the session, which will initiate a reinstall of Steam from the OS client
|
|
subprocess.check_call([session_select_command(), 'gamescope'])
|