feat(host): KWin virtual output primary + settle portal env on switch
android / android (push) Failing after 22s
ci / web (push) Failing after 14s
ci / docs-site (push) Failing after 0s
ci / bench (push) Failing after 0s
deb / build-publish (push) Failing after 1s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Failing after 1s
decky / build-publish (push) Failing after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Failing after 1s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Failing after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Failing after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
docker / deploy-docs (push) Has been skipped
flatpak / build-publish (push) Failing after 3s
apple / swift (push) Successful in 54s
ci / rust (push) Failing after 1m42s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 52s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m11s
android / android (push) Failing after 22s
ci / web (push) Failing after 14s
ci / docs-site (push) Failing after 0s
ci / bench (push) Failing after 0s
deb / build-publish (push) Failing after 1s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Failing after 1s
decky / build-publish (push) Failing after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Failing after 1s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Failing after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Failing after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
docker / deploy-docs (push) Has been skipped
flatpak / build-publish (push) Failing after 3s
apple / swift (push) Successful in 54s
ci / rust (push) Failing after 1m42s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Failing after 52s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m11s
Two parked follow-ups from the session-aware host work: #3 — KWin/Mutter virtual output not set primary. The auto-detected desktop path *is* "stream this desktop", but the per-session virtual output wasn't promoted to primary, so KDE/GNOME panels + windows stayed on an unstreamed real output and the streamed screen showed only wallpaper. apply_session_env now defaults PUNKTFUNK_KWIN_VIRTUAL_PRIMARY / PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY on for the auto path (explicit config still wins), so the streamed output becomes the sole desktop. #2 — input flaky after a mid-stream Gaming->Desktop switch. The xdg portal (D-Bus-activated) and the systemd --user env still pointed at the old session, so the host's RemoteDesktop portal opened against a half-stale env: it accepted events but they didn't reach the compositor until a reconnect. New vdisplay::settle_desktop_portal() pushes the live session env into the systemd/D-Bus activation environment and (for KWin) restarts the portal so it re-reads it, mirroring a fresh desktop login (and the existing wlroots portal restart). Called from the mid-stream switch rebuild slot before the injector reopens. GNOME uses Mutter's direct EIS, so it only gets the env push. Compiles, clippy/fmt clean, 78 host tests pass. Live validation on the Bazzite box next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2014,6 +2014,15 @@ fn virtual_stream(
|
||||
env: sw.env,
|
||||
});
|
||||
crate::vdisplay::apply_input_env(sw.compositor);
|
||||
// Switching INTO a desktop mid-stream: the xdg portal / systemd-user env may still
|
||||
// point at the old session, so input would silently not land until a reconnect.
|
||||
// Settle it (env push + KWin portal restart) before the injector reopens against it.
|
||||
if matches!(
|
||||
sw.compositor,
|
||||
crate::vdisplay::Compositor::Kwin | crate::vdisplay::Compositor::Mutter
|
||||
) {
|
||||
crate::vdisplay::settle_desktop_portal(sw.compositor);
|
||||
}
|
||||
// Build the new backend's pipeline BEFORE dropping the old one (retry absorbs the
|
||||
// brief compositor-coexistence race during a switch); on failure keep the old.
|
||||
let rebuilt =
|
||||
|
||||
@@ -367,10 +367,72 @@ pub fn apply_session_env(active: &ActiveSession) {
|
||||
if active.kind == ActiveKind::DesktopGnome {
|
||||
std::env::set_var("PUNKTFUNK_FORCE_SHM", "1");
|
||||
}
|
||||
// Stream the desktop as the SOLE output: promote the per-session virtual output to PRIMARY so
|
||||
// the panels + windows land on the streamed surface, not an unstreamed real output (the
|
||||
// auto-detected desktop path *is* "stream this desktop"). Default-on for the auto path; an
|
||||
// explicit `PUNKTFUNK_{KWIN,MUTTER}_VIRTUAL_PRIMARY` still wins.
|
||||
match active.kind {
|
||||
ActiveKind::DesktopKde if std::env::var_os("PUNKTFUNK_KWIN_VIRTUAL_PRIMARY").is_none() => {
|
||||
std::env::set_var("PUNKTFUNK_KWIN_VIRTUAL_PRIMARY", "1");
|
||||
}
|
||||
ActiveKind::DesktopGnome
|
||||
if std::env::var_os("PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY").is_none() =>
|
||||
{
|
||||
std::env::set_var("PUNKTFUNK_MUTTER_VIRTUAL_PRIMARY", "1");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn apply_session_env(_active: &ActiveSession) {}
|
||||
|
||||
/// On a **mid-stream** switch to a desktop, the xdg-desktop-portal (D-Bus-activated) and the systemd
|
||||
/// `--user` environment can still point at the OLD session, so the host's RemoteDesktop portal opens
|
||||
/// against a half-stale env — it accepts events but they don't reach the compositor until a
|
||||
/// reconnect. Push the live session env into the systemd/D-Bus activation environment and (for KWin,
|
||||
/// whose input rides the xdg RemoteDesktop portal) restart the portal so it re-reads it — the same
|
||||
/// settling a fresh desktop login does. Best-effort; mirrors the wlroots portal restart. GNOME uses
|
||||
/// Mutter's *direct* EIS (no xdg portal), so it only needs the env push.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn settle_desktop_portal(chosen: Compositor) {
|
||||
const VARS: &[&str] = &[
|
||||
"WAYLAND_DISPLAY",
|
||||
"XDG_CURRENT_DESKTOP",
|
||||
"DBUS_SESSION_BUS_ADDRESS",
|
||||
"XDG_RUNTIME_DIR",
|
||||
];
|
||||
// Push our (correct) env into the systemd --user manager + the D-Bus activation environment so a
|
||||
// re-activated portal/backend inherits the live session.
|
||||
let _ = std::process::Command::new("systemctl")
|
||||
.args(["--user", "import-environment"])
|
||||
.args(VARS)
|
||||
.status();
|
||||
let _ = std::process::Command::new("dbus-update-activation-environment")
|
||||
.arg("--systemd")
|
||||
.args(VARS)
|
||||
.status();
|
||||
// KWin input goes through the xdg RemoteDesktop portal; the frontend routes RemoteDesktop to a
|
||||
// backend by its OWN startup XDG_CURRENT_DESKTOP, so restart it (+ the KDE backend) to re-read
|
||||
// the now-live session, then let it settle before the injector reopens against it.
|
||||
if chosen == Compositor::Kwin {
|
||||
let _ = std::process::Command::new("systemctl")
|
||||
.args([
|
||||
"--user",
|
||||
"try-restart",
|
||||
"xdg-desktop-portal-kde.service",
|
||||
"xdg-desktop-portal.service",
|
||||
])
|
||||
.status();
|
||||
std::thread::sleep(std::time::Duration::from_millis(600));
|
||||
}
|
||||
tracing::info!(
|
||||
compositor = chosen.id(),
|
||||
"settled desktop portal env for the switched-to session"
|
||||
);
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn settle_desktop_portal(_chosen: Compositor) {}
|
||||
|
||||
/// Route input to match the chosen video backend (they must not diverge), via the highest-priority
|
||||
/// `PUNKTFUNK_INPUT_BACKEND` knob the injector honors. For gamescope, the **default is a managed
|
||||
/// session at the client's mode** (tears the TV's autologin down on connect; restored on a debounced
|
||||
|
||||
Reference in New Issue
Block a user