mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 03:00:18 +02:00
Record headset results for sideloading and devkit pairing
Tested on a Frame (BUILD_ID 20260922.6101926): - Devkit pairing: the service runs and answers properties.json, but /register refuses with "please put the Steam client in pairing mode" unless Steam is on Settings > Developer > Pair new host. Both setup paths now say so and keep asking for 2 minutes before falling back to the password. - Sideloading: registering, launching, quoted start paths and Remove work; a Windows exe runs under Proton 11 through FEX. An aarch64 build starts but outside the runtime container, and an x86-64 Linux build doesn't start because the x86-64 Steam Linux Runtime 4.0 isn't installed. The inspect note for x86-64 Linux builds now says so. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
84ac687399
commit
1580dae42e
8 files changed
+106
-36
No files matched your search
@@ -149,10 +149,11 @@ computer.
|
|||||||
password once in a terminal window. If it can't find the Frame, type the
|
password once in a terminal window. If it can't find the Frame, type the
|
||||||
IP address from the Frame's Quick Settings.
|
IP address from the Frame's Quick Settings.
|
||||||
|
|
||||||
Before asking for the password it tries Valve's SteamOS devkit pairing: if
|
Before asking for the password it tries Valve's SteamOS devkit pairing: in
|
||||||
the headset shows a pairing request, approve it and no password is needed.
|
the headset, open Steam Settings → Developer → **Pair new host** and approve
|
||||||
(**Inferred from Valve's source** ([steamos-devkit-service](https://gitlab.steamos.cloud/devkit/steamos-devkit-service)),
|
the request, and no password is needed. (The service and the pairing-mode
|
||||||
not yet verified on a Frame; see [SSH](docs/ssh.md#password-free-pairing-steamos-devkit-service).)
|
step are verified on a Frame; the approval itself isn't yet. See
|
||||||
|
[SSH](docs/ssh.md#password-free-pairing-steamos-devkit-service).)
|
||||||
3. That's it. The app now reaches the headset whenever it's awake and on the
|
3. That's it. The app now reaches the headset whenever it's awake and on the
|
||||||
same network. For anywhere else, see [Tailscale](docs/tailscale.md).
|
same network. For anywhere else, see [Tailscale](docs/tailscale.md).
|
||||||
|
|
||||||
|
|||||||
+27
-9
@@ -48,8 +48,8 @@ The program's header decides, not its file name:
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Windows `.exe`, x86-64 (PE machine `0x8664`) | `proton-experimental` | 1 | Inferred: ARM64 Proton runs x86-64 code through FEX |
|
| Windows `.exe`, x86-64 (PE machine `0x8664`) | `proton-experimental` | 1 | Inferred: ARM64 Proton runs x86-64 code through FEX |
|
||||||
| Windows `.exe`, 32-bit x86 (`0x14c`) or ARM64 (`0xaa64`) | `proton-experimental` | 1 | Inferred |
|
| Windows `.exe`, 32-bit x86 (`0x14c`) or ARM64 (`0xaa64`) | `proton-experimental` | 1 | Inferred |
|
||||||
| Linux ELF, aarch64 (`e_machine` `0xB7`) | `SteamLinuxRuntime_4-arm64` | 0 | Inferred: native |
|
| Linux ELF, aarch64 (`e_machine` `0xB7`) | `SteamLinuxRuntime_4-arm64` | 0 | Verified: starts, but natively (see below) |
|
||||||
| Linux ELF, x86-64 (`0x3E`) | `SteamLinuxRuntime_4` | 0 | Inferred: runs through FEX; the least certain row |
|
| Linux ELF, x86-64 (`0x3E`) | `SteamLinuxRuntime_4` | 0 | Verified not to start: the runtime isn't installed (see below) |
|
||||||
| Shell script | the runtime of the Linux binary beside it, else `SteamLinuxRuntime_4-arm64` | 0 | Guess |
|
| Shell script | the runtime of the Linux binary beside it, else `SteamLinuxRuntime_4-arm64` | 0 | Guess |
|
||||||
| Anything else (32-bit Linux, other CPUs, DLLs, data) | refused with a message | | |
|
| Anything else (32-bit Linux, other CPUs, DLLs, data) | refused with a message | | |
|
||||||
|
|
||||||
@@ -142,12 +142,30 @@ splits that string is **not checked**.
|
|||||||
server, which only accepts requests from its own page (see
|
server, which only accepts requests from its own page (see
|
||||||
[frame-control.md](frame-control.md#how-it-works)).
|
[frame-control.md](frame-control.md#how-it-works)).
|
||||||
|
|
||||||
## To check on a headset
|
## Checked on a headset
|
||||||
|
|
||||||
- [ ] `create-shortcut` registers a title and it shows in the library under its id.
|
Tested 2026-09-26 on a Frame (BUILD_ID 20260922.6101926) with small static test
|
||||||
- [ ] An aarch64 build launches in `SteamLinuxRuntime_4-arm64`.
|
programs and PuTTY's official 64-bit `putty.exe`, through both the command line
|
||||||
- [ ] An x86-64 Windows `.exe` launches under Proton Experimental through FEX.
|
and the app (inspect, install job, ▶, Remove, and install links):
|
||||||
- [ ] An x86-64 Linux build launches in `SteamLinuxRuntime_4` through FEX.
|
|
||||||
- [ ] `steam-devkit-rpc run-game` starts it, and `steamos-delete` removes the shortcut.
|
- [x] `create-shortcut` registers a title; it shows in the Steam library and in
|
||||||
- [ ] How Steam splits a start command with a quoted path.
|
**Sideloaded titles**, and Steam maps it to the chosen compat tool.
|
||||||
|
- [x] `steam-devkit-rpc run-game` starts it (Steam logs `devkit run-game: started
|
||||||
|
devkit game "<id>"`), and Remove (`steamos-delete`) deletes the files, the
|
||||||
|
shortcut and the Proton prefix.
|
||||||
|
- [x] A quoted path in the start command is fine: Steam runs
|
||||||
|
`proton waitforexitandrun "/home/steamos/devkit-game/<id>/<exe>"`.
|
||||||
|
- [x] An x86-64 Windows `.exe` runs under **Proton 11 (stable)** through FEX
|
||||||
|
(ARM64EC) inside the Steam Linux Runtime 4.0 ARM64 container; PuTTY stayed up.
|
||||||
|
Proton Experimental wasn't installed at the time (it was downloading), so it's
|
||||||
|
untested. A Go-built x86-64 test program crashed in `libarm64ecfex.dll`
|
||||||
|
(a FEX limitation with that program, not the sideloading).
|
||||||
|
- [ ] **An aarch64 build runs natively, not in `SteamLinuxRuntime_4-arm64`**:
|
||||||
|
Steam records the mapping (`CompatToolMapping`, `compat_log.txt`) but launches
|
||||||
|
the devkit title without the runtime's `_v2-entry-point` prefix. Fine for a
|
||||||
|
self-contained build; a build that needs the runtime's libraries may not start.
|
||||||
|
- [ ] **An x86-64 Linux build doesn't start**: Steam logs `Tool 4183110 "Steam
|
||||||
|
Linux Runtime 4.0" is found for appID …, but is not installed`, and the Frame
|
||||||
|
doesn't install that x86-64 runtime for a devkit title (a `steam://install/4183110`
|
||||||
|
request did nothing).
|
||||||
- [ ] Whether these titles open as flat panels or need anything VR-specific.
|
- [ ] Whether these titles open as flat panels or need anything VR-specific.
|
||||||
+13
-6
@@ -65,15 +65,23 @@ The script only asks for the password if the pairing below doesn't work.
|
|||||||
|
|
||||||
## Password-free pairing (SteamOS devkit service)
|
## Password-free pairing (SteamOS devkit service)
|
||||||
|
|
||||||
**Inferred from Valve's source ([steamos-devkit-service](https://gitlab.steamos.cloud/devkit/steamos-devkit-service),
|
From Valve's source ([steamos-devkit-service](https://gitlab.steamos.cloud/devkit/steamos-devkit-service),
|
||||||
[steamos-devkit](https://gitlab.steamos.cloud/devkit/steamos-devkit) client); not yet
|
[steamos-devkit](https://gitlab.steamos.cloud/devkit/steamos-devkit) client). **Verified on a
|
||||||
verified on a Frame.** SteamOS's devkit service is what Valve's Devkit Client
|
Frame 2026-09-26** (BUILD_ID 20260922.6101926): the service runs with Developer Mode
|
||||||
uses to pair. `scripts/connect.sh` and `ui/frame_connect.py` try it first:
|
on, `properties.json` answers with `"login": "steamos"`, the headset advertises
|
||||||
|
`_steamos-devkit._tcp` as `frame`, and `/register` needs pairing mode (below). The
|
||||||
|
approve prompt and key install are not verified yet. SteamOS's devkit service is
|
||||||
|
what Valve's Devkit Client uses to pair. `scripts/connect.sh` and
|
||||||
|
`ui/frame_connect.py` try it first:
|
||||||
|
|
||||||
- The headset serves HTTP on port **32000** and advertises mDNS
|
- The headset serves HTTP on port **32000** and advertises mDNS
|
||||||
`_steamos-devkit._tcp`. `GET /properties.json` gives the `login` user; the
|
`_steamos-devkit._tcp`. `GET /properties.json` gives the `login` user; the
|
||||||
script uses it as `User` (unless you set `FRAME_USER`, or it says `root`),
|
script uses it as `User` (unless you set `FRAME_USER`, or it says `root`),
|
||||||
for the password fallback too, and keeps it on re-runs.
|
for the password fallback too, and keeps it on re-runs.
|
||||||
|
- **Open Steam Settings → Developer → Pair new host in the headset first.**
|
||||||
|
Otherwise `/register` answers at once with `403` `"please put the Steam client
|
||||||
|
in pairing mode: Settings -> Developer -> Pair new host"` (verified). The
|
||||||
|
scripts say so and keep asking for 2 minutes while you open it.
|
||||||
- `POST /register` with `ssh-rsa <key> <comment> 900b919520e4cf601998a71eec318fec`
|
- `POST /register` with `ssh-rsa <key> <comment> 900b919520e4cf601998a71eec318fec`
|
||||||
(a fixed token from Valve's client) shows an approve prompt inside the
|
(a fixed token from Valve's client) shows an approve prompt inside the
|
||||||
headset naming the comment (`frame-control@<your computer>`). It waits 30 s,
|
headset naming the comment (`frame-control@<your computer>`). It waits 30 s,
|
||||||
@@ -89,8 +97,7 @@ uses to pair. `scripts/connect.sh` and `ui/frame_connect.py` try it first:
|
|||||||
to copying the ed25519 key with the Developer Mode password, as before.
|
to copying the ed25519 key with the Developer Mode password, as before.
|
||||||
|
|
||||||
Anyone on your network can send the request, so only approve a prompt you
|
Anyone on your network can send the request, so only approve a prompt you
|
||||||
started. Whether the Frame runs this service with Developer Mode on is the
|
started. `curl http://<frame-ip>:32000/properties.json` shows whether the service is up.
|
||||||
unverified part: `curl http://frame.local:32000/properties.json` answers the question.
|
|
||||||
|
|
||||||
`~/.ssh/authorized_keys` lives under `/home`, which SteamOS keeps across OS
|
`~/.ssh/authorized_keys` lives under `/home`, which SteamOS keeps across OS
|
||||||
updates (inferred from Deck; the Frame uses the same A/B image scheme).
|
updates (inferred from Deck; the Frame uses the same A/B image scheme).
|
||||||
|
|||||||
+16
-10
@@ -182,17 +182,23 @@ pair_with_devkit() {
|
|||||||
comment="frame-control@$(hostname -s | tr -cs 'A-Za-z0-9._-' '-' | sed 's/^[-.]*//; s/[-.]*$//')"
|
comment="frame-control@$(hostname -s | tr -cs 'A-Za-z0-9._-' '-' | sed 's/^[-.]*//; s/[-.]*$//')"
|
||||||
[[ "$comment" == "frame-control@" ]] && comment="frame-control@computer"
|
[[ "$comment" == "frame-control@" ]] && comment="frame-control@computer"
|
||||||
body="ssh-rsa $(awk '{print $2}' "$DEVKIT_KEY.pub") $comment $MAGIC_PHRASE"
|
body="ssh-rsa $(awk '{print $2}' "$DEVKIT_KEY.pub") $comment $MAGIC_PHRASE"
|
||||||
print " Approve the pairing request in the headset (it waits about 30 seconds)"
|
print " In the headset: Steam Settings > Developer > Pair new host, then approve the request"
|
||||||
if ! resp=$(print -r -- "$body" | curl -sS --noproxy '*' -m 60 -H 'Content-Type: text/plain' \
|
# The headset refuses at once unless Steam is on its "Pair new host" screen
|
||||||
--data-binary @- -w '\n%{http_code}' "$(devkit_url /register)" 2>&1); then
|
# (verified on a Frame, 2026-09-26), so keep asking for 2 minutes while it's opened.
|
||||||
devkit_why="devkit pairing failed: no answer (${${resp##*curl: }%%$'\n'*})"; return 1
|
local deadline=$(( SECONDS + 120 ))
|
||||||
fi
|
while true; do
|
||||||
code=${resp##*$'\n'}
|
if ! resp=$(print -r -- "$body" | curl -sS --noproxy '*' -m 60 -H 'Content-Type: text/plain' \
|
||||||
text=${resp%$'\n'*}
|
--data-binary @- -w '\n%{http_code}' "$(devkit_url /register)" 2>&1); then
|
||||||
if [[ "$code" != 2* ]]; then
|
devkit_why="devkit pairing failed: no answer (${${resp##*curl: }%%$'\n'*})"; return 1
|
||||||
|
fi
|
||||||
|
code=${resp##*$'\n'}
|
||||||
|
text=${resp%$'\n'*}
|
||||||
|
[[ "$code" == 2* ]] && break
|
||||||
err=$(print -r -- "$text" | sed -n 's/.*"error"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
|
err=$(print -r -- "$text" | sed -n 's/.*"error"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
|
||||||
devkit_why="devkit pairing failed: ${err:-${text:-HTTP $code}}"; return 1
|
devkit_why="devkit pairing failed: ${err:-${text:-HTTP $code}}"
|
||||||
fi
|
[[ "$devkit_why" == *"pairing mode"* ]] && (( SECONDS < deadline )) || return 1
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
# The approval is what turns sshd on, so it may take a moment to answer.
|
# The approval is what turns sshd on, so it may take a moment to answer.
|
||||||
local i
|
local i
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
|
|||||||
+28
-2
@@ -22,6 +22,7 @@ PUB = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC+/x= frame-control@old\n"
|
|||||||
class StubDevkit(BaseHTTPRequestHandler):
|
class StubDevkit(BaseHTTPRequestHandler):
|
||||||
"""Answers like steamos-devkit-service; `reply` picks the /register outcome."""
|
"""Answers like steamos-devkit-service; `reply` picks the /register outcome."""
|
||||||
reply = (200, b"Registered\n")
|
reply = (200, b"Registered\n")
|
||||||
|
replies = [] # if set, each /register takes the next one instead of `reply`
|
||||||
properties = {"txtvers": 1, "login": "steamos", "settings": "{}", "devkit1": ["devkit-1"]}
|
properties = {"txtvers": 1, "login": "steamos", "settings": "{}", "devkit1": ["devkit-1"]}
|
||||||
bodies = []
|
bodies = []
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ class StubDevkit(BaseHTTPRequestHandler):
|
|||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
body = self.rfile.read(int(self.headers["Content-Length"]))
|
body = self.rfile.read(int(self.headers["Content-Length"]))
|
||||||
StubDevkit.bodies.append((self.path, self.headers["Content-Type"], body))
|
StubDevkit.bodies.append((self.path, self.headers["Content-Type"], body))
|
||||||
code, text = self.reply
|
code, text = StubDevkit.replies.pop(0) if StubDevkit.replies else self.reply
|
||||||
self.send_response(code)
|
self.send_response(code)
|
||||||
self.send_header("Content-type", "text/plain")
|
self.send_header("Content-type", "text/plain")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
@@ -63,6 +64,7 @@ class DevkitPairing(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
StubDevkit.reply = (200, b"Registered\n")
|
StubDevkit.reply = (200, b"Registered\n")
|
||||||
StubDevkit.bodies = []
|
StubDevkit.bodies = []
|
||||||
|
StubDevkit.replies = []
|
||||||
self.said = []
|
self.said = []
|
||||||
self._say, fc.say = fc.say, self.said.append
|
self._say, fc.say = fc.say, self.said.append
|
||||||
|
|
||||||
@@ -104,7 +106,31 @@ class DevkitPairing(unittest.TestCase):
|
|||||||
path, ctype, body = StubDevkit.bodies[0]
|
path, ctype, body = StubDevkit.bodies[0]
|
||||||
self.assertEqual((path, ctype), ("/register", "text/plain"))
|
self.assertEqual((path, ctype), ("/register", "text/plain"))
|
||||||
self.assertEqual(body.decode(), fc.register_body(PUB, "frame-control@test"))
|
self.assertEqual(body.decode(), fc.register_body(PUB, "frame-control@test"))
|
||||||
self.assertTrue(any("Approve the pairing request" in s for s in self.said))
|
self.assertTrue(any("Pair new host" in s for s in self.said))
|
||||||
|
|
||||||
|
NOT_PAIRING = (403, b'{"error": "devkit approve-ssh-key: please put the Steam client in pairing mode: '
|
||||||
|
b'Settings -> Developer -> Pair new host"}')
|
||||||
|
|
||||||
|
def test_pair_waits_for_pairing_mode(self):
|
||||||
|
# The headset refuses until Steam is on "Pair new host", then prompts.
|
||||||
|
StubDevkit.replies = [self.NOT_PAIRING, self.NOT_PAIRING, (200, b"Registered\n")]
|
||||||
|
sleep, fc.time.sleep = fc.time.sleep, lambda s: None
|
||||||
|
try:
|
||||||
|
reason = fc.devkit_pair("127.0.0.1", PUB, "c", self.port)
|
||||||
|
finally:
|
||||||
|
fc.time.sleep = sleep
|
||||||
|
self.assertIsNone(reason)
|
||||||
|
self.assertEqual(len(StubDevkit.bodies), 3)
|
||||||
|
|
||||||
|
def test_pair_gives_up_without_pairing_mode(self):
|
||||||
|
StubDevkit.reply = self.NOT_PAIRING
|
||||||
|
wait, fc.PAIRING_MODE_WAIT = fc.PAIRING_MODE_WAIT, 0
|
||||||
|
try:
|
||||||
|
reason = fc.devkit_pair("127.0.0.1", PUB, "c", self.port)
|
||||||
|
finally:
|
||||||
|
fc.PAIRING_MODE_WAIT = wait
|
||||||
|
self.assertIn("pairing mode", reason)
|
||||||
|
self.assertEqual(len(StubDevkit.bodies), 1)
|
||||||
|
|
||||||
def test_pair_refused_falls_back(self):
|
def test_pair_refused_falls_back(self):
|
||||||
StubDevkit.reply = (403, b'{"error": "timeout - Steam did not respond to the pairing request"}')
|
StubDevkit.reply = (403, b'{"error": "timeout - Steam did not respond to the pairing request"}')
|
||||||
|
|||||||
@@ -109,10 +109,10 @@ class Targets(unittest.TestCase):
|
|||||||
self.assertEqual((p['target'], p['runtime']), ('game.arm64', 'SteamLinuxRuntime_4-arm64'))
|
self.assertEqual((p['target'], p['runtime']), ('game.arm64', 'SteamLinuxRuntime_4-arm64'))
|
||||||
self.assertEqual(p['runtimes'], ['SteamLinuxRuntime_4-arm64'])
|
self.assertEqual(p['runtimes'], ['SteamLinuxRuntime_4-arm64'])
|
||||||
|
|
||||||
def test_x86_64_linux_build_is_marked_inferred(self):
|
def test_x86_64_linux_build_warns_it_may_not_start(self):
|
||||||
p = self.plan({'game.x86_64': elf(0x3E)}, 'game')
|
p = self.plan({'game.x86_64': elf(0x3E)}, 'game')
|
||||||
self.assertEqual(p['runtime'], 'SteamLinuxRuntime_4')
|
self.assertEqual(p['runtime'], 'SteamLinuxRuntime_4')
|
||||||
self.assertIn('inferred', p['note'])
|
self.assertIn("won't start", p['note'])
|
||||||
|
|
||||||
def test_native_arm64_beats_x86_64(self):
|
def test_native_arm64_beats_x86_64(self):
|
||||||
p = self.plan({'game.x86_64': elf(0x3E, pad=900), 'game.arm64': elf(0xB7)}, 'game')
|
p = self.plan({'game.x86_64': elf(0x3E, pad=900), 'game.arm64': elf(0xB7)}, 'game')
|
||||||
|
|||||||
+12
-2
@@ -58,11 +58,14 @@ def say(msg):
|
|||||||
# port 32000: GET /properties.json names the user to log in as; POST /register with
|
# port 32000: GET /properties.json names the user to log in as; POST /register with
|
||||||
# "ssh-rsa <key> <comment> <magic>" shows an approve prompt in the headset (the
|
# "ssh-rsa <key> <comment> <magic>" shows an approve prompt in the headset (the
|
||||||
# comment is what it displays, 30 s to answer), then installs the key and turns sshd on.
|
# comment is what it displays, 30 s to answer), then installs the key and turns sshd on.
|
||||||
|
# The prompt only appears while Steam is on Settings > Developer > Pair new host;
|
||||||
|
# otherwise /register answers 403 "please put the Steam client in pairing mode".
|
||||||
|
|
||||||
DEVKIT_PORT = 32000
|
DEVKIT_PORT = 32000
|
||||||
DEVKIT_SERVICE = "_steamos-devkit._tcp"
|
DEVKIT_SERVICE = "_steamos-devkit._tcp"
|
||||||
MAGIC_PHRASE = "900b919520e4cf601998a71eec318fec" # fixed token Valve's client appends
|
MAGIC_PHRASE = "900b919520e4cf601998a71eec318fec" # fixed token Valve's client appends
|
||||||
REGISTER_TIMEOUT = 60
|
REGISTER_TIMEOUT = 60
|
||||||
|
PAIRING_MODE_WAIT = 120 # seconds to keep asking while the user opens "Pair new host"
|
||||||
# A LAN host: never go through an HTTP(S)_PROXY from the environment.
|
# A LAN host: never go through an HTTP(S)_PROXY from the environment.
|
||||||
_opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
_opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
|
||||||
@@ -141,8 +144,15 @@ def devkit_pair(host, pub, comment, port=DEVKIT_PORT, on_login=None):
|
|||||||
return f"devkit service not reachable on port {port}: {why(e)}"
|
return f"devkit service not reachable on port {port}: {why(e)}"
|
||||||
if login and on_login:
|
if login and on_login:
|
||||||
on_login(login)
|
on_login(login)
|
||||||
say(" Approve the pairing request in the headset (it waits about 30 seconds)")
|
say(" In the headset: Steam Settings > Developer > Pair new host, then approve the request")
|
||||||
ok, msg = register(host, register_body(pub, comment), port)
|
body = register_body(pub, comment)
|
||||||
|
ok, msg = register(host, body, port)
|
||||||
|
# The headset refuses at once unless Steam is on its "Pair new host" screen
|
||||||
|
# (verified on a Frame, 2026-09-26), so keep asking while the user opens it.
|
||||||
|
deadline = time.monotonic() + PAIRING_MODE_WAIT
|
||||||
|
while not ok and "pairing mode" in msg and time.monotonic() < deadline:
|
||||||
|
time.sleep(3)
|
||||||
|
ok, msg = register(host, body, port)
|
||||||
return None if ok else f"devkit pairing failed: {msg}"
|
return None if ok else f"devkit pairing failed: {msg}"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -399,7 +399,9 @@ def runtime_for(target, found=()):
|
|||||||
if fmt == 'elf' and arch == 'arm64':
|
if fmt == 'elf' and arch == 'arm64':
|
||||||
return 'SteamLinuxRuntime_4-arm64', 'Native ARM64 Linux build.'
|
return 'SteamLinuxRuntime_4-arm64', 'Native ARM64 Linux build.'
|
||||||
if fmt == 'elf' and arch == 'x86_64':
|
if fmt == 'elf' and arch == 'x86_64':
|
||||||
return 'SteamLinuxRuntime_4', 'x86-64 Linux build: runs through FEX (inferred, not yet checked).'
|
return 'SteamLinuxRuntime_4', ("x86-64 Linux build: probably won't start. It needs the x86-64 Steam "
|
||||||
|
"Linux Runtime 4.0, which the Frame didn't install for a sideloaded title "
|
||||||
|
"(2026-09-26). Use an ARM64 or Windows build if there is one.")
|
||||||
raise FrameError(f"{target['path']} is a {arch} Linux program; the Frame runs ARM64 and x86-64 (through FEX) only")
|
raise FrameError(f"{target['path']} is a {arch} Linux program; the Frame runs ARM64 and x86-64 (through FEX) only")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user