aboutsummaryrefslogtreecommitdiffstats
path: root/games/README.md
blob: 46f035985b7729a5e47126c4676f10877e73517a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# games

A game launcher on DP-1: an endless horizontal strip of small slanted
tiles along the bottom, and the selected game, the one at the centre,
shown large above it in Steam's wide hero art with its title. Installed
games come first, most recently played first; owned games that are not
installed follow, greyed and framed in peach. Non-Steam shortcuts added to
Steam count as installed. A short library repeats to fill the loop.

Left and Right or the wheel move, Enter or a click on the centre tile
launches, a click on another tile selects it. Typing filters by name.
Escape or a click on the background closes. Everything goes through
`steam -silent steam://...`, so the Steam window stays down: `rungameid`
for an installed game or shortcut (keeping its Proton and launch options),
`install` for one that is not.

Down switches the strip to the Steam store: typing searches it through
the store's keyless `api/storesearch` endpoint (country fixed to
Italy, without one it answers nothing), and results show the
price, or Owned / Installed in green for games already in the library.
Enter opens the store page in the Steam client; Up or Escape goes back to
the library, keeping what was typed. Art comes from Steam's CDN; newer
apps keep theirs under hashed paths that 404, so the preview falls back
to the search's capsule image and the tile to the initial.

`games.py` reads, on every opening:

- installed games from the `appmanifest_*.acf` files of every library in
  `steamapps/libraryfolders.vdf`, leaving out Proton and the runtimes;
- shortcuts from the binary `userdata/<account>/config/shortcuts.vdf`,
  whose game id is `appid << 32 | 0x02000000`;
- covers and hero art from Steam's own `appcache/librarycache`, so
  nothing is fetched for them. A tile with no cover shows the initial.

Owned games need the Steam Web API (`IPlayerService/GetOwnedGames`), so
they come from `~/.cache/keystone/games/owned.json`, which
`games.py --refresh` rewrites in the background on every opening. The API
key is read with `pass show "$GAMES_PASS_ENTRY"`; the SteamID is derived
from the userdata account directory.

Every game window lands on workspace 8, however it was started. Steam
sets `SteamGameId` in the environment of every game it runs, native,
Proton or a non-Steam shortcut, so the shell moves any newly opened
window whose process carries it. Class rules in the Hyprland config miss
games that name themselves: a GOG Ren'Py build added as a shortcut had no
`steam_app_` class and opened wherever focus was.

## Running

    qs -p ./games
    qs -p ./games ipc call games toggle    # bound to XF86Search

## Check

    /usr/bin/python3 games/games.py --check