From 0bb57087694c59f377c0eb3b1384d710ba38ba41 Mon Sep 17 00:00:00 2001 From: rickpeters Date: Thu, 15 Oct 2026 20:29:13 +0200 Subject: [PATCH 1/5] GNOME theme: Restart GNOME button after applying the theme (relogin through GDM when set up, else a logout) --- CHANGELOG.md | 2 ++ lib/backend.sh | 5 ++++- lib/gnome-theme.sh | 18 ++++++++++++++++++ ui/qml/AppState.qml | 6 ++++-- ui/qml/DoneScreen.qml | 6 ++++-- ui/steamify-ui | 10 ++++++++++ 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ce0269..3aa68da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ one per merged pull request. - `2ca288d` **fix: the NVIDIA gaming mode setup no longer fails with "the patched gamescope doesn't start" on a gamescope that works: the smoke test judged gamescope's exit status through a pipe into `grep -q` under `pipefail`, so gamescope's crash on the closed pipe (or a hung shutdown) counted as a failed test, intermittently, for the CPU-specific and the generic build alike (seen on an RTX 5080). The test now reads gamescope's output from a file and kills a hung shutdown (`timeout -k`)** - `645b331` **feat: the GNOME theme installs GNOME Settings with the "GNOME Desktop" panel (dock, desktop icons, tiling) as a prebuilt package, downloaded from the `settings` release of steamify-gnome-theme (built by GitHub Actions on current Arch, rebuilt weekly) and checked against its `.sha256`; a failure keeps the stock Settings. Turning the theme off puts the stock `gnome-control-center` back. Installs with the GNOME theme on re-apply it (feature version 2.13.0)** +- **feat: after the GNOME theme is applied in a GNOME session, the app's done screen has "Restart GNOME" (GNOME Shell on Wayland can't restart in place, and the dock, extensions and Settings panel only show after a new login): with GDM autologin (the SteamOS conversion) Steamify's relogin helper logs the user straight back into the session they were in (without changing the session gaming mode's boot goes back to), else it is a plain logout (`gnome-session-quit`)** + ## 2.12.0 - 2026-10-13 - **feat: setting up the SteamOS conversion on NVIDIA first checks that nothing blocks it (a pacman lock, less than 1.5 GB free), and a failed setup is remembered: a desktop notification now and a reminder in the menu until a setup works** diff --git a/lib/backend.sh b/lib/backend.sh index efd6665..d4d771d 100644 --- a/lib/backend.sh +++ b/lib/backend.sh @@ -195,7 +195,10 @@ backend_apply() { feature_record_unticked os_version_refresh [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true - backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false)" + # (Only when the theme itself worked: a failed one has nothing to log in again for.) + local relogin=false + [[ " ${failed[*]:-} " != *" gnometheme "* ]] && gnometheme_relogin_needed "${TO_DISABLE[@]}" "${TO_ENABLE[@]}" && relogin=true + backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false),\"relogin\":$relogin" } backend_main() { diff --git a/lib/gnome-theme.sh b/lib/gnome-theme.sh index 474f71d..4ea8d0e 100644 --- a/lib/gnome-theme.sh +++ b/lib/gnome-theme.sh @@ -111,6 +111,22 @@ gnometheme_shell() { done } +gnometheme_relogin_needed() { + # A GNOME session is running and this run changed the theme: GNOME Shell on Wayland can't be restarted in place, so + # the new extensions, the dock and the Settings panel show after the user logs in again. + [[ " $* " == *" gnometheme "* ]] && pgrep -u "$(id -u)" -x gnome-shell >/dev/null 2>&1 +} + +gnometheme_relogin() { + # End the GNOME session. With GDM autologin set up by the conversion, steamify-gdm-relogin logs the user straight back + # into the session they were in (GDM takes it from AccountsService; the session is not changed here, unlike + # steamos-session-select, which would make GNOME the one gaming mode's boot goes back to). Without that helper there + # is no autologin: a plain logout, and the user logs in again. + local helper=/usr/local/libexec/steamify-gdm-relogin + if [[ -x "$helper" ]] && sudo -n "$helper"; then return 0; fi + gnome-session-quit --logout --no-prompt +} + gnometheme_settings_install() { # The prebuilt GNOME Settings with the Desktop panel, replacing the stock one. Never fatal: the theme works without the # panel, so a failed download or install keeps the stock Settings and says so. @@ -172,6 +188,8 @@ gnometheme_enable() { gnometheme_shell gnometheme_settings_install ok "SteamOS theme for GNOME on (apps pick up the colors when they start again)." + # (GNOME Shell on Wayland can't restart in place: the app offers "Restart GNOME", which is this.) + gnometheme_relogin_needed gnometheme && info "Log out and back in for the dock, the extensions and the Settings panel to show." } gnometheme_disable() { diff --git a/ui/qml/AppState.qml b/ui/qml/AppState.qml index a57a51b..0a745c2 100644 --- a/ui/qml/AppState.qml +++ b/ui/qml/AppState.qml @@ -18,6 +18,7 @@ Item { property var steps: ({}) // id -> "wait"|"run"|"ok"|"fail" property var failed: [] property bool restartNeeded: false + property bool reloginNeeded: false // GNOME loads the new theme's extensions at the next login // Review & apply only when something would change; "Re-apply what's on" // is for when nothing did. readonly property bool canApply: computePlan().length > 0 @@ -123,7 +124,7 @@ Item { } // --- Password and apply --- - function resetRun() { steps = {}; failed = []; restartNeeded = false; runError = ""; doneCount = 0; logModel.clear(); } + function resetRun() { steps = {}; failed = []; restartNeeded = false; reloginNeeded = false; runError = ""; doneCount = 0; logModel.clear(); } function startApply(password) { resetRun(); var s = {}; @@ -203,7 +204,7 @@ Item { // A successful BIOS check ends with bios-ready instead, keeping // the password for the flash. app.sessionPassword = ""; - app.failed = ev.failed || []; app.restartNeeded = !!ev.restart; + app.failed = ev.failed || []; app.restartNeeded = !!ev.restart; app.reloginNeeded = !!ev.relogin; app.runError = app.errorText(ev.error); app.screen = "done"; } @@ -249,6 +250,7 @@ Item { else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; } } else if (screen === "done") { if (a === "accept" && restartNeeded) backend.restart(); + else if (a === "accept" && reloginNeeded) backend.restartGnome(); else if (a === "back" || a === "accept") backToMenu(); } } diff --git a/ui/qml/DoneScreen.qml b/ui/qml/DoneScreen.qml index 6f3aeac..2e2cbd0 100644 --- a/ui/qml/DoneScreen.qml +++ b/ui/qml/DoneScreen.qml @@ -9,7 +9,8 @@ Item { readonly property bool problem: app.failed.length > 0 || app.runError !== "" readonly property string message: { if (app.runError) return app.runError; - if (!app.biosRun) return app.plan.length + (app.plan.length === 1 ? " change" : " changes") + " applied." + (app.restartNeeded ? " Some take effect after a restart." : ""); + if (!app.biosRun) return app.plan.length + (app.plan.length === 1 ? " change" : " changes") + " applied." + (app.restartNeeded ? " Some take effect after a restart." : "") + + (app.reloginNeeded && !app.restartNeeded ? " GNOME shows the dock, extensions and Settings panel after you log in again: Restart GNOME does that." : ""); if (app.failed.length) return "The BIOS was not changed."; return (app.bios && app.bios.dryRun ? "Dry run: nothing was flashed. " : "BIOS " + (app.biosInfo.newest || "") + " is staged and is written during the restart. ") + "Keep the power on and don't touch the machine until it has fully started again, even if the screen stays black."; @@ -33,6 +34,7 @@ Item { delegate: Text { required property string line; width: details.width - 12; text: line; color: "#aab5c4"; font.family: Theme.mono; font.pixelSize: 13; wrapMode: Text.WrapAnywhere } } } Row { anchors.horizontalCenter: parent.horizontalCenter; spacing: 14; topPadding: 8 Btn { k: Input.g.back; text: "Back to the menu"; height: 52; onClicked: app.act("back") } - Btn { visible: app.restartNeeded; k: Input.g.ok; text: "Restart now"; primary: true; height: 52; onClicked: app.backend.restart() } } + Btn { visible: app.restartNeeded; k: Input.g.ok; text: "Restart now"; primary: true; height: 52; onClicked: app.backend.restart() } + Btn { visible: app.reloginNeeded && !app.restartNeeded; k: Input.g.ok; text: "Restart GNOME"; primary: true; height: 52; onClicked: app.backend.restartGnome() } } } } diff --git a/ui/steamify-ui b/ui/steamify-ui index 8bf2f27..930f2b8 100755 --- a/ui/steamify-ui +++ b/ui/steamify-ui @@ -345,6 +345,16 @@ class Backend(QObject): def restart(self): QProcess.startDetached("systemctl", ["reboot"]) + @Slot() + def restartGnome(self): + # GNOME Shell on Wayland can't restart in place: end the session. With GDM autologin (the SteamOS conversion) + # Steamify's helper logs the user straight back in; else a plain logout. + import subprocess + helper = "/usr/local/libexec/steamify-gdm-relogin" + if os.access(helper, os.X_OK) and subprocess.run(["sudo", "-n", helper], capture_output=True).returncode == 0: + return + QProcess.startDetached("gnome-session-quit", ["--logout", "--no-prompt"]) + def shutdown(self): if self._proc and self._proc.state() != QProcess.NotRunning: self._proc.kill() From e942f3264d1ade6232213eec10dbd06bb81eab2b Mon Sep 17 00:00:00 2001 From: rickpeters Date: Thu, 15 Oct 2026 20:33:16 +0200 Subject: [PATCH 2/5] UI: stop scaling at 1.5 on a desktop (a 1440p or ultrawide screen made everything twice as big); the TV mode still scales to the screen --- CHANGELOG.md | 1 + ui/qml/Main.qml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa68da..a46a19a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ one per merged pull request. - `645b331` **feat: the GNOME theme installs GNOME Settings with the "GNOME Desktop" panel (dock, desktop icons, tiling) as a prebuilt package, downloaded from the `settings` release of steamify-gnome-theme (built by GitHub Actions on current Arch, rebuilt weekly) and checked against its `.sha256`; a failure keeps the stock Settings. Turning the theme off puts the stock `gnome-control-center` back. Installs with the GNOME theme on re-apply it (feature version 2.13.0)** - **feat: after the GNOME theme is applied in a GNOME session, the app's done screen has "Restart GNOME" (GNOME Shell on Wayland can't restart in place, and the dock, extensions and Settings panel only show after a new login): with GDM autologin (the SteamOS conversion) Steamify's relogin helper logs the user straight back into the session they were in (without changing the session gaming mode's boot goes back to), else it is a plain logout (`gnome-session-quit`)** +- **fix: the app on a desktop (a window, however large, also fullscreen) stops scaling at 1.5: on a 1440p or ultrawide monitor it was twice as big as designed (the scale followed the screen's height). The TV mode (`--fullscreen`, gamescope) still scales to the screen** ## 2.12.0 - 2026-10-13 diff --git a/ui/qml/Main.qml b/ui/qml/Main.qml index 59f3032..d5f268a 100644 --- a/ui/qml/Main.qml +++ b/ui/qml/Main.qml @@ -36,7 +36,9 @@ ApplicationWindow { // own shape, so the bars reach the edges and the content fills it. Item { id: stage - readonly property real k: Math.min(win.width / 1280, win.height / 720) + // On a desktop (a window, however large) the scale stops at 1.5: a 1440p or ultrawide monitor would otherwise show + // everything twice as big. The TV mode (--fullscreen, gamescope) scales to the screen: it is read from the couch. + readonly property real k: Math.min(win.width / 1280, win.height / 720, win.fullscreen ? Infinity : 1.5) width: win.width / k; height: win.height / k transformOrigin: Item.TopLeft scale: k From ad3b07097fb144de1848260906e97a26126070cb Mon Sep 17 00:00:00 2001 From: rickpeters Date: Thu, 15 Oct 2026 20:35:34 +0200 Subject: [PATCH 3/5] UI: on a desktop keep the desktop's own scale (1.0), not a cap of 1.5 --- CHANGELOG.md | 2 +- ui/qml/Main.qml | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a46a19a..5c48294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ one per merged pull request. - `645b331` **feat: the GNOME theme installs GNOME Settings with the "GNOME Desktop" panel (dock, desktop icons, tiling) as a prebuilt package, downloaded from the `settings` release of steamify-gnome-theme (built by GitHub Actions on current Arch, rebuilt weekly) and checked against its `.sha256`; a failure keeps the stock Settings. Turning the theme off puts the stock `gnome-control-center` back. Installs with the GNOME theme on re-apply it (feature version 2.13.0)** - **feat: after the GNOME theme is applied in a GNOME session, the app's done screen has "Restart GNOME" (GNOME Shell on Wayland can't restart in place, and the dock, extensions and Settings panel only show after a new login): with GDM autologin (the SteamOS conversion) Steamify's relogin helper logs the user straight back into the session they were in (without changing the session gaming mode's boot goes back to), else it is a plain logout (`gnome-session-quit`)** -- **fix: the app on a desktop (a window, however large, also fullscreen) stops scaling at 1.5: on a 1440p or ultrawide monitor it was twice as big as designed (the scale followed the screen's height). The TV mode (`--fullscreen`, gamescope) still scales to the screen** +- **fix: the app on a desktop (a window, however large, also fullscreen) keeps the desktop's own scale instead of blowing its 1280x720 layout up to the screen's height (twice as big on a 1440p or ultrawide monitor): the layout fills the window at the scale of GNOME or KDE, and only a window smaller than 1280x720 shrinks it. The TV mode (`--fullscreen`, gamescope) still scales to the screen** ## 2.12.0 - 2026-10-13 diff --git a/ui/qml/Main.qml b/ui/qml/Main.qml index d5f268a..e9383fd 100644 --- a/ui/qml/Main.qml +++ b/ui/qml/Main.qml @@ -36,9 +36,11 @@ ApplicationWindow { // own shape, so the bars reach the edges and the content fills it. Item { id: stage - // On a desktop (a window, however large) the scale stops at 1.5: a 1440p or ultrawide monitor would otherwise show - // everything twice as big. The TV mode (--fullscreen, gamescope) scales to the screen: it is read from the couch. - readonly property real k: Math.min(win.width / 1280, win.height / 720, win.fullscreen ? Infinity : 1.5) + // On a desktop (a window, however large) the UI keeps the desktop's own scale: 1 here is one logical pixel, which Qt + // multiplies by the scale of GNOME or KDE, like any other app (a 1440p or ultrawide monitor would otherwise show + // everything twice as big). Only a window smaller than the layout shrinks it. The TV mode (--fullscreen, gamescope) + // scales to the screen: it is read from the couch. + readonly property real k: Math.min(win.width / 1280, win.height / 720, win.fullscreen ? Infinity : 1) width: win.width / k; height: win.height / k transformOrigin: Item.TopLeft scale: k From b9d14fa4de38c2b4361dd38889a841437b23a8a0 Mon Sep 17 00:00:00 2001 From: rickpeters Date: Thu, 15 Oct 2026 20:37:39 +0200 Subject: [PATCH 4/5] UI: a switch flips with one click (the first click only selected the row) --- CHANGELOG.md | 1 + ui/qml/MenuRow.qml | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c48294..1f5f6e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ one per merged pull request. - **feat: after the GNOME theme is applied in a GNOME session, the app's done screen has "Restart GNOME" (GNOME Shell on Wayland can't restart in place, and the dock, extensions and Settings panel only show after a new login): with GDM autologin (the SteamOS conversion) Steamify's relogin helper logs the user straight back into the session they were in (without changing the session gaming mode's boot goes back to), else it is a plain logout (`gnome-session-quit`)** - **fix: the app on a desktop (a window, however large, also fullscreen) keeps the desktop's own scale instead of blowing its 1280x720 layout up to the screen's height (twice as big on a 1440p or ultrawide monitor): the layout fills the window at the scale of GNOME or KDE, and only a window smaller than 1280x720 shrinks it. The TV mode (`--fullscreen`, gamescope) still scales to the screen** +- **fix: in the app a switch flips with one click (the first click used to only select the row, the second flipped it); the BIOS update and the boot choice still need the row selected first** ## 2.12.0 - 2026-10-13 diff --git a/ui/qml/MenuRow.qml b/ui/qml/MenuRow.qml index 7b8bb44..20b4b1c 100644 --- a/ui/qml/MenuRow.qml +++ b/ui/qml/MenuRow.qml @@ -31,7 +31,14 @@ Item { color: row.selected ? Theme.cardSel : Theme.card border.width: row.selected ? 2 : 0; border.color: Theme.accent MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor - onClicked: { Input.type = "keyboard"; if (row.selected) app.act("accept"); else app.sel = row.rowIndex; } } + onClicked: { + Input.type = "keyboard"; + var wasSelected = row.selected; + app.sel = row.rowIndex; + // A switch flips on the first click. The others (the BIOS update, the boot choice) need the row selected + // first: a stray click mustn't start a flash. + if (wasSelected || row.it.kind === "toggle") app.act("accept"); + } } Text { visible: !!row.it.parent; x: 20; anchors.verticalCenter: parent.verticalCenter; text: "└"; color: "#56627a"; font.pixelSize: 18 } Column { From b625ff98eb3bd408e44ca318500fbd6b4d82f5df Mon Sep 17 00:00:00 2001 From: rickpeters Date: Thu, 15 Oct 2026 20:38:23 +0200 Subject: [PATCH 5/5] UI: a click on a row only selects it; the switch and the BIOS button act on their own click --- CHANGELOG.md | 2 +- ui/qml/MenuRow.qml | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f5f6e4..d550277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ one per merged pull request. - **feat: after the GNOME theme is applied in a GNOME session, the app's done screen has "Restart GNOME" (GNOME Shell on Wayland can't restart in place, and the dock, extensions and Settings panel only show after a new login): with GDM autologin (the SteamOS conversion) Steamify's relogin helper logs the user straight back into the session they were in (without changing the session gaming mode's boot goes back to), else it is a plain logout (`gnome-session-quit`)** - **fix: the app on a desktop (a window, however large, also fullscreen) keeps the desktop's own scale instead of blowing its 1280x720 layout up to the screen's height (twice as big on a 1440p or ultrawide monitor): the layout fills the window at the scale of GNOME or KDE, and only a window smaller than 1280x720 shrinks it. The TV mode (`--fullscreen`, gamescope) still scales to the screen** -- **fix: in the app a switch flips with one click (the first click used to only select the row, the second flipped it); the BIOS update and the boot choice still need the row selected first** +- **fix: in the app a click on a row only selects it (a second click on the selected row used to flip it, so a click on a switch needed two); a click on the switch itself, or on the BIOS update button, acts at once and selects the row too** ## 2.12.0 - 2026-10-13 diff --git a/ui/qml/MenuRow.qml b/ui/qml/MenuRow.qml index 20b4b1c..4c9e282 100644 --- a/ui/qml/MenuRow.qml +++ b/ui/qml/MenuRow.qml @@ -31,14 +31,9 @@ Item { color: row.selected ? Theme.cardSel : Theme.card border.width: row.selected ? 2 : 0; border.color: Theme.accent MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor - onClicked: { - Input.type = "keyboard"; - var wasSelected = row.selected; - app.sel = row.rowIndex; - // A switch flips on the first click. The others (the BIOS update, the boot choice) need the row selected - // first: a stray click mustn't start a flash. - if (wasSelected || row.it.kind === "toggle") app.act("accept"); - } } + // A click on the row only selects it; the switch, the Gaming/Desktop buttons and the BIOS button act on their own + // click (below), selecting the row too. + onClicked: { Input.type = "keyboard"; app.sel = row.rowIndex; } } Text { visible: !!row.it.parent; x: 20; anchors.verticalCenter: parent.verticalCenter; text: "└"; color: "#56627a"; font.pixelSize: 18 } Column { @@ -74,14 +69,18 @@ Item { Rectangle { id: sw; visible: row.it.kind === "toggle"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter width: 46; height: 26; radius: 13; color: row.on ? Theme.accent : "#343f50" Behavior on color { ColorAnimation { duration: 120 } } - Rectangle { width: 20; height: 20; radius: 10; color: "#f4f7fb"; y: 3; x: row.on ? 23 : 3; Behavior on x { NumberAnimation { duration: 120 } } } } + Rectangle { width: 20; height: 20; radius: 10; color: "#f4f7fb"; y: 3; x: row.on ? 23 : 3; Behavior on x { NumberAnimation { duration: 120 } } } + MouseArea { anchors.fill: parent; anchors.margins: -6; cursorShape: Qt.PointingHandCursor + onClicked: { Input.type = "keyboard"; app.sel = row.rowIndex; app.act("accept"); } } } Text { visible: row.it.kind === "toggle"; anchors.right: sw.left; anchors.rightMargin: 12; anchors.verticalCenter: parent.verticalCenter text: row.it.on ? "now on" : "now off"; color: row.it.on ? Theme.good : Theme.faint; font.family: Theme.mono; font.pixelSize: 12 } // action (Update BIOS) Rectangle { visible: row.it.kind === "action"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter width: at.implicitWidth + 24; height: 30; radius: 8; color: Theme.warnBg Text { id: at; anchors.centerIn: parent; color: Theme.warn; font.family: Theme.body; font.pixelSize: 13; font.weight: Font.DemiBold - text: app.biosChecking ? "Checking…" : app.bios && app.bios.selectable ? "Update…" : (app.bios && app.bios.newest ? "Up to date" : "Unavailable") } } + text: app.biosChecking ? "Checking…" : app.bios && app.bios.selectable ? "Update…" : (app.bios && app.bios.newest ? "Up to date" : "Unavailable") } + MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor + onClicked: { Input.type = "keyboard"; app.sel = row.rowIndex; app.act("accept"); } } } } } }