diff --git a/clients/decky/README.md b/clients/decky/README.md index 20778036..10d82718 100644 --- a/clients/decky/README.md +++ b/clients/decky/README.md @@ -2,49 +2,61 @@ Stream to your **Steam Deck** without ever leaving Gaming Mode. This **[Decky Loader](https://decky.xyz/)** plugin adds a **Punktfunk** panel to the Quick Access Menu -(the `…` button): discover hosts on your network, pair with a PIN, tweak stream settings, and launch -a fullscreen, gamescope-focused stream — all from the couch, gamepad-navigable. +(the `…` button): the hosts you can stream, the pinned cards you set up, and one tap into each. -The video itself is the native GTK4 Linux client (the `io.unom.Punktfunk` flatpak); the plugin -discovers, pairs, configures, and *launches it the right way* so gamescope fullscreens it — the same -Steam-shortcut trick MoonDeck uses. Because it's built from real Steam UI primitives (`@decky/ui`), -the panel looks and feels native to Gaming Mode. +The plugin is a **launcher**, not a client. It doesn't decode video, browse your library, or hold +any settings of its own — the Rust client does all of that, and the plugin's job is to start it +*the right way* so gamescope fullscreens and focuses it (the same Steam-shortcut trick MoonDeck +uses). Everything the panel doesn't do is one tap away in the client's own gamepad UI. ## What it does -1. **Discover** — browses the LAN over mDNS for Punktfunk hosts, in both the QAM panel and a - fullscreen page; each host row opens a details view (address, pairing policy, certificate - fingerprint to cross-check against the host's log). -2. **Pair** — for a host that requires it, a gamepad-navigable PIN keypad runs the SPAKE2 pairing - ceremony headlessly, then remembers the host so future streams connect silently. -3. **Stream** — launches fullscreen via a branded "Punktfunk" Steam shortcut so gamescope focuses it. -4. **Games** — each host row has a games button that opens its **library picker**: pin titles as - one-tap "Stream " rows in the QAM (jump straight into e.g. Playnite on the host), or - **"Open library on screen"** to launch the client's controller-driven, console-style library - browser (aurora backdrop + poster coverflow; A plays, B returns to Gaming Mode). Pins survive - plugin reinstalls (stored next to the client's config) and follow a host across IP changes - (matched by certificate fingerprint). -5. **Settings** — the client's whole settings store, written to its config. Laid out like SteamOS's - own Settings: a left rail of categories (`SidebarNavigation`), one page each, so no page needs - scrolling. The categories and their order are the console settings screen's — Stream (resolution - / refresh / render scale / bitrate / compositor), Video (codec / decoder / GPU / HDR / 4:4:4), - Presentation (prioritize / smoothness buffer / V-Sync / VRR), Audio (channels / output + mic - device / echo cancellation), Controllers, Touch & mouse, Interface (stats overlay / auto-wake / - library / fullscreen). The device pickers are populated - from the session binary (`--list-adapters` / `--list-audio`); the GPU row appears only where - there is more than one adapter. -6. **About** — plugin version, an explicit "Check for updates" button, the setup-guide link, and - a force-stop for a wedged stream client. +1. **Hosts** — the hosts on your network plus the ones you've saved, in one list. Discovery is + mDNS; saved hosts are also probed directly, so a box reached over Tailscale or a VPN shows as + online even though it never advertises. Rows sort online-first, then most recently used. +2. **Trust** — an unpaired host opens a small sheet with two ways in: + - **Request access** (the default) — no PIN. The host's operator approves this Deck in its + console or web UI and the stream starts by itself. See [Request access](#request-access). + - **Use a PIN instead** — the gamepad-navigable keypad, running the same SPAKE2 ceremony. +3. **Stream** — launches fullscreen via a branded "Punktfunk" Steam shortcut so gamescope focuses + it. A sleeping host is woken first (the client runs the real wake-and-wait loop, then dials). +4. **Pinned cards** — a *(host, profile)* pair renders nested under its host as `▸ ` + and streams with that settings profile applied. Cards are the **shared** pinning model every + other client speaks, stored on the host's record — so one you make in the desktop client shows + up here, and vice versa. The plugin renders them; it doesn't create or edit them. +5. **Open Punktfunk** — launches the client's **console home**: the host picker, add-host by + address, PIN pairing, the game library browser, and the **full settings screen**. This is where + everything the panel no longer does now lives. +6. **About** — plugin version, "Check for updates", "Recreate library shortcut", and a force-stop + for a wedged stream. To leave a stream: the in-client controller chord (**L1 + R1 + Start + Select**), or close the "game" from the Steam overlay — either returns you to Gaming Mode. +### Request access + +Request access is not a second pairing ceremony — it is a **launch**. The plugin saves the host +with the fingerprint it **advertised**, then starts an ordinary identified connect with the +handshake budget stretched to 185 s. The host *parks* that connection until its operator approves +the device, then admits the same connection; the stream starts on its own, and the record flips +to **paired** so every later stream is silent. + +**No advertised fingerprint, no request access.** That pinned fingerprint is the only thing +standing between a 185-second wait and an impostor answering for the host, so a host you typed in +by address gets the PIN path only — and the sheet says why. The plugin never trusts-on-first-use +past a missing fingerprint. + ## Install on the Deck -You need **[Decky Loader](https://decky.xyz/)** and the **`io.unom.Punktfunk` flatpak** -([`packaging/flatpak`](../../packaging/flatpak/README.md)) installed on the Deck — SteamOS `/usr` is -read-only, so the flatpak (which bundles libadwaita/SDL3) is the canonical client. Discovery uses -`avahi-browse`, which ships on SteamOS/Bazzite. +You need **[Decky Loader](https://decky.xyz/)** and a **Punktfunk client** on the Deck. On a normal +Deck that's the `io.unom.Punktfunk` flatpak ([`packaging/flatpak`](../../packaging/flatpak/README.md)) — +SteamOS `/usr` is read-only, so the flatpak (which bundles libadwaita/SDL3) is the canonical client. +A native install (sysext, distro package, nix profile, your own build) works too. + +**The client must be v0.22.0 or newer** — that is when the headless `punktfunk` CLI shipped, and +the panel drives everything through it. An older client says so in the panel, with the update +button that fixes it right there. (Discovery no longer needs `avahi-browse` on the Deck; the +client's own mDNS does it.) **Recommended — install from URL** (published by CI): in Decky → Settings → **Developer Mode** → **Install Plugin from URL**, paste: @@ -55,17 +67,15 @@ https://unom.io/pf-decky (short link for `https://git.unom.io/api/packages/unom/generic/punktfunk-decky/latest/punktfunk.zip`; for a pinned version use `https://git.unom.io/api/packages/unom/generic/punktfunk-decky//punktfunk.zip` -directly). The plugin then **self-updates** without -the Decky store — when a newer build exists, an **Update** button appears and drives Decky -Loader's own (SHA-256-verified) install. Installs and updates can take a couple of minutes on some -networks: Decky's installer also contacts its plugin store first, which may be slow or blackholed -before the actual download proceeds. +directly). The plugin then **self-updates** without the Decky store — when a newer build exists, an +**Update** button appears and drives Decky Loader's own (SHA-256-verified) install. Installs and +updates can take a couple of minutes on some networks: Decky's installer also contacts its plugin +store first, which may be slow or blackholed before the actual download proceeds. ### Updating the client The plugin also reports — and where it can, installs — updates for the **client** it launches. -What is possible depends on how that client was installed, and the About tab names the install -kind so the answer is never a mystery: +What is possible depends on how that client was installed: | Install | Update | | --- | --- | @@ -88,6 +98,8 @@ pnpm install pnpm build # rollup → dist/index.js pnpm run package # → out/punktfunk/ + out/punktfunk-v.zip DECK=deck@ pnpm run deploy # rsync → /tmp, sudo-install into the root-owned plugins dir, restart loader + +python3.13 scripts/test-backend.py # backend unit checks (needs Python ≥3.10) ``` `~/homebrew/plugins/` is root-owned (the loader runs as root), so `deploy.sh` stages to a temp dir @@ -96,28 +108,46 @@ restart is required for an out-of-band install to appear. ## Architecture +Everything below the panel is the CLI. `main.py` builds argv and maps exit codes; it parses none of +the client's data files and re-implements none of its rules. + | File | Role | | --- | --- | -| `src/index.tsx` | Plugin entry: the QAM panel + route registration. | -| `src/page.tsx` | The `/punktfunk` fullscreen page — Hosts (with per-host details) / Settings / About tabs. | -| `src/settings.tsx` · `src/pair.tsx` | The settings screen (a `SidebarNavigation` of seven category pages over one shared settings object); the gamepad-navigable PIN-pairing modal. | -| `src/library.tsx` | The per-host game picker (pin/unpin, "Open library on screen") + the pinned-game launch helper. | -| `src/hostmgmt.tsx` | Add / edit host dialogs — mutate the shared known-hosts store (`client-known-hosts.json`) via the flatpak client's headless modes, so a host saved here shows up in the desktop client too. | -| `src/ui.tsx` | Shared UI primitives for the fullscreen page + modals (right-aligned row actions, consistent Field layout). | -| `src/hooks.ts` · `src/boundary.tsx` | Shared discovery/update/pins hooks + actions; the render error boundary. | -| `src/steam.ts` | Steam-shortcut launch (`AddShortcut` / `SetAppLaunchOptions` / `RunGame`) — the focus-correct stream start. The shortcut's exe is `/bin/sh` with the wrapper passed as an argument, so the script never needs an exec bit (Decky's zip extraction drops it and the root-owned plugins dir can't be chmodded by the unprivileged backend). Launch extras ride env-prefix tokens: `PF_LAUNCH=` (pinned game) / `PF_BROWSE=1` + `PF_MGMT=` (on-screen library); ids are validated space/quote-free at pin AND launch time. | -| `src/backend.ts` | Typed `callable` bridges to `main.py`. | -| `bin/punktfunkrun.sh` | The launch wrapper the Steam shortcut runs (so the window is focusable); maps `PF_LAUNCH`/`PF_BROWSE`/`PF_MGMT` to `--launch`/`--browse`/`--mgmt`. An older flatpak ignores the flags harmlessly (plain stream / hosts page). | -| `main.py` | Backend: `discover` (via `avahi-browse`) / `pair` / `library` (headless flatpak `--library`, TSV) / pins store (`decky-pinned.json`) / settings / `kill_stream` / `check_update` (with an explicit CA-bundle search — Decky's embedded Python has no usable default TLS roots on SteamOS). | -| `scripts/test-backend.py` | Stdlib-only checks for the backend's pure parsers (TSV, error classes, avahi TXT) + the pins round trip. | +| `src/index.tsx` | Plugin entry + the QAM panel: update banner, hosts (with nested pinned cards), the console-home door, about. | +| `src/hooks.ts` | `useHosts` (one call merging discovery and the saved store), the update hooks, and the launch action. Also the trust-state model the rows render. | +| `src/trust.tsx` · `src/pair.tsx` | The trust sheet (Request access / Use a PIN instead / Cancel) and the gamepad-navigable PIN keypad. | +| `src/steam.ts` | Steam-shortcut launch (`AddShortcut` / `SetAppLaunchOptions` / `RunGame`) — the focus-correct stream start. The shortcut's exe is `/bin/sh` with the wrapper passed as an argument, so the script never needs an exec bit (Decky's zip extraction drops it and the root-owned plugins dir can't be chmodded by the unprivileged backend). | +| `src/backend.ts` · `src/boundary.tsx` · `src/os-icon.tsx` | Typed `callable` bridges to `main.py`; the render error boundary; the host row's OS mark. | +| `bin/punktfunkrun.sh` | The launch wrapper the Steam shortcut runs (so the window is focusable). Reads `PF_REF` / `PF_PROFILE` / `PF_REQUEST_ACCESS` / `PF_BROWSE` and runs `punktfunk launch` — or the session's `--browse` for console home. | +| `main.py` | Backend: four thin CLI shells (`discover` / `hosts` / `pair` / `trust_host`) plus the Steam-side work only a plugin can do — `runner_info`, `shortcut_art`, `apply_controller_config`, `kill_stream`, `check_update` / `update_client` (with an explicit CA-bundle search — Decky's embedded Python has no usable default TLS roots on SteamOS). | +| `scripts/test-backend.py` | Stdlib-only checks: argv shape, the CLI exit-code mapping, and the Steam configset editor. | | `plugin.json` · `update.json` | Decky manifest; CI-baked update channel. | +### Why the launch goes through Steam + +gamescope only gives focus and fullscreen to the window tree Steam launched via `reaper` (it +detects the "current app" by AppID — gamescope#484). A client spawned from the plugin's own +backend comes up invisible and unfocused. So the plugin registers non-Steam shortcuts whose exe is +`/bin/sh` running `bin/punktfunkrun.sh`, and starts them with `RunGame`. + +There are **two** shortcuts, both named `Punktfunk` so Steam keys them to one Steam Input +configset (the key is the lowercase name): a hidden, stateful one that carries the stream, and the +visible, stateless library entry that opens console home. + ## Limitations / next steps -- No manual "add host by IP" entry yet (discovery is mDNS-only). -- No in-stream overlay inside the plugin — the client owns the session once launched. -- Pairing needs the operator to **arm pairing on the host** so it shows the PIN; the plugin can't arm - it remotely. +- **Profiles and pinned cards can't be created here** — the panel renders them; making one needs + the desktop client, or the client's own gamepad UI once that work lands. A Deck with no profiles + simply sees host rows, and nothing is broken. +- **Per-game pins are on hold.** The shared model pins *host+profile*; nothing in the shared store + persists a pinned *game* yet. The old `decky-pinned.json` is left on disk untouched so a later + migration can read it. +- Pairing with a PIN needs the operator to **arm pairing on the host** so it shows the PIN; the + plugin can't arm it remotely. Request access needs no arming — just an approval. +- **A parked connect looks like a hanging one.** The plugin toasts before launching a request-access + stream to set expectations, which is a patch rather than a fix; teaching the session's connect + screen the same "waiting for approval" copy the console shell already has would pay off for every + shell. ## Related diff --git a/docs-site/content/docs/client-settings.md b/docs-site/content/docs/client-settings.md index 025e744d..0d46f6a0 100644 --- a/docs-site/content/docs/client-settings.md +++ b/docs-site/content/docs/client-settings.md @@ -15,13 +15,14 @@ The Linux, Windows, Mac, iPhone/iPad and Android apps group settings the same wa **Display**, **Input**, **Audio**, **Controllers** — under *Preferences* on Linux and *Settings* elsewhere. The Apple TV app shows one scrolling list instead, and so does any client's settings screen reached with a controller. A controller-driven launch (Steam Deck Gaming Mode) opens the -client's **console home**, whose settings screen is one steppable list; the Decky plugin's Settings -tab covers the same store in the same groups and the same order, as a left rail of categories the -way SteamOS's own Settings looks. The console home is part of the -client — it is not the host's -[web console](/docs/web-console). +client's **console home**, whose settings screen is one steppable list of sections — **Stream**, +**Video**, **Presentation**, **Audio**, **Controller**, **Touchscreen**, **Interface**, +**Profiles**. On a Steam Deck that list *is* the settings surface: the +[Decky plugin](/docs/steam-deck) is a launcher and keeps no settings of its own, and its **Open +Punktfunk** button puts the console home one tap from the Quick Access Menu. The console home is +part of the client — it is not the host's [web console](/docs/web-console). -Linux stores them in `~/.config/punktfunk/client-gtk-settings.json`, the same file the Decky plugin +Linux stores them in `~/.config/punktfunk/client-gtk-settings.json`, the same file the console home writes, so a change in either shows up in the other. Windows uses `%APPDATA%\punktfunk\client-windows-settings.json`; the Apple and Android apps use their own stores. @@ -45,9 +46,9 @@ and your client scales what it gets — see **Match window** — *default: off.* The stream mode follows your window instead, and each resize renegotiates the host's display and encoder, so a windowed session stays pixel-exact. Fullscreen -degenerates to the display's native mode. Offered by the Linux, Windows, Mac, iPhone/iPad, console -home and Decky screens (on Decky it sits in the Resolution picker, and Gaming-Mode streams are -always fullscreen, so it lands on native); not by Android. +degenerates to the display's native mode. Offered by the Linux, Windows, Mac, iPhone/iPad and +console-home screens (in the console home it is an option inside the Resolution picker, and a +Gaming-Mode stream is always fullscreen, so there it lands on native); not by Android. **Refresh rate** — *default: Native*, the refresh of the display your window is on. The Apple app stores an explicit rate (60 Hz by default): iPhone and iPad offer the rates the device can display, @@ -68,11 +69,11 @@ capacity probe stay off for the whole session. multiplied by this, and your device resamples the result to its window. Above 1× supersamples for sharpness, at more bandwidth *and* more decode work; below 1× is lighter on both the host and the link. The stops run 0.5× to 4×. The result is floored to an even size and capped per axis at -4096 px for H.264, 8192 px otherwise. Offered everywhere except the console home's list. +4096 px for H.264, 8192 px otherwise. Offered everywhere. **Video codec** — *default: Automatic.* A soft preference: the host emits your choice when it can also produce it, otherwise the best codec you both speak, in the order HEVC → AV1 → H.264. -**PyroWave** is never auto-picked — pick it explicitly on Linux, Windows, the console home, Decky, or +**PyroWave** is never auto-picked — pick it explicitly on Linux, Windows, the console home, or an Apple device whose decode probe passes; anywhere else it isn't offered, and asking for it lands on that same order. See [PyroWave](/docs/pyrowave). The Android and Apple apps hide AV1 unless the device has a hardware AV1 decoder; Android never offers PyroWave. @@ -86,13 +87,13 @@ Full detail: [HDR](/docs/hdr). needs HEVC or PyroWave, the host's own 4:4:4 policy left on, a capture path that delivers full chroma, and a GPU that can encode it; if any gate fails the host says 4:2:0 before your decoder is built. The Apple, Linux and Windows apps all advertise it (Apple additionally requires its hardware -decode probe to pass). The console home and Decky offer the toggle; Android doesn't. +decode probe to pass). The console home offers the toggle; Android doesn't. **Prioritize** — *default: Lowest latency.* What the client optimizes for when a decoded frame is ready. **Lowest latency** shows every frame the moment the display can take it, so a network hiccup becomes an occasional repeated or skipped frame. **Smoothness** holds a small buffer that evens -those hiccups out, at that buffer's worth of added delay. Linux and Windows apps, the console home -and Decky; the Apple and Android apps have carried the same setting for a while, and it is stored +those hiccups out, at that buffer's worth of added delay. Linux and Windows apps and the console +home; the Apple and Android apps have carried the same setting for a while, and it is stored under the same name, so a [profile](/docs/profiles-and-links) means the same thing on every device. **Smoothness buffer** — *default: Automatic (two frames).* Only shown under **Smoothness**. How @@ -106,7 +107,7 @@ the instant it's ready instead of waiting for the screen's next refresh: the low can give you, at the cost of visible tearing on fast motion. It is **best-effort** — not every driver or compositor offers a tearing mode, and where none is available the stream stays tear-free. The Detailed [stats overlay](/docs/stats) names the mode actually in use, so you can tell "off" -from "off but unavailable". Linux and Windows apps, the console home and Decky. +from "off but unavailable". Linux and Windows apps and the console home. **Follow variable refresh rate** — *default: on.* On a VRR / FreeSync / G-Sync screen, let the panel refresh in step with the stream rather than on a fixed cadence — which removes the wait between a @@ -115,8 +116,8 @@ windowed one is at the compositor's mercy) and is harmless on a fixed-refresh sc graphics driver that offers the modern queue-free display mode; on an older driver it does nothing unless you also set `PUNKTFUNK_VRR_FIFO=1` (see [configuration](/docs/configuration)), because the older way of following a panel costs noticeable latency on a fixed-refresh screen. The stats overlay -reports `vrr yes` once it has *measured* that the panel really is following. Linux and Windows apps, -the console home and Decky. +reports `vrr yes` once it has *measured* that the panel really is following. Linux and Windows apps +and the console home. **Host compositor** — *default: Automatic.* Which backend a **Linux** host uses to drive the virtual output. Advisory: a host without that backend quietly auto-detects instead. @@ -130,7 +131,7 @@ claims a sink advertising exactly that many channels, so applications produce re **Windows** host loopback-captures your current output endpoint and lets Windows convert it — so 5.1 from a stereo endpoint is an upmix, not new channels. Offered everywhere. -**Microphone** — *default: off on Linux, Windows, Android, the console home and Decky; on in the +**Microphone** — *default: off on Linux, Windows, Android and the console home; on in the Apple app.* Sends this device's microphone to the host's virtual mic. On Linux and Windows the row is spelled *Stream microphone*, and **Ctrl+Alt+Shift+V** mutes it mid-stream without ending anything — see [Muting your microphone](/docs/input#muting-your-microphone). @@ -142,18 +143,18 @@ from an echo-cancelled PipeWire source when your desktop provides one, on **Wind for the Communications stream category so the endpoint's processing engages, and on **Apple** and **Android** the platform's voice-processing mode. Turn it off if your microphone already runs its own processing, or if the canceller makes your voice sound thin. The row sits under the microphone -toggle and greys out while the microphone is off. Offered by the Linux, Windows, Apple, Android, -console-home and Decky clients. What it can and can't fix is in +toggle and greys out while the microphone is off. Offered by the Linux, Windows, Apple, Android and +console-home clients. What it can and can't fix is in [Why do I hear myself](/docs/echo). **Speaker** and **Microphone** device pickers — *default: System default.* Which endpoint stream -audio plays out of, and which input feeds the uplink. Only the Linux app (PipeWire nodes), the -**Mac** app (which also has a microphone *channel* picker) and **Decky** have these — iPhone, iPad, -Apple TV, Android and the console home have none, and the Windows app has none and ignores a stored -speaker choice. On Linux, a device that has since disappeared keeps a "(not detected)" entry rather -than silently snapping back to the default; the Mac shows it as "Unavailable device" and Decky as -"(not connected)". Decky reads the endpoint list from the client's session binary, so a client -older than the two-binary split leaves these pickers on Automatic. +audio plays out of, and which input feeds the uplink. Only the Linux app (PipeWire nodes) and the +**Mac** app (which also has a microphone *channel* picker) have these — iPhone, iPad, Apple TV, +Android and the console home have none, and the Windows app has none and ignores a stored speaker +choice. On Linux, a device that has since disappeared keeps a "(not detected)" entry rather than +silently snapping back to the default; the Mac shows it as "Unavailable device". A Steam Deck in +Gaming Mode therefore has no endpoint picker at all: the session uses whatever the Desktop-Mode app +last stored, and the system default otherwise. ## Input @@ -182,7 +183,7 @@ client greys them out to say so. **Gamepad type** (*Controller type* on Apple, Android and the console home) — *default: Automatic*, which matches each physical controller. The pickers offer Xbox 360, Xbox One, DualSense and -DualShock 4 everywhere, plus Steam Deck on Linux, Android, the console home and Decky. Your client +DualShock 4 everywhere, plus Steam Deck on Linux, Android and the console home. Your client declares a type per pad as it connects — Automatic declares what that controller really is, an explicit choice declares your choice — and the host builds each virtual pad from that. A type the host has no backend for degrades to an Xbox 360 pad rather than failing: Xbox One on a Windows host, @@ -193,8 +194,8 @@ which forwards *every* connected controller, each as its own player, on Linux, W console home. Pinning one restricts the session to that controller alone — single-player. The Android app has no such picker. -**Capture system shortcuts** — *default: on.* Offered by the Linux and Windows apps, the console home -and Decky; Windows spells the row out as *Capture system shortcuts (Alt+Tab, Win, …)*. On a Deck it +**Capture system shortcuts** — *default: on.* Offered by the Linux and Windows apps and the console +home; Windows spells the row out as *Capture system shortcuts (Alt+Tab, Win, …)*. On a Deck it matters only for a keyboard you attached yourself, for the reason the paragraph below gives: Gaming Mode is gamescope, which has nothing to hold back. On, Alt+Tab and the Windows key (Super on Linux) reach the host while the stream has input captured. Off, they act on this machine @@ -215,21 +216,22 @@ the wlroots compositors all do, and X11 sessions grab the keyboard directly. Und Wake-on-LAN and waits for it to boot — only for a host whose MAC address this client has already learned. Turn it off for hosts you reach over a VPN, where "offline" usually means "not reachable by broadcast" and the wake only adds a delay. The Linux, Windows, Apple and Android apps have this -toggle, as do the console home and Decky — and note that the Decky plugin sends a wake of its own -before a stream starts whatever this setting says, so on a Deck it governs the client's connect -rather than the launch. The console home also offers wake as an explicit action on an offline host. -See +toggle, as does the console home — and on a Steam Deck it governs the +[Decky plugin's](/docs/steam-deck) launches too, because the plugin starts every stream through the +client, which reads this setting like any other connect. The console home also offers wake as an +explicit action on an offline host, whatever the toggle says. See [Wake-on-LAN](/docs/wake-on-lan). **Show game library** — *default: off on Linux and Windows; on in the Apple and Android apps.* Browse a paired host's games and launch one directly; the Windows app still labels it experimental. The -console home and Decky have the toggle too — on Decky it governs the *client's* screens, since the -plugin's own library browser works either way. See [Game library](/docs/game-library). +console home has the toggle too, and it governs the desktop clients that share the store — the +console's own **Library** button is offered on any paired host either way. See +[Game library](/docs/game-library). **Start streams in fullscreen** — *default: on.* On Linux and Windows, F11 or Alt+Enter leaves fullscreen live. On a Mac the setting is **Fullscreen while streaming**, and the window comes back -when you return to the host list. The console home and Decky carry the row for the desktop client -that shares the store — a Gaming-Mode launch is fullscreen whatever it says. iPhone, iPad, Apple TV +when you return to the host list. The console home carries the row for the desktop client that +shares the store — a Gaming-Mode launch is fullscreen whatever it says. iPhone, iPad, Apple TV and Android have no equivalent. ## Overlay @@ -237,9 +239,9 @@ and Android have no equivalent. **Statistics overlay** — *default: Normal.* Four tiers — Off, Compact, Normal, Detailed — each a superset of the one before. This setting only picks the tier a session *starts* at — you can cycle them live in-stream, with a shortcut that differs by platform. The Apple app additionally lets you -choose which corner the overlay sits in (Top Left, Top Right, Bottom Left, Bottom Right). The Decky -plugin has the tier picker too, in its Settings section. The shortcuts, and every number in the -overlay, are in +choose which corner the overlay sits in (Top Left, Top Right, Bottom Left, Bottom Right). The +console home has the tier picker too, as **Statistics overlay** under **Interface**. The shortcuts, +and every number in the overlay, are in [Understanding the stats overlay](/docs/stats). ## Settings that are facts about your device @@ -251,8 +253,8 @@ stay global and **cannot be put in a settings profile**: vendor-ordered and falls back on its own; change it only when debugging, and note that `PUNKTFUNK_DECODER` overrides it ([Configuration](/docs/configuration#client-side-native-clients)). The decoder picker is on Linux, - Windows, in the console home and in Decky; the GPU picker on Windows, and on Linux and Decky only - when the machine has more than one adapter — which a Deck doesn't, so the row isn't there. The + Windows and in the console home; the GPU picker on Windows, and on Linux only when the machine has + more than one adapter — the console home has none, and a Deck has a single adapter anyway. The Apple and Android apps have neither. - **Speaker** and **Microphone** device pickers — this device's audio endpoints. - **Forwarded controller** — which physical pad is in your hands. The *type* the host creates is a diff --git a/docs-site/content/docs/clipboard.md b/docs-site/content/docs/clipboard.md index fbb370fe..af27dbca 100644 --- a/docs-site/content/docs/clipboard.md +++ b/docs-site/content/docs/clipboard.md @@ -77,7 +77,8 @@ The setting is read when a session starts, so if you change it while streaming, macOS can also flip it mid-session: **Stream ▸ Share Clipboard** (⌃⌥⇧C), which becomes **Stop Sharing Clipboard** once the host has acknowledged it. -iOS, iPadOS, tvOS and the Steam Deck Decky plugin have no clipboard switch — see +iOS, iPadOS, tvOS and a Steam Deck in Gaming Mode have no clipboard switch — neither the Decky +panel nor the client's console home has a host edit sheet — see [what each client does](#which-hosts-and-clients-support-it) below. ## Nothing crosses until something pastes @@ -134,8 +135,9 @@ when a host application pastes. The **Linux client has the switch but no working clipboard bridge**: it enables the plane and then has no code to read or write the desktop's own clipboard, so nothing is announced and nothing is -pasted. Turning it on there is harmless but has no effect today. The Decky plugin on the Steam Deck -has no switch at all. +pasted. Turning it on there is harmless but has no effect today. On a Steam Deck in Gaming Mode +there is no switch at all — the Decky panel doesn't edit hosts — and since a Deck streams with that +same Linux client, a switch there would have nothing to move anyway. When you copy **on the Windows client**, images cross only if the copying application publishes the registered `PNG` clipboard format. Many Windows apps publish only a bitmap, and those copies aren't diff --git a/docs-site/content/docs/game-library.md b/docs-site/content/docs/game-library.md index 158d44eb..631880a2 100644 --- a/docs-site/content/docs/game-library.md +++ b/docs-site/content/docs/game-library.md @@ -132,11 +132,10 @@ and runs what it already knows about the title, so a client can never hand the h - **Android** — the library lives only in the controller-optimized home, which a TV always uses and a phone or tablet switches to when a controller is connected. Press **Y** on a saved host, or open its options and choose **Library**. -- **Steam Deck (Decky)** — the plugin's per-host **Games** picker lists the library and lets you - **Pin** titles; a pinned game becomes a one-tap row under **Pinned Games** in the Quick Access Menu. - The picker itself doesn't launch anything — either tap a pinned row, or use **Open library on - screen** to browse the host's games full-screen on the Deck and launch from there. See - [Steam Deck](/docs/steam-deck). +- **Steam Deck (Decky)** — the panel is a launcher and browses nothing itself: tap **Open + Punktfunk**, which opens the client's console home, and a paired host's **Library** button is + right there — full-screen covers, gamepad-navigable, and a press starts the stream with the title + launching. See [Steam Deck](/docs/steam-deck). - **Moonlight** — when the host runs with `--gamestream`, your library appears in Moonlight's app list beside `Desktop`, with covers served by the host. A title keeps the same app id across host restarts, so Moonlight's cached tiles stay correct. Titles with no launch recipe are left out. diff --git a/docs-site/content/docs/input.md b/docs-site/content/docs/input.md index 6cb01d4e..a78a772a 100644 --- a/docs-site/content/docs/input.md +++ b/docs-site/content/docs/input.md @@ -49,8 +49,9 @@ your settings. If the stream isn't sending a microphone at all (**Stream microph [client settings](/docs/client-settings#audio)) the shortcut does nothing and no badge appears, rather than pretending to mute something. -This is on the **Linux and Windows** clients. The Apple, Android and Decky clients have no mute -shortcut yet; turn **Stream microphone** off in their settings instead. +This is on the **Linux and Windows** clients — including a Steam Deck stream, which is the Linux +client, so an attached keyboard gets the chord. The Apple and Android clients have no mute shortcut +yet; turn **Stream microphone** off in their settings instead. Alt-Tabbing away releases input on its own and takes it back when you return. A release you asked for with the chord stays released until you opt back in. Either way, keys and buttons you were diff --git a/docs-site/content/docs/install-client.md b/docs-site/content/docs/install-client.md index 6983ab47..3da99035 100644 --- a/docs-site/content/docs/install-client.md +++ b/docs-site/content/docs/install-client.md @@ -79,9 +79,11 @@ list: [Clients → the `punktfunk` CLI](/docs/clients#scripting-the-punktfunk-cl ## Steam Deck Most Deck users want **Gaming Mode**: install the **[Decky plugin](/docs/steam-deck)** and a -**Punktfunk** panel lands in the Quick Access Menu, so you can discover hosts, pair with a PIN, and -stream **without dropping to the desktop**. Follow the **[Steam Deck (Decky) guide](/docs/steam-deck)** -— it walks through Decky Loader, the plugin, and the one-time client install. +**Punktfunk** panel lands in the Quick Access Menu, so you can find a host, get let in (a PIN, or a +request the host's operator approves), and stream **without dropping to the desktop**. Everything +else — settings, the game library, adding a host by address — is one tap away in the client's own +gamepad UI. Follow the **[Steam Deck (Decky) guide](/docs/steam-deck)** — it walks through Decky +Loader, the plugin, and the one-time client install. > The plugin doesn't decode video itself — it drives whichever `punktfunk-client` is installed on > the Deck. The Flatpak below is the tested default; a native package or a sysext works too. If your diff --git a/docs-site/content/docs/pairing.md b/docs-site/content/docs/pairing.md index 9b30b037..815f0958 100644 --- a/docs-site/content/docs/pairing.md +++ b/docs-site/content/docs/pairing.md @@ -61,8 +61,8 @@ Then, on the client: - **[Native clients](/docs/clients) (Apple, Linux, Windows, Android):** select the host (or use *Pair with PIN…* from its menu) and enter the PIN the host displays. - **[Steam Deck](/docs/steam-deck) (the Decky plugin):** open Punktfunk from the Quick Access menu - and pick the host — an unpaired one's button reads **Pair & Stream**. Enter the PIN on the - 4-digit pad it opens. + and pick the host — an unpaired one opens a sheet offering **Request access** (no PIN: somebody + approves the Deck at the host) or **Use a PIN instead**, which opens the 4-digit pad. - **[Moonlight](/docs/moonlight):** choose **Pair**; Moonlight shows a 4-digit PIN, and you type that PIN into the console's **Moonlight (GameStream) pairing** card and press **Submit PIN**. (This direction is the reverse of the native flow, and arming doesn't apply to it.) diff --git a/docs-site/content/docs/profiles-and-links.md b/docs-site/content/docs/profiles-and-links.md index 153ea182..af8c8f07 100644 --- a/docs-site/content/docs/profiles-and-links.md +++ b/docs-site/content/docs/profiles-and-links.md @@ -11,8 +11,9 @@ Both live in the client apps — the Apple app, the Linux GTK client, the Window Android app. Neither exists in the host's [web console](/docs/web-console). The controller-driven surfaces are a half-exception: Apple TV, the Android app's console mode and -the Steam Deck console the Decky plugin launches all *use* the profile a host is bound to, but none -of them can create or edit one. Do that on a desktop or a phone first. +the Steam Deck console the Decky plugin launches all *use* the profile a host is bound to and can +pin one as its own card, but none of them can create or edit one. Do that on a desktop or a phone +first. The Decky panel itself only *shows* those pins, nested under their host as one-tap cards. ## What a profile is diff --git a/docs-site/content/docs/steam-deck.md b/docs-site/content/docs/steam-deck.md index fc1db17a..bf46700a 100644 --- a/docs-site/content/docs/steam-deck.md +++ b/docs-site/content/docs/steam-deck.md @@ -7,10 +7,12 @@ The **Decky plugin** adds a **Punktfunk** panel to the Steam Deck's Quick Access button), so you can find a host, pair, and start streaming **without leaving Gaming Mode**. It's the couch-friendly front end for the Steam Deck — built from real Steam UI, gamepad-navigable end to end. -Under the hood the plugin doesn't decode video itself: it discovers hosts, runs the PIN pairing, and -**launches the regular [Linux client](/docs/clients#linux-desktop-client-gtk4)** (usually the -`io.unom.Punktfunk` Flatpak) the way gamescope needs so it fullscreens correctly. So the Deck has two -ways to stream, and they share one client + one paired identity: +The plugin is a **launcher**, not a second client. It doesn't decode video, browse your library or +hold settings of its own — it starts the regular +[Linux client](/docs/clients#linux-desktop-client-gtk4) (usually the `io.unom.Punktfunk` Flatpak) +the way gamescope needs so it fullscreens correctly. Everything the panel doesn't do is one tap +away in that client's own gamepad UI. So the Deck has two ways to stream, and they share one +client + one paired identity: - **Gaming Mode** → the **Decky plugin** (this page). - **Desktop Mode** → run the [Flatpak](/docs/install-client#steam-deck) directly, like any Linux app. @@ -30,11 +32,13 @@ You need three things on the Deck: (Full options: [Install a Client → Steam Deck](/docs/install-client#steam-deck).) If you have no Flatpak but a native `punktfunk-client` — a sysext, a distro package, a nix profile, your own - build — the plugin launches that instead; with both installed the Flatpak wins, unless - `PF_DECKY_CLIENT=native` (or `flatpak`) is set in the plugin backend's environment. But - **pairing, Wake-on-LAN and the host game library still go through the Flatpak**, so install it - on the Deck even then. Both kinds share `~/.config/punktfunk`, so your identity, known hosts - and settings are the same either way. + build — the plugin uses that instead; with both installed the Flatpak wins, unless + `PF_DECKY_CLIENT=native` (or `flatpak`) is set in the plugin backend's environment. Both kinds + share `~/.config/punktfunk`, so your identity, known hosts and settings are the same either way. + + **The client must be v0.22.0 or newer.** The panel drives everything through the client's + headless `punktfunk` command, which shipped in that release. An older client says so in the + panel, with the update button that fixes it right there. 3. **A Punktfunk host** running on your LAN — see [Install the Host](/docs/install). The Deck finds it automatically over mDNS, so nothing to configure here. @@ -64,40 +68,68 @@ The **Punktfunk** panel appears in the Quick Access Menu right away — no Deck ## Use it -Open the **Punktfunk** panel from the Quick Access Menu, or **Open Punktfunk** for the full-screen -page (host list + stream settings). +Open the **Punktfunk** panel from the Quick Access Menu. It has one list — the hosts you can +stream — plus a door into the client's own gamepad UI for everything else. -- **Discover** — hosts on your network appear automatically (mDNS). Tap **Refresh** to rescan. A - lock icon means the host requires [pairing](/docs/pairing). -- **Add a host by hand** — if mDNS can't reach it (another subnet, a VPN), tap **+** on the Hosts - tab and enter its address; the port defaults to **9777**. Saved hosts can be renamed, re-pointed - at a new address, or forgotten from the same row. -- **Sleeping host?** Streaming sends a [Wake-on-LAN](/docs/wake-on-lan) packet first, and when one - actually went out the Deck waits far longer than usual for the host to answer, so a stream - survives a resume from sleep. Nothing to enable — it's a no-op until the plugin has learned that - host's MAC address, and the packet only lands if the host machine is armed to wake in its - BIOS and its network card. -- **Pair** — for a locked host, [arm pairing on the host](/docs/pairing) (its console or web - console shows a 4-digit PIN), then enter that PIN on the Deck's keypad. Pairing persists, so the - next connection is silent. -- **Stream** — pick a host and the stream launches fullscreen in Gaming Mode. The plugin drives a +- **Hosts** — hosts on your network appear automatically (mDNS), alongside the ones you've already + saved. A saved host is also probed directly, so a box reached over a VPN or Tailscale shows as + online even though it never advertises. Tap **Refresh** to rescan. The list sorts online hosts + first, then whichever you streamed most recently. A lock icon means the host still has to let + this Deck in. +- **Let a host in** — tapping a locked host opens a small sheet with two ways through: + - **Request access** — no PIN at all. See [Request access](#request-access) below. + - **Use a PIN instead** — [arm pairing on the host](/docs/pairing) (its console or web console + shows a 4-digit PIN), then enter it on the Deck's keypad. + + Either way the host is remembered, so the next connection is silent. +- **Stream** — tap a host and the stream launches fullscreen in Gaming Mode. The plugin drives a hidden Steam shortcut behind the scenes so gamescope focuses and fullscreens it. -- **Library entry** — a visible, branded **Punktfunk** app also appears in your Steam library. - Launching it opens the client's console home (host picker, pairing, settings), gamepad-navigable - — it does not resume a stream. If it ever disappears, the Quick Access Menu panel has a button to - put it back. -- **Games** — tap **Games** on a host row to browse that host's [library](/docs/game-library), and - **Pin** the ones you play. Pinned games show up on the full page *and* in the Quick Access Menu - as one-tap streams that launch straight into the game. -- **Settings** — resolution, refresh rate, **render scale**, bitrate, **video codec**, gamepad type, - **host compositor**, and mic, written to the client the plugin launches. Leave **Resolution** / - **Refresh** on *Native* to get the Deck's own mode, **Render scale** at 1× unless you want to - trade bandwidth for sharpness (>1×) or sharpness for bandwidth (<1×), and **Video codec** / - **Host compositor** on *Automatic* — that suits almost every host, so change them only when - you're troubleshooting. With **Gamepad type** on *Automatic* the Deck's built-in controller is - forwarded as a **Steam Deck** pad (paddles, both trackpads, gyro) — that needs Steam Input set to - **Off** for Punktfunk (game page → ⚙ → Controller Settings), else Steam keeps those controls and - only sticks + buttons reach the host. +- **Sleeping host?** Streaming sends a [Wake-on-LAN](/docs/wake-on-lan) packet and waits for the + host to actually come back before dialling, so a stream survives a resume from sleep. Nothing to + enable — it's a no-op until the client has learned that host's MAC address, and the packet only + lands if the host machine is armed to wake in its BIOS and its network card. +- **Pinned cards** — a host with pinned [settings profiles](/docs/client-settings) shows them + nested underneath it as `▸ `. Tapping one streams that host with that profile + applied — your "4K on the TV" and "battery saver" presets, one tap each. Pins are made in the + Punktfunk app (or any other client) and shared across all of them; the panel shows them, it + doesn't create them. +- **Open Punktfunk** — opens the client's console home: the host picker, adding a host by address, + pairing, browsing a host's [game library](/docs/game-library), and the **full settings screen**. + This is where resolution, bitrate, codec, audio, controllers and the stats overlay live. +- **Library entry** — a visible, branded **Punktfunk** app also appears in your Steam library, and + launching it opens that same console home — it does not resume a stream. If it ever disappears, + the Quick Access Menu panel has a button to put it back. + +> **Where did the plugin's settings tab go?** Into the app, at **Open Punktfunk → Settings** — the +> same rows over the same settings, gamepad-navigable, and one tap from the same panel. The plugin +> used to carry its own copy of that screen, which meant two places to change one setting and a +> copy that fell behind. There is now one. + +With **Controller type** on *Automatic* the Deck's built-in controller is forwarded as a **Steam +Deck** pad (paddles, both trackpads, gyro) — that needs Steam Input set to **Off** for Punktfunk +(game page → ⚙ → Controller Settings), else Steam keeps those controls and only sticks + buttons +reach the host. + +### Request access + +**Request access lets you in without typing a PIN**: instead of the host showing you a code, you +ask, and whoever is at the host approves the Deck in its [web console](/docs/web-console) or on +screen. + +Tap the host → **Request access**. The Deck says *"Approve this Deck in 's console — the +stream starts by itself"*, and the stream opens and waits. The moment somebody approves it, the +picture comes up — no going back to the panel, nothing else to tap. If nobody approves within +about three minutes, it gives up like any failed connection and you can try again or use a PIN. + +It's the better option when you're not the person sitting at the host, or when reading a PIN off +another screen is awkward. Two things to know: + +- The host must be **advertising on your network** for this to be offered. A host you added by + address (a VPN box, another subnet) has no advertised identity for the Deck to pin, so the sheet + offers the PIN path only and says so. That's a safety rule, not a limitation to work around: + pinning the advertised identity is what stops something else answering in the host's place while + the Deck waits. +- Once approved, the host shows as **paired** and every later stream connects silently. > **Steam Input off is a trade-off, not a free win.** The plugin installs a Steam Input layout > called **Punktfunk** and points its shortcuts at it, and that layout's whole job is making the @@ -115,9 +147,9 @@ input, so it is safe to hit by accident. The plugin **checks for updates itself** — no Decky store needed. It covers **both** the plugin *and* the streaming client (they version independently), so when either has a newer build the panel shows an -**Update** button (in the Quick Access Menu and on the full page). Tap it: the client updates in -place, and if the plugin itself changed it downloads, verifies, replaces itself, and reloads — all -without leaving Gaming Mode. +**Update** button at the top of the panel. Tap it: the client updates in place, and if the plugin +itself changed it downloads, verifies, replaces itself, and reloads — all without leaving Gaming +Mode. One exception: if your client isn't one the plugin can install for you (a sysext, a nix profile, a source build), the panel shows you the update **command** instead of a button — tap-to-install would @@ -139,13 +171,16 @@ The plugin check follows the [channel](/docs/channels) you installed from: a plu | Symptom | Fix | |---|---| -| The stream never starts, **Pair** reports `flatpak-not-found`, or **Games** says the client isn't installed | Install the client Flatpak in Desktop Mode (see [Before you start](#before-you-start)). | -| No hosts listed | Make sure the host is running and on the **same LAN**; the Deck needs `avahi` (shipped on SteamOS). Tap **Refresh**. | -| Pairing fails / "not armed" | The PIN is shown only after you **arm pairing on the host**. Arm it, then enter the PIN within the window. | -| Stream launches but doesn't focus | Start it from the panel (not by launching the Flatpak by hand) so Steam/gamescope focuses it. | -| The stream wedges — black, or won't close | Open the full page → **About** tab → **Force-stop**, then start it again. | -| The **Punktfunk** library entry disappeared | Quick Access Menu → **Recreate library shortcut**; it puts the entry back in place. | -| You want a clean slate | **About** tab → **Reset Punktfunk** — clears saved hosts, stream settings and pinned games on this Deck, and keeps your paired identity. | +| The panel says **"Update the Punktfunk client"** | The installed client predates v0.22.0 and has no `punktfunk` command to drive. Tap the update button in the same panel, or update it in Desktop Mode. | +| The stream never starts, or the panel can't reach the client | Install the client Flatpak in Desktop Mode (see [Before you start](#before-you-start)). | +| No hosts listed | Make sure the host is running and on the **same LAN**. Tap **Refresh**. For a host mDNS can't reach, add it by address in **Open Punktfunk → Add host**. | +| Pairing fails / "not armed" | The PIN is shown only after you **arm pairing on the host**. Arm it, then enter the PIN within the window — or use **Request access** instead, which needs no PIN. | +| **Request access** isn't offered | The host isn't advertising on this network, so there's no identity to pin. Use the PIN path. | +| A request-access stream sits there | That's it waiting — somebody has to approve the Deck on the host. It gives up after about three minutes. | +| Stream launches but doesn't focus | Start it from the panel (not by launching the client by hand) so Steam/gamescope focuses it. | +| The stream wedges — black, or won't close | Panel → **About** → **Force-stop**, then start it again. | +| The **Punktfunk** library entry disappeared | Panel → **Recreate library shortcut**; it puts the entry back in place. | +| You want a clean slate | **Open Punktfunk → Settings** for stream settings, or `punktfunk reset` in Desktop Mode to forget every saved host. Your paired identity is kept either way. | Nothing here matching? The problem is probably on the host side — start at [Troubleshooting](/docs/troubleshooting), which is organised by symptom (host not found, pairing diff --git a/docs-site/content/docs/support-matrix.md b/docs-site/content/docs/support-matrix.md index 9f8f31ec..124c52a2 100644 --- a/docs-site/content/docs/support-matrix.md +++ b/docs-site/content/docs/support-matrix.md @@ -354,7 +354,7 @@ macOS, iOS/iPadOS and tvOS. Android is one app, with Android TV being the same a | iPhone · iPad | ✅ | ✅ | ✅ ⁴ | ✅ | ✅ | ❌ ³ | | Apple TV | ⚠️ ⁵ | ✅ | ✅ ⁴ | ✅ | ✅ | ❌ ³ | | Android · Android TV | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ ³ | -| Decky (Steam Deck) | ❌ ⁶ | ❌ | ✅ ⁷ | ❌ | ✅ | ✅ ⁸ | +| Decky (Steam Deck) | ⚠️ ⁶ | ❌ | ⚠️ ⁷ | ❌ | ✅ | ✅ ⁸ | | `punktfunk` CLI | ✅ | ✅ ⁹ | ✅ | ✅ | ✅ | ❌ | | Moonlight | ❌ ¹⁰ | ❌ ¹⁰ | ✅ ¹¹ | ❓ ¹² | ❓ ¹² | ❓ ¹² | @@ -375,10 +375,12 @@ macOS, iOS/iPadOS and tvOS. Android is one app, with Android TV being the same a exists, so a fresh Apple TV has none. Of the settings a profile can carry, tvOS also drops the ones the platform has no input for: inverted scroll, modifier layout, variable refresh rate, mouse mode and touch mode. -6. The plugin writes flat values into the shared client settings; it has no profile surface. The - client it launches still honours whatever profile that settings file names. -7. Including pinned one-tap "Stream *game*" rows in the Quick Access Menu, which follow a host - across IP changes. Not subject to the desktop opt-in. +6. The panel *shows* the profiles a host has pinned, as nested one-tap cards, and streams with + them; it has no profile surface of its own. Pins are made in a client's own UI — including the + console home **Open Punktfunk** opens — and are shared, so every client shows the same cards. + Creating and editing a profile stays a desktop-app job. +7. Not in the panel: **Open Punktfunk** opens the client's console home, and a paired host's + library is one button from there. 8. Both the plugin itself and, where the install kind allows it, the client it launches. 9. The CLI parses and follows links; it does not register the URL scheme — the graphical apps do. 10. [Profiles and links](/docs/profiles-and-links) are Punktfunk-app concepts and do not exist on @@ -400,7 +402,8 @@ macOS, iOS/iPadOS and tvOS. Android is one app, with Android TV being the same a 1. Multiple controllers, each on its own stable slot, arriving and leaving independently. The pad **type** the host emulates is picked per pad; the pickers are not identical across apps — Linux, - Android and Decky offer six presets including Steam Deck, Windows and Apple offer five. + Android and the console home offer six presets including Steam Deck, Windows and Apple offer + five. 2. DualSense and DualShock 4 touchpad and motion are forwarded, and the host's adaptive-trigger and lightbar effects are replayed on a real DualSense. On the desktop clients any controller SDL exposes a gyro on forwards motion — a Switch Pro or the Steam Deck's own pad included — and the @@ -514,7 +517,7 @@ capability. | **Linux and Windows desktop clients** | Packaged and current. They are one codebase: the same session binary streams for both, and for the Decky plugin and the `punktfunk` CLI. | | **Apple client** (macOS · iOS · iPadOS · tvOS) | One universal build, distributed as a **TestFlight beta**; the Mac also has a notarized DMG. Feature-complete apart from the platform gaps named above (no microphone on tvOS, clipboard on macOS only). | | **Android client** (phone · TV) | Published on **Google Play** as a public listing for releases, with an invite-only Internal testing track for canary, plus a sideloadable APK. The same app in leanback mode is the TV client. | -| **Decky plugin** (Steam Deck) | Ships through install-from-URL rather than the Decky store, and keeps itself and the client it launches up to date. It launches the Linux client rather than streaming itself, and has no settings surface of its own beyond the flat values it writes into the shared client settings. | +| **Decky plugin** (Steam Deck) | Ships through install-from-URL rather than the Decky store, and keeps itself and the client it launches up to date. It is a launcher, not a second client: it starts the Linux client rather than streaming itself, and holds no settings, no library and no host editor of its own — its **Open Punktfunk** button hands all of that to the client's console home. | | **Web console** | The full management surface — dashboard and sessions, pairing, library, displays, plugins and the plugin store, logs, stats, settings, and host updates. It cannot yet run a speed test or set a bitrate; the client apps can. | | **Plugins** | Three first-party ones (ROM Manager, Playnite, VirtualHere) plus the SDK, installed from the console. See [Plugins](/docs/plugins). | | **`pf-webos`** (LG TV) | A community client in a separate repository. Nothing here can establish its state; ask that project. | diff --git a/docs-site/content/docs/wake-on-lan.md b/docs-site/content/docs/wake-on-lan.md index 5a8c09ae..65367143 100644 --- a/docs-site/content/docs/wake-on-lan.md +++ b/docs-site/content/docs/wake-on-lan.md @@ -74,9 +74,10 @@ saved host's own menu, and only appears when that host is offline *and* an addre | Android · Android TV | **Wake host** — waits, showing the "Waking…" screen | **Wake-on-LAN MAC** in **Edit host** | | Punktfunk Console (controller shell) | on an offline host with a known address, the confirm button reads **Wake & Connect** — it waits, then connects | not offered | -Punktfunk Console has no auto-wake setting of its own, and offers **Wake & Connect** whatever the -desktop app's setting says. In the Apple apps the same button appears when you drive them with a -controller, but there it does follow the auto-wake setting. +Punktfunk Console carries the row too — **Wake hosts automatically**, in the same settings list the +desktop apps write — but its **Wake & Connect** button is an explicit action and appears whatever +that row says. In the Apple apps the same button appears when you drive them with a controller, but +there it does follow the auto-wake setting. The Apple apps also publish a **Wake Host** action to Shortcuts, so an automation can wake a host without opening the app. On iPhone and iPad it has a ready-made phrase: *"Wake ⟨host⟩ with @@ -88,10 +89,14 @@ host list, and shows an explanation with a link to system settings if you declin ### On the Steam Deck -The [Decky plugin](/docs/steam-deck) has no wake button and no wake setting. It sends a wake through -the Flatpak client just before **every** stream launch, and it is a no-op until that client has -learned the host's address. When a packet really did go out, the plugin also stretches the stream's -connect budget to 75 seconds, so the connection survives the host resuming from sleep. +The [Decky plugin](/docs/steam-deck) has no wake button and no wake setting of its own. It starts +every stream through the client, so the wake is the client's, on exactly the terms above: a packet +the moment the host doesn't answer, re-sent every 6 seconds while the client watches for it once a +second, and the dial only when it really is back. It follows **Wake hosts automatically** in the +client's own settings — **Open Punktfunk → Settings** from the same panel — and is a no-op until the +client has learned that host's MAC address. (The plugin used to fire a packet itself and stretch the +connect budget to 75 seconds to cover the resume; a wait that watches for the host beats a fixed +budget, so that is gone.) ### From the command line