Merge pull request 'fix(bazzite): the shipped template pinned ATTACH, so Game Mode mirrored the box's screen instead of giving the client its own display' (#189) from worktree-no-attach-default into main
ci / bun-nix (push) Successful in 30s
ci / web (push) Successful in 1m9s
apple / swift (push) Successful in 1m44s
ci / rust-arm64 (push) Successful in 1m35s
windows-host / package (push) Failing after 1m31s
windows-host / canary-manifest (push) Skipped
windows-host / winget-source (push) Skipped
android / android (push) Failing after 3m35s
deb / build-publish-client-arm64 (push) Successful in 3m12s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 13s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 9s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
ci / docs-site (push) Successful in 6m7s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 3m2s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 20s
apple / screenshots (push) Successful in 6m7s
deb / build-publish-host (push) Successful in 6m41s
docker / builders-arm64cross (push) Successful in 11s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3m9s
arch / build-publish (push) Successful in 11m45s
deb / build-publish (push) Successful in 12m33s
docker / deploy-docs (push) Failing after 7m31s
ci / rust (push) Successful in 18m21s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m26s
ci / bun-nix (push) Successful in 30s
ci / web (push) Successful in 1m9s
apple / swift (push) Successful in 1m44s
ci / rust-arm64 (push) Successful in 1m35s
windows-host / package (push) Failing after 1m31s
windows-host / canary-manifest (push) Skipped
windows-host / winget-source (push) Skipped
android / android (push) Failing after 3m35s
deb / build-publish-client-arm64 (push) Successful in 3m12s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 13s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 9s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
ci / docs-site (push) Successful in 6m7s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 3m2s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 20s
apple / screenshots (push) Successful in 6m7s
deb / build-publish-host (push) Successful in 6m41s
docker / builders-arm64cross (push) Successful in 11s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3m9s
arch / build-publish (push) Successful in 11m45s
deb / build-publish (push) Successful in 12m33s
docker / deploy-docs (push) Failing after 7m31s
ci / rust (push) Successful in 18m21s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m26s
Reviewed-on: #189
This commit was merged in pull request #189.
This commit is contained in:
@@ -111,9 +111,15 @@ struct OperatorGamescope {
|
||||
#[cfg(target_os = "linux")]
|
||||
fn operator_gamescope() -> &'static OperatorGamescope {
|
||||
OPERATOR_GAMESCOPE.get_or_init(|| {
|
||||
// Explicit-off grammar, NOT a presence test. These two used to be `var_os(..).is_some()`,
|
||||
// which read `PUNKTFUNK_GAMESCOPE_ATTACH=0` as ATTACH ON — the exact opposite of what the
|
||||
// line says, and of every other knob on this host (`env_on` is shared for that reason).
|
||||
// Anyone turning a shipped `=1` off does it the way the rest of the file works, and the
|
||||
// rung this feeds outranks a dedicated game session, so a silent inversion here costs the
|
||||
// client its own display for the whole stream.
|
||||
let ov = with_env_lock(|| OperatorGamescope {
|
||||
managed: std::env::var_os("PUNKTFUNK_GAMESCOPE_MANAGED").is_some(),
|
||||
attach: std::env::var_os("PUNKTFUNK_GAMESCOPE_ATTACH").is_some(),
|
||||
managed: pf_host_config::env_on("PUNKTFUNK_GAMESCOPE_MANAGED").unwrap_or(false),
|
||||
attach: pf_host_config::env_on("PUNKTFUNK_GAMESCOPE_ATTACH").unwrap_or(false),
|
||||
node: std::env::var("PUNKTFUNK_GAMESCOPE_NODE")
|
||||
.ok()
|
||||
.filter(|v| !v.is_empty()),
|
||||
|
||||
@@ -161,21 +161,27 @@ The only settings that matter (GPU zero-copy is on by default):
|
||||
```sh
|
||||
PUNKTFUNK_VIDEO_SOURCE=virtual
|
||||
# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU.
|
||||
PUNKTFUNK_GAMESCOPE_ATTACH=1 # Gaming Mode = attach to the box's own session — SDR, and no cursor (see below)
|
||||
```
|
||||
|
||||
### Gaming Mode: attach vs managed
|
||||
|
||||
For Gaming Mode there are two models (pick one; the shipped default is **attach**):
|
||||
For Gaming Mode there are two models. The template forces **neither** — the host picks per connect,
|
||||
and on Bazzite (which ships `gamescope-session-plus`) that is **managed**:
|
||||
|
||||
- **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`, the template's default) — the **box** owns its
|
||||
gamescope session on its own display, and the host attaches to whatever's live without ever
|
||||
tearing it down (on a headless box, a box-owned autologin session is restarted at the client's
|
||||
resolution on a mismatch; with a display connected it streams at the box's own mode). Switching
|
||||
Desktop ↔ Game is rock-solid.
|
||||
- **Managed** (`PUNKTFUNK_GAMESCOPE_MANAGED=1`, and remove the attach line) — the host takes the
|
||||
box's gamescope over and relaunches it **headless** at the *client's* exact resolution and
|
||||
refresh — Game Mode on the virtual screen — restoring the box on idle.
|
||||
- **Managed** (what you get by default here) — the host takes the box's gamescope over and
|
||||
relaunches it **headless** at the *client's* exact resolution and refresh — Game Mode on the
|
||||
virtual screen — restoring the box on idle. This is the model that gives the client a display of
|
||||
its **own**, and the only one under which a game launched from a client's library gets a
|
||||
dedicated session. It needs the [`punktfunk` group](#allow-controller-input): the takeover stops
|
||||
the display manager for the length of the stream, and without that grant it cannot.
|
||||
- **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`) — the **box** owns its gamescope session on its own
|
||||
display, and the host attaches to whatever's live without ever tearing it down (on a headless
|
||||
box, a box-owned autologin session is restarted at the client's resolution on a mismatch; with a
|
||||
display connected it streams at the box's own mode). Switching Desktop ↔ Game is rock-solid, and
|
||||
the cost is that a box with a screen attached serves the client a **mirror** of that screen
|
||||
rather than its own display. Setting it also outranks a dedicated game session.
|
||||
|
||||
`=0` turns the attach override off, the same as removing the line.
|
||||
|
||||
Full treatment: [Steam / gamescope → How the host gets a
|
||||
gamescope](/docs/gamescope#how-the-host-gets-a-gamescope).
|
||||
@@ -227,14 +233,17 @@ These apply to the **Gaming Mode (gamescope)** path; the KDE Desktop path is una
|
||||
headless capture can deadlock; between the two, capture works but the Steam overlay (Shift+Tab /
|
||||
the Quick Access Menu) is never painted into the captured node. Bazzite's current gamescope is
|
||||
past both; this only bites if you've pinned an old one.
|
||||
- **The template pins attach, and that costs you both the cursor and HDR.** The sysext ships the
|
||||
`punktfunk-gamescope` build, but it only reaches a session the host starts itself — and the
|
||||
`host.env` template above sets `PUNKTFUNK_GAMESCOPE_ATTACH=1`, where the live session is
|
||||
Bazzite's own stock gamescope. Comment that line out and let the managed default take over: you
|
||||
get the compositor-drawn pointer and real HDR. To stay on attach instead, set
|
||||
`PUNKTFUNK_GAMESCOPE_HDR=0` and `PUNKTFUNK_GAMESCOPE_BIN=/usr/bin/gamescope`. Why each half
|
||||
breaks: [gamescope → Known limits](/docs/gamescope#known-limits) for the cursor,
|
||||
- **Forcing attach costs you the cursor, HDR and your own display.** The sysext ships the
|
||||
`punktfunk-gamescope` build, but it only reaches a session the host starts itself — under
|
||||
`PUNKTFUNK_GAMESCOPE_ATTACH=1` the live session is Bazzite's own stock gamescope. The managed
|
||||
default gets you the compositor-drawn pointer, real HDR and a display of the client's own. If you
|
||||
deliberately stay on attach, also set `PUNKTFUNK_GAMESCOPE_HDR=0` and
|
||||
`PUNKTFUNK_GAMESCOPE_BIN=/usr/bin/gamescope`. Why each half breaks:
|
||||
[gamescope → Known limits](/docs/gamescope#known-limits) for the cursor,
|
||||
[HDR → Linux + gamescope](/docs/hdr#linux--gamescope) for the failed connect.
|
||||
⚠ Older templates set `PUNKTFUNK_GAMESCOPE_ATTACH=1` for you — if you copied one, delete that
|
||||
line from `~/.config/punktfunk/host.env`, because an upgrade never rewrites a file you already
|
||||
have.
|
||||
|
||||
Those are the two that bite on Bazzite. The full set — touch, mouse modes, the clipboard — is on
|
||||
[gamescope → Known limits](/docs/gamescope#known-limits).
|
||||
|
||||
@@ -87,7 +87,7 @@ the full picture (and [Bazzite](/docs/bazzite) for that distro's specifics).
|
||||
|
||||
| Setting | Values | Meaning |
|
||||
|---|---|---|
|
||||
| `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | **Attach** model: the box owns its gamescope session on its own display (you switch Gaming ↔ Desktop with the Steam UI); the host just captures whatever's live and never tears it down. On a **headless** box the box-owned autologin session is restarted at the client's resolution on a mismatch; a box driving a physical display, and any foreign/bare gamescope, streams at its own mode. |
|
||||
| `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` · `0` *(unset = auto)* | **Attach** model: the box owns its gamescope session on its own display (you switch Gaming ↔ Desktop with the Steam UI); the host just captures whatever's live and never tears it down. On a **headless** box the box-owned autologin session is restarted at the client's resolution on a mismatch; a box driving a physical display, and any foreign/bare gamescope, streams at its own mode — i.e. the client is served a **mirror**, not a display of its own. Setting this also outranks a dedicated game session. No template ships it set; `=0` is the same as leaving it out. |
|
||||
| `PUNKTFUNK_GAMESCOPE_MANAGED` | `1` | **Managed** model (the default where session infra is detected): the host takes the box's gamescope over and relaunches it **headless** at the *client's* exact resolution — Game Mode on the virtual screen — restoring the box on idle. |
|
||||
| `PUNKTFUNK_GAMESCOPE_SESSION` | `steam` | The host owns a `gamescope-session-plus` (Steam) session at the client's mode (headless appliance; no physical session running). |
|
||||
| `PUNKTFUNK_GAMESCOPE_NODE` | `auto` · node id | Discover + capture a **running** gamescope's PipeWire node at a fixed mode. Do **not** combine with `SESSION`. |
|
||||
|
||||
@@ -18,8 +18,8 @@ from the install guide for your OS: [Bazzite](/docs/bazzite) or [SteamOS (Host)]
|
||||
## How the host gets a gamescope
|
||||
|
||||
There are three models; the host picks one per session, and you rarely have to. With **nothing
|
||||
set**, a box that has gamescope session infrastructure (Bazzite, SteamOS, Nobara) gets **managed**;
|
||||
the [Bazzite template](/docs/bazzite) ships with **attach** chosen instead.
|
||||
set** — which is what every shipped template does — a box that has gamescope session infrastructure
|
||||
(Bazzite, SteamOS, Nobara) gets **managed**.
|
||||
|
||||
- **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`) — the **box** owns its gamescope session and decides
|
||||
Gaming vs Desktop via the normal Steam UI. Game Mode stays on the box's own (physical) display;
|
||||
@@ -208,8 +208,8 @@ you and the two ways out.
|
||||
|
||||
The cursor is the half this page owns. The host leaves the pointer to the compositor whenever the
|
||||
installed build can paint it (below) — so on an attached session, which can't, nothing draws it and
|
||||
the stream has no cursor at all. Commenting
|
||||
`PUNKTFUNK_GAMESCOPE_ATTACH=1` out of the [Bazzite template](/docs/bazzite) and letting the managed
|
||||
the stream has no cursor at all. Removing
|
||||
`PUNKTFUNK_GAMESCOPE_ATTACH=1` from your `host.env` and letting the managed
|
||||
default take over fixes that along with HDR. To stay on attach, point `PUNKTFUNK_GAMESCOPE_BIN` at
|
||||
your distro's own `gamescope` (`/usr/bin/gamescope`) instead: the host goes back to compositing the
|
||||
cursor itself, and — since the HDR answer comes from the same binary — stops attempting HDR too.
|
||||
|
||||
@@ -76,12 +76,13 @@ available, and offers it. The attached session can't answer that negotiation, so
|
||||
with no picture, the host latches an SDR downgrade for the rest of its life, and the next connect
|
||||
streams — in SDR.
|
||||
|
||||
That is exactly what the [Bazzite](/docs/bazzite) template ships: it pins attach *and* the sysext
|
||||
installs `punktfunk-gamescope`. Either comment `PUNKTFUNK_GAMESCOPE_ATTACH=1` out and let the managed
|
||||
default take over (you get HDR and the compositor-drawn cursor), or stay on attach and set
|
||||
`PUNKTFUNK_GAMESCOPE_HDR=0` so the failed attempt never happens. Staying on attach also leaves the
|
||||
stream with no cursor; [HDR on gamescope](/docs/gamescope#hdr-on-gamescope) has the fix for that
|
||||
half.
|
||||
That combination bites on [Bazzite](/docs/bazzite), where the sysext installs `punktfunk-gamescope`
|
||||
alongside a stock session gamescope. No template pins attach any more, so the managed default gets
|
||||
you HDR and the compositor-drawn cursor — but an older template did, and an upgrade never rewrites a
|
||||
`host.env` you already have, so check yours for `PUNKTFUNK_GAMESCOPE_ATTACH=1` and delete the line.
|
||||
If you deliberately stay on attach, set `PUNKTFUNK_GAMESCOPE_HDR=0` so the failed attempt never
|
||||
happens. Staying on attach also leaves the stream with no cursor;
|
||||
[HDR on gamescope](/docs/gamescope#hdr-on-gamescope) has the fix for that half.
|
||||
|
||||
SDR content rides the same PQ container — the desktop, the Steam overlay, an SDR game — mapped in at
|
||||
`PUNKTFUNK_GAMESCOPE_SDR_NITS` (gamescope's own default is 400). That is the knob when white looks
|
||||
|
||||
@@ -261,9 +261,6 @@ PUNKTFUNK_VIDEO_SOURCE=virtual
|
||||
# PUNKTFUNK_ZEROCOPY=0
|
||||
|
||||
#RUST_LOG=info
|
||||
|
||||
# Gaming Mode = ATTACH: the box owns its gamescope session; the host captures + follows it.
|
||||
PUNKTFUNK_GAMESCOPE_ATTACH=1
|
||||
```
|
||||
|
||||
**What each knob means and why these are the Bazzite defaults:**
|
||||
@@ -272,7 +269,7 @@ PUNKTFUNK_GAMESCOPE_ATTACH=1
|
||||
|---|---|---|
|
||||
| *(no compositor / no anchors)* | — | The host **auto-detects** the live session per connect (Gaming Mode gamescope vs the KDE desktop) and follows switches mid-stream; a `systemctl --user` service inherits the right `XDG_RUNTIME_DIR` and the host derives the bus itself. Pinning `PUNKTFUNK_COMPOSITOR` or hardcoding uid-1000 anchors only breaks this — leave them out. |
|
||||
| `PUNKTFUNK_VIDEO_SOURCE` | `virtual` | Create a per-client virtual output at the client's exact WxH@Hz (the flagship "native resolution, no scaling" mode), vs. `portal` which captures an existing monitor. |
|
||||
| `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | Gaming Mode model: the **box** owns its gamescope session; the host attaches to whatever's live and never tears it down. Swap for `PUNKTFUNK_GAMESCOPE_MANAGED=1` to have the host relaunch the gaming session headless at the **client's** exact mode instead (see the template's comments). |
|
||||
| `PUNKTFUNK_GAMESCOPE_ATTACH` | *(unset)* | Gaming Mode model. Left unset the host picks one per connect, which on Bazzite is **managed**: it relaunches the gaming session headless at the **client's** exact mode, so the client gets a display of its own. Set `=1` to force **attach** instead — the box keeps its session and the client is served a mirror of the box's own screen at the box's mode (see the template's comments). |
|
||||
| `PUNKTFUNK_ZEROCOPY` | `on` *(default)* | GPU zero-copy capture (dmabuf → CUDA → NVENC), on by default. Falls back to CPU automatically if unavailable; set `0` to force the CPU path. |
|
||||
| `RUST_LOG` | (commented) | Uncomment `RUST_LOG=info` for verbose logs while debugging. |
|
||||
|
||||
|
||||
+22
-12
@@ -20,19 +20,29 @@ PUNKTFUNK_VIDEO_SOURCE=virtual
|
||||
# PUNKTFUNK_COMPOSITOR=kwin|mutter|wlroots|gamescope
|
||||
# PUNKTFUNK_INPUT_BACKEND=libei|wlr|gamescope|uinput
|
||||
#
|
||||
# GAME MODE = ATTACH (the box owns its session; the host follows). The box decides whether it's in
|
||||
# Steam Gaming Mode or a Desktop — you switch with the normal Steam UI / "Switch to Desktop". The
|
||||
# host just ATTACHES to whatever's live and captures it; it never tears the session down. So
|
||||
# switching Desktop<->Game is rock-solid, and when you disconnect the box STAYS in its current
|
||||
# mode — reconnecting drops you right back where you were. On a resolution mismatch the host
|
||||
# restarts the box's own game-mode session at the CLIENT's resolution (a foreign/bare gamescope
|
||||
# instead streams at its own mode).
|
||||
PUNKTFUNK_GAMESCOPE_ATTACH=1
|
||||
# GAME MODE: nothing here forces a model — the host picks one per connect. On this box (Bazzite
|
||||
# ships gamescope-session-plus) that is the MANAGED model: on connect the host takes the box's
|
||||
# gaming session over and relaunches it headless at the CLIENT's exact resolution, then restores
|
||||
# the box's own session on a debounced idle. That is what gives the client a display of its OWN
|
||||
# instead of a copy of the TV, and it is also what lets a game launched from a client's library get
|
||||
# a dedicated session to itself.
|
||||
#
|
||||
# Opt OUT to the MANAGED model instead (host tears the box's gamescope down on connect and launches
|
||||
# its OWN at the CLIENT's exact resolution; restores on a debounced idle). Client-mode-following, but
|
||||
# it does not coexist with a box-owned game-mode session — pick one:
|
||||
# PUNKTFUNK_GAMESCOPE_MANAGED=1 # (and remove PUNKTFUNK_GAMESCOPE_ATTACH above)
|
||||
# ⚠ The managed takeover has to stop the display manager for the length of the stream, and that is
|
||||
# privileged: it works for members of the `punktfunk` group and nobody else. Join it once —
|
||||
# `sudo usermod -aG punktfunk "$USER"`, then log out and back in. Skip it and the takeover cannot
|
||||
# stop SDDM, which relogin-loops against it for the whole stream and can starve the game. The host
|
||||
# checks at startup and says so in its log.
|
||||
#
|
||||
# Opt IN to the ATTACH model if you would rather the BOX keep ownership: the host captures whatever
|
||||
# gamescope is live and never tears it down, so Desktop<->Game switching is rock-solid and the box
|
||||
# stays in its current mode when you disconnect. The cost is that a box driving a physical display
|
||||
# streams at the BOX's own mode, MIRRORED — the client does not get its own display — and this
|
||||
# override outranks a dedicated game session too. Remove the line to go back to auto (`=0` also
|
||||
# turns it off; any other value is on):
|
||||
# PUNKTFUNK_GAMESCOPE_ATTACH=1
|
||||
#
|
||||
# Force the MANAGED model on a box where the auto-detection finds no session infrastructure:
|
||||
# PUNKTFUNK_GAMESCOPE_MANAGED=1
|
||||
#
|
||||
# Follow a Gaming<->Desktop switch MID-STREAM (rebuild the backend in place, no reconnect). This is
|
||||
# ON BY DEFAULT on Bazzite/SteamOS (the host detects the platform); set =0 to disable it:
|
||||
|
||||
Reference in New Issue
Block a user