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,
|
env: sw.env,
|
||||||
});
|
});
|
||||||
crate::vdisplay::apply_input_env(sw.compositor);
|
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
|
// 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.
|
// brief compositor-coexistence race during a switch); on failure keep the old.
|
||||||
let rebuilt =
|
let rebuilt =
|
||||||
|
|||||||
@@ -367,10 +367,72 @@ pub fn apply_session_env(active: &ActiveSession) {
|
|||||||
if active.kind == ActiveKind::DesktopGnome {
|
if active.kind == ActiveKind::DesktopGnome {
|
||||||
std::env::set_var("PUNKTFUNK_FORCE_SHM", "1");
|
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"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub fn apply_session_env(_active: &ActiveSession) {}
|
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
|
/// 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
|
/// `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
|
/// 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