feat: add pixelated app icons and remove devkit prefix from steam shortcuts
No files matched your search
@@ -259,8 +259,7 @@ choice, and troubleshooting). In short, after a one-time Developer-Mode pairing:
|
||||
# override the target with --host steamos@<ip> or FRAME_HOST
|
||||
```
|
||||
|
||||
Either way the app appears in **Steam ▸ Library ▸ Non-Steam** as *Devkit Game:
|
||||
PixelVRPlayer*. To run it manually over SSH for debugging:
|
||||
Either way the app appears in **Steam ▸ Library ▸ Non-Steam** as **Pixel VR Player**. To run it manually over SSH for debugging:
|
||||
|
||||
```bash
|
||||
ssh steamos@frame '~/devkit-game/PixelVRPlayer/launch.sh /path/to/video.mp4'
|
||||
|
||||
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 936 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 936 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -94,8 +94,7 @@ All three methods below share the same setup. Do this once.
|
||||
- **Easiest to iterate:** Option B (one command builds nothing you didn't ask for
|
||||
and launches in the headset).
|
||||
|
||||
They all end up in the same place: *Steam ▸ Library ▸ Non-Steam ▸ **Devkit Game:
|
||||
PixelVRPlayer***.
|
||||
They all end up in the same place: *Steam ▸ Library ▸ Non-Steam ▸ **Pixel VR Player***.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ def main() -> int:
|
||||
'mkdir -p "$target/shaders"; printf %s "$target"'
|
||||
)
|
||||
|
||||
# 2. Copy binary, shaders and the direct-run helper.
|
||||
# 2. Copy binary, shaders, launcher, and icon.
|
||||
print("Copying binary ...")
|
||||
scp([str(binary)], f"{REMOTE_DIR}/pixelvr")
|
||||
if shaders:
|
||||
@@ -136,6 +136,9 @@ def main() -> int:
|
||||
launch_helper = PROJECT_ROOT / "tools" / "launch.sh"
|
||||
if launch_helper.is_file():
|
||||
scp([str(launch_helper)], f"{REMOTE_DIR}/launch.sh")
|
||||
icon_file = PROJECT_ROOT / "assets" / "icon.png"
|
||||
if icon_file.is_file():
|
||||
scp([str(icon_file)], f"{REMOTE_DIR}/icon.png")
|
||||
|
||||
# 3. Register the Non-Steam shortcut (runs on the host, not in Sniper).
|
||||
request = {
|
||||
@@ -165,6 +168,67 @@ def main() -> int:
|
||||
if isinstance(response, dict) and response.get("error"):
|
||||
raise RuntimeError(f"shortcut registration failed: {response['error']}")
|
||||
|
||||
# 3b. Sanitize shortcuts.vdf to remove the 'Devkit Game: ' prefix and set icon.
|
||||
sanitize_script = (
|
||||
"import glob, os, struct, shutil\n"
|
||||
"OBJ, STR, INT, U64, END = 0, 1, 2, 7, 8\n"
|
||||
"def read_cstr(buf, pos):\n"
|
||||
" end = buf.find(b'\\0', pos)\n"
|
||||
" return buf[pos:end].decode('utf-8', 'surrogateescape'), end + 1\n"
|
||||
"def parse_obj(buf, pos):\n"
|
||||
" entries = {}\n"
|
||||
" while pos < len(buf):\n"
|
||||
" kind = buf[pos]\n"
|
||||
" if kind in (END, 0x0B): return entries, pos + 1\n"
|
||||
" key, pos = read_cstr(buf, pos + 1)\n"
|
||||
" if kind == OBJ: value, pos = parse_obj(buf, pos)\n"
|
||||
" elif kind == STR: value, pos = read_cstr(buf, pos)\n"
|
||||
" elif kind == INT: value = struct.unpack_from('<i', buf, pos)[0]; pos += 4\n"
|
||||
" elif kind == U64: value = struct.unpack_from('<Q', buf, pos)[0]; pos += 8\n"
|
||||
" else: break\n"
|
||||
" entries[key] = [kind, value]\n"
|
||||
" return entries, pos\n"
|
||||
"def dump(entries):\n"
|
||||
" out = bytearray()\n"
|
||||
" for key, (kind, value) in entries.items():\n"
|
||||
" kb = key.encode('utf-8', 'surrogateescape')\n"
|
||||
" if kind == OBJ: out += bytes([OBJ]) + kb + b'\\0' + dump(value) + bytes([END])\n"
|
||||
" elif kind == STR: out += bytes([STR]) + kb + b'\\0' + value.encode('utf-8', 'surrogateescape') + b'\\0'\n"
|
||||
" elif kind == INT: out += bytes([INT]) + kb + b'\\0' + struct.pack('<i', value)\n"
|
||||
" elif kind == U64: out += bytes([U64]) + kb + b'\\0' + struct.pack('<Q', value)\n"
|
||||
" return bytes(out)\n"
|
||||
"for vdf in glob.glob(os.path.expanduser('~/.steam/steam/userdata/*/config/shortcuts.vdf')):\n"
|
||||
" try:\n"
|
||||
" with open(vdf, 'rb') as f: data = f.read()\n"
|
||||
" if not data or data[0] != OBJ: continue\n"
|
||||
" key, pos = read_cstr(data, 1)\n"
|
||||
" s_dict, _ = parse_obj(data, pos)\n"
|
||||
" mod = False\n"
|
||||
" for k, v in s_dict.items():\n"
|
||||
" if isinstance(v, list) and v[0] == OBJ:\n"
|
||||
" e = v[1]\n"
|
||||
" name = e.get('AppName', [STR, ''])[1]\n"
|
||||
" if 'Pixel' in name:\n"
|
||||
" e['AppName'] = [STR, 'Pixel VR Player']\n"
|
||||
" e['Devkit'] = [INT, 0]\n"
|
||||
" e['DevkitGameID'] = [STR, '']\n"
|
||||
" icon_p = os.path.expanduser('~/" + REMOTE_DIR + "/icon.png')\n"
|
||||
" if os.path.isfile(icon_p):\n"
|
||||
" e['icon'] = [STR, icon_p]\n"
|
||||
" grid = os.path.join(os.path.dirname(os.path.dirname(vdf)), 'config', 'grid')\n"
|
||||
" os.makedirs(grid, exist_ok=True)\n"
|
||||
" raw_id = e.get('appid', [INT, 0])[1] & 0xFFFFFFFF\n"
|
||||
" shutil.copyfile(icon_p, os.path.join(grid, f'{raw_id}.png'))\n"
|
||||
" mod = True\n"
|
||||
" if mod:\n"
|
||||
" tmp = vdf + '.tmp'\n"
|
||||
" with open(tmp, 'wb') as f: f.write(dump({key: [OBJ, s_dict]}) + bytes([END]))\n"
|
||||
" os.replace(tmp, vdf)\n"
|
||||
" except Exception:\n"
|
||||
" pass\n"
|
||||
)
|
||||
ssh(f"python3 -c {shlex.quote(sanitize_script)}")
|
||||
|
||||
# 4. Optionally launch it in the headset.
|
||||
if args.launch:
|
||||
print("Launching in the headset ...")
|
||||
@@ -184,7 +248,7 @@ def main() -> int:
|
||||
print(f"Deployed to {host}:~/{REMOTE_DIR}")
|
||||
if player_args:
|
||||
print(" argv:", " ".join(["./pixelvr", *player_args]))
|
||||
print(f" Launch from Steam > Library > Non-Steam > 'Devkit Game: {GAME}'")
|
||||
print(" Launch from Steam > Library > Non-Steam > 'Pixel VR Player'")
|
||||
print(f" Or over SSH: ssh {host} '{REMOTE_DIR}/launch.sh <video>'")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -230,7 +230,8 @@ def find(shortcuts, name, exe):
|
||||
continue
|
||||
stored_exe = entry.get("Exe", [STR, ""])[1].strip('"')
|
||||
stored_name = entry.get("AppName", [STR, ""])[1]
|
||||
if (exe and stored_exe == exe) or stored_name == name:
|
||||
devkit_id = entry.get("DevkitGameID", [STR, ""])[1]
|
||||
if (exe and stored_exe == exe) or stored_name == name or stored_name in ("Devkit Game: PixelVRPlayer", "Devkit Game: Pixel VR Player") or devkit_id == "PixelVRPlayer":
|
||||
return key, entry
|
||||
return None, None
|
||||
|
||||
@@ -276,11 +277,12 @@ def main():
|
||||
shortcuts[str(index)] = [OBJ, entry]
|
||||
if "appid" not in entry:
|
||||
entry["appid"] = [INT, to_i32(app_id(quoted_exe, name))]
|
||||
icon_path = os.path.join(start_dir, "icon.png") if os.path.isfile(os.path.join(start_dir, "icon.png")) else ""
|
||||
for field, value in (
|
||||
("AppName", [STR, name]),
|
||||
("Exe", [STR, quoted_exe]),
|
||||
("StartDir", [STR, '"%s"' % start_dir]),
|
||||
("icon", [STR, ""]),
|
||||
("icon", [STR, icon_path]),
|
||||
("ShortcutPath", [STR, ""]),
|
||||
("LaunchOptions", [STR, ""]),
|
||||
("IsHidden", [INT, 0]),
|
||||
@@ -295,6 +297,17 @@ def main():
|
||||
):
|
||||
entry[field] = value
|
||||
entry.setdefault("tags", [OBJ, {}])
|
||||
if icon_path and os.path.isfile(icon_path):
|
||||
try:
|
||||
import shutil
|
||||
steam_user_dir = os.path.dirname(os.path.dirname(path))
|
||||
grid_dir = os.path.join(steam_user_dir, "config", "grid")
|
||||
os.makedirs(grid_dir, exist_ok=True)
|
||||
raw_appid = entry["appid"][1] & 0xFFFFFFFF
|
||||
shutil.copyfile(icon_path, os.path.join(grid_dir, "%d_icon.png" % raw_appid))
|
||||
shutil.copyfile(icon_path, os.path.join(grid_dir, "%d.png" % raw_appid))
|
||||
except Exception:
|
||||
pass
|
||||
print("%s '%s' (%d shortcut(s) in the library)" % (
|
||||
"added" if created else "updated", name, len(shortcuts)))
|
||||
|
||||
@@ -509,6 +522,13 @@ if [ -f "$srcdir/VERSION" ]; then
|
||||
elif [ -n "$VERSION" ]; then
|
||||
printf '%s\n' "$VERSION" > "$INSTALL_DIR/VERSION"
|
||||
fi
|
||||
if [ -f "$srcdir/icon.png" ]; then
|
||||
cp "$srcdir/icon.png" "$INSTALL_DIR/icon.png"
|
||||
elif [ -n "$script_dir" ] && [ -f "$script_dir/icon.png" ]; then
|
||||
cp "$script_dir/icon.png" "$INSTALL_DIR/icon.png"
|
||||
elif [ -f "assets/icon.png" ]; then
|
||||
cp "assets/icon.png" "$INSTALL_DIR/icon.png"
|
||||
fi
|
||||
ok "$(ls -1 "$INSTALL_DIR/shaders" | wc -l | tr -d ' ') shaders, $(du -h "$INSTALL_DIR/pixelvr" 2>/dev/null | cut -f1) binary"
|
||||
|
||||
missing_libs=""
|
||||
|
||||
@@ -40,6 +40,9 @@ cp tools/install.sh "${stage}/PixelVRPlayer/install.sh"
|
||||
if [[ -f "tools/Install Pixel VR Player.desktop" ]]; then
|
||||
cp "tools/Install Pixel VR Player.desktop" "${stage}/PixelVRPlayer/Install Pixel VR Player.desktop"
|
||||
fi
|
||||
if [[ -f "assets/icon.png" ]]; then
|
||||
cp "assets/icon.png" "${stage}/PixelVRPlayer/icon.png"
|
||||
fi
|
||||
if [[ -n "${VERSION}" ]]; then
|
||||
printf '%s\n' "${VERSION}" > "${stage}/PixelVRPlayer/VERSION"
|
||||
fi
|
||||
|
||||