Compare commits

..
Author SHA1 Message Date
theupriser e9b7031bec Merge release/2.13.0 into bugfix/gnome-theme-leftovers
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 21:05:44 +02:00
theupriser 7dce8295c1 Merge pull request #89 from theupriser/feature/gnome-relogin-auto
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 28s
App: a run that only needs a GNOME login ends with a countdown and logs in again
2026-10-15 21:05:12 +02:00
theupriser 80d44a3475 GNOME theme off: remove the dock's and tiling's settings schemas copied for GNOME Settings 2026-10-15 21:02:02 +02:00
theupriser 49fba4af82 Restart GNOME logs in to GNOME, not the account's default session (gaming mode after the conversion); the default is put back afterwards
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 21:01:39 +02:00
theupriser 4b601bb196 App: a run that only needs a new GNOME login ends with a countdown and logs in again by itself 2026-10-15 20:56:26 +02:00
theupriser 906b338fcc Merge pull request #87 from theupriser/bugfix/dock-icon-png
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 16s
Dock icon: ship the icon as a PNG and refer to it by name
2026-10-15 20:51:09 +02:00
theupriser ba501ff213 Merge pull request #88 from theupriser/feature/gnome-relogin
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 15s
App: Restart GNOME after the theme, desktop scale, row and switch clicks
2026-10-15 20:50:43 +02:00
theupriser bc11fc7653 Merge release/2.13.0 into bugfix/dock-icon-png
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 20:50:16 +02:00
theupriser 7f17d67e78 Merge pull request #86 from theupriser/bugfix/qt6-svg
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 14s
Install qt6-svg with PySide6: the app's logo doesn't show without it
2026-10-15 20:49:31 +02:00
theupriser 6f575448cf Dock icon: ship the icon as a PNG and refer to it by name (GNOME can't always draw the SVG) 2026-10-15 20:48:31 +02:00
theupriser 7b751ab726 Install qt6-svg with PySide6: without Qt's SVG plugin the app's logo doesn't show 2026-10-15 20:40:26 +02:00
10 changed files with 109 additions and 14 deletions

No files matched your search

+4 -3
View File
@@ -34,7 +34,7 @@ jobs:
.github/tools/bundle.sh dist/steamify.sh
# The app: sources, not the single-file build (its backend mode runs
# steamify.sh from its own folder).
tar -czf dist/steamify-app.tar.gz steamify.sh lib patches services ui assets/steam-gaming-settings.svg LICENSE.md
tar -czf dist/steamify-app.tar.gz steamify.sh lib patches services ui assets/steam-gaming-settings.svg assets/steam-gaming-settings.png LICENSE.md
cp ui/steamify-app.sh dist/steamify-app.sh
- name: Check
@@ -119,7 +119,7 @@ jobs:
## Changes in v$VERSION
$changes"
gh release create "v$VERSION" dist/steamify.sh dist/steamify-app.sh dist/steamify-app.tar.gz assets/steam-gaming-settings.svg \
gh release create "v$VERSION" dist/steamify.sh dist/steamify-app.sh dist/steamify-app.tar.gz assets/steam-gaming-settings.svg assets/steam-gaming-settings.png \
--target "$GITHUB_SHA" --title "Steamify CachyOS v$VERSION" --notes "$notes" --latest
# "latest" follows the newest version tag, for the older install URL
@@ -132,7 +132,7 @@ jobs:
else
gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
fi
gh release upload latest dist/steamify.sh dist/steamify-app.sh dist/steamify-app.tar.gz assets/steam-gaming-settings.svg --clobber
gh release upload latest dist/steamify.sh dist/steamify-app.sh dist/steamify-app.tar.gz assets/steam-gaming-settings.svg assets/steam-gaming-settings.png --clobber
# The mirror (Gitea) publishes its own release per version too, the same way: never overwrites one,
# notes from the changelog. gh can't talk to Gitea: its API for the check, a Gitea release action to
@@ -191,6 +191,7 @@ jobs:
dist/steamify-app.sh
dist/steamify-app.tar.gz
assets/steam-gaming-settings.svg
assets/steam-gaming-settings.png
# A new version starts the Steamify ISO's release of the ISO repo: its iso-1-github-tag.yml (always run from
# master) names the ISO after the newest Steamify release, tags it, and the Gitea mirror builds and attaches the ISO.
+5
View File
@@ -10,10 +10,15 @@ one per merged pull request.
- **feat: GNOME support (the "SteamOS theme for GNOME" option, GDM session switching, the GNOME launcher/notifier/tray behaviour): the Vapor colors, the Steam Deck wallpaper, Ubuntu Dock, Tiling Assistant, Desktop Icons NG, AppIndicator and Steamify's own Vapor shell extension, with Switch to Desktop remembering KDE or GNOME. Prototype work since 2.12.0: `5dfc916`, `fc28ea4`, `ae12c1b`, `2b84e6a`, `c5b59d9`, `62a30e6`, `039f269`, `12eb562`, `c7f5f51`, `7904833`, `ce1aa1b`, `ab3b581`, `eaa3f28`, `4221ae6`, `8432f63`, `09ef0b1`, `68342e7`, `0e0bf57`, `7a84b0d`, `dbaeaa1`**
- `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)**
- **fix: the app's logo (an SVG) shows on a fresh install: `pyside6` doesn't depend on `qt6-svg`, Qt's SVG plugin, so without it the header logo was empty and the window had no icon of its own; it is installed with PySide6 (the app's installer, the launcher, the update notifier)**
- **fix: the app's icon shows in GNOME's dock on a fresh install: GNOME couldn't draw the SVG (a blank icon: "Could not load a pixbuf from icon theme"), by path or by name. The app's installer and the launcher put a 256x256 PNG (`assets/steam-gaming-settings.png`, a release file next to the SVG) in the user's icon theme and the launchers refer to it by name; the SVG stays as the fallback for releases without the PNG**
- **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 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**
- **feat: a run that only needs a new GNOME login (the theme, no reboot) ends with a countdown on the done screen ("Restarting GNOME in 10 s", with Restart GNOME now and Cancel) and then logs in again by itself, the counterpart of KDE restarting plasmashell (GNOME Shell on Wayland is the compositor too and can't restart in place). A run that needs a reboot keeps "Restart now" (it loads GNOME fresh): before, the Restart GNOME button was hidden then, and nothing started by itself**
- **fix: Restart GNOME logs in to GNOME: after the SteamOS conversion the account's session is gaming mode, and the relogin went there (a restart that took a minute: Steam starting in gaming mode). `steamify-gdm-relogin` takes the session to log in to (the one the app runs in) for this login only and puts the account's session, the one the next boot goes to, back once the new session is up**
- **fix: turning the GNOME theme off also removes the dock's and tiling's settings schemas that were copied to `~/.local/share/glib-2.0/schemas` for GNOME Settings' panel (they stayed behind, for extensions that were gone). Desktop Icons NG and AppIndicator stay: they belong to the SteamOS conversion (the desktop shortcut and Steam's tray icon)**
## 2.12.0 - 2026-10-13
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+13 -1
View File
@@ -123,7 +123,7 @@ gnometheme_relogin() {
# 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
if [[ -x "$helper" ]] && sudo -n "$helper" "${XDG_SESSION_DESKTOP:-gnome}"; then return 0; fi
gnome-session-quit --logout --no-prompt
}
@@ -198,6 +198,18 @@ gnometheme_disable() {
# (The Dock/Desktop Icons/Tiling Settings launchers of earlier prototypes.)
rm -f "${XDG_DATA_HOME:-$HOME/.local/share}"/applications/steamify-{dock,icons,tiling}-settings.desktop \
"${XDG_DATA_HOME:-$HOME/.local/share}/gnome-background-properties/steamify-steamos.xml"
# The schemas of the dock and of tiling that were copied next to the user's own for GNOME Settings' panel
# (gnometheme_extensions_install): they go with the extensions, or settings schemas of nothing are left behind.
local schemas="${XDG_DATA_HOME:-$HOME/.local/share}/glib-2.0/schemas" ext f
for ext in ubuntu-dock@ubuntu.com tiling-assistant@ubuntu.com; do
for f in "$GNOME_USER_EXTENSIONS/$ext"/schemas/*.gschema.xml; do
[[ -e "$f" ]] && rm -f "$schemas/$(basename "$f")"
done
done
# (Also by name: the extension folders may be gone already.)
rm -f "$schemas"/org.gnome.shell.extensions.{dash-to-dock,tiling-assistant}.gschema.xml
if compgen -G "$schemas/*.gschema.xml" >/dev/null; then glib-compile-schemas "$schemas" 2>/dev/null
else rm -f "$schemas/gschemas.compiled"; rmdir "$schemas" 2>/dev/null; fi
rm -rf "$GNOME_USER_EXTENSIONS/steamify-vapor@steamify.github.io" "$GNOME_USER_EXTENSIONS/ubuntu-dock@ubuntu.com" "$GNOME_USER_EXTENSIONS/tiling-assistant@ubuntu.com"
if [[ -f "$STATE_DIR/gnometheme-gtk4.css" ]]; then
mv -f "$STATE_DIR/gnometheme-gtk4.css" "$GNOME_THEME_CSS"
+2
View File
@@ -30,6 +30,8 @@ notify_enable() {
info "Adding update notifications..."
# PySide6 for the tray icon, notify-send for the notification's buttons.
python3 -c 'import PySide6.QtWidgets' 2>/dev/null || missing+=(pyside6)
# (Its icons are SVGs, and Qt needs its SVG plugin for those.)
pacman -Q qt6-svg >/dev/null 2>&1 || missing+=(qt6-svg)
command -v notify-send >/dev/null || missing+=(libnotify)
if ((${#missing[@]})); then
sudo pacman -S --needed --noconfirm "${missing[@]}" ||
+9 -2
View File
@@ -96,7 +96,7 @@ EOF
#!/bin/bash
# Installed by Steamify CachyOS: start the newest release of its app.
set -o pipefail
if [[ "\${1:-}" != --in-terminal ]] && ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then
if [[ "\${1:-}" != --in-terminal ]] && { ! python3 -c 'import PySide6.QtQml' 2>/dev/null || ! pacman -Q qt6-svg >/dev/null 2>&1; }; then
exec $terminal "\$0" --in-terminal
fi
if ! curl -fsSL --max-time 30 "$WIZARD_APP_URL" | bash; then
@@ -110,7 +110,14 @@ EOF
# The icon comes with the release; without it, Steam's own icon.
local icon=steam tmp
tmp="$(mktemp)"
if curl -fsSL --max-time 20 "$WIZARD_RELEASE/steam-gaming-settings.svg" -o "$tmp" && grep -q '<svg' "$tmp"; then
local theme="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor"
# A PNG, by name, first: GNOME's dock can't always draw an SVG (blank icon, "Could not load a pixbuf from icon theme").
# (Releases before 2.13.0 only have the SVG.)
if curl -fsSL --max-time 20 "$WIZARD_RELEASE/steam-gaming-settings.png" -o "$tmp" && head -c 8 "$tmp" | grep -q PNG; then
install -D -m 644 "$tmp" "$theme/256x256/apps/io.github.theupriser.Steamify.png"
icon=io.github.theupriser.Steamify
gtk-update-icon-cache -q -f -t "$theme" 2>/dev/null || true
elif curl -fsSL --max-time 20 "$WIZARD_RELEASE/steam-gaming-settings.svg" -o "$tmp" && grep -q '<svg' "$tmp"; then
install -D -m 644 "$tmp" "$WIZARD_ICON"
# The full path: GNOME Shell's dock doesn't find a name in the user's icon folder (blank icon), and every desktop takes a path.
icon="$WIZARD_ICON"
+37 -1
View File
@@ -6,11 +6,36 @@
# ("A graphical session is already running!"): so the user is ended first,
# then GDM restarted. That work runs in its own transient unit, because
# ending the user kills this process too.
#
# An optional session id as the argument ("gnome"): that session is logged in
# for this time only. The session in AccountsService (what the next boot goes
# to: gaming mode after the SteamOS conversion) is put back once the new
# session is up.
if [[ "$1" != --work ]]; then
[[ -n "${SUDO_USER:-}" ]] || exit 1
exec systemd-run --no-block --collect --unit=steamify-gdm-relogin "$0" --work "$SUDO_USER"
exec systemd-run --no-block --collect --unit=steamify-gdm-relogin "$0" --work "$SUDO_USER" "${1:-}"
fi
USER_NAME="$2"
SESSION="$3"
ACCOUNT="/var/lib/AccountsService/users/$USER_NAME"
# Session= and XSession= of the user, as GDM reads them at the next login.
get_session() { sed -n "s/^$1=//p" "$ACCOUNT" 2>/dev/null | head -1; }
set_session() {
local key
touch "$ACCOUNT"
grep -q '^\[User\]' "$ACCOUNT" || echo "[User]" >> "$ACCOUNT"
for key in Session XSession; do
if grep -q "^$key=" "$ACCOUNT"; then sed -i "s|^$key=.*|$key=$1|" "$ACCOUNT"
else sed -i "/^\[User\]/a $key=$1" "$ACCOUNT"; fi
done
systemctl restart accounts-daemon 2>/dev/null || true
}
if [[ -n "$SESSION" ]]; then
OLD_SESSION="$(get_session Session)"; OLD_XSESSION="$(get_session XSession)"
set_session "${SESSION%.desktop}"
fi
# GDM is stopped first, so its login screen doesn't show while the old user
# manager shuts down; a new login right away fails ("A graphical session is
# already running!", or the session dies at once), so wait until it is gone.
@@ -20,3 +45,14 @@ loginctl terminate-user "$USER_NAME"
USER_RUNTIME="/run/user/$(id -u "$USER_NAME")"
for _ in $(seq 120); do [[ -d "$USER_RUNTIME" ]] || break; sleep 0.25; done
systemctl start gdm.service
if [[ -n "$SESSION" ]]; then
# The new session has read what it logs in to once its user manager runs; then the next boot's session goes back.
for _ in $(seq 120); do [[ -d "$USER_RUNTIME" ]] && break; sleep 0.25; done
sleep 8
if [[ -n "$OLD_SESSION" ]]; then
sed -i "s|^Session=.*|Session=$OLD_SESSION|" "$ACCOUNT"
if [[ -n "$OLD_XSESSION" ]]; then sed -i "s|^XSession=.*|XSession=$OLD_XSESSION|" "$ACCOUNT"; fi
systemctl restart accounts-daemon 2>/dev/null || true
fi
fi
+17 -2
View File
@@ -7,10 +7,24 @@ import QtQuick.Controls.Basic
Item {
required property AppState app
readonly property bool problem: app.failed.length > 0 || app.runError !== ""
// GNOME Shell on Wayland can't restart in place (it is the compositor too), so what a KDE run does for plasmashell is a
// new login here: started by itself after a countdown, unless the run needs a reboot anyway (which does the same).
readonly property bool relogin: app.reloginNeeded && !app.restartNeeded && !problem
property int countdown: 10
property bool cancelled: false
property bool fired: false
readonly property bool counting: relogin && !cancelled && !fired
onVisibleChanged: if (visible) { countdown = 10; cancelled = false; fired = false; }
function reloginNow() { fired = true; app.backend.restartGnome(); }
Timer {
interval: 1000; repeat: true; running: parent.visible && parent.counting
onTriggered: { parent.countdown -= 1; if (parent.countdown <= 0) parent.reloginNow(); }
}
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." : "")
+ (app.reloginNeeded && !app.restartNeeded ? " GNOME shows the dock, extensions and Settings panel after you log in again: Restart GNOME does that." : "");
+ (counting ? " Restarting GNOME in " + countdown + " s, so it loads the new dock, extensions and Settings panel."
: (relogin ? " GNOME shows the dock, extensions and Settings panel after you log in again." : ""));
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.";
@@ -35,6 +49,7 @@ Item {
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.reloginNeeded && !app.restartNeeded; k: Input.g.ok; text: "Restart GNOME"; primary: true; height: 52; onClicked: app.backend.restartGnome() } }
Btn { visible: relogin; k: Input.g.ok; text: counting ? "Restart GNOME now" : "Restart GNOME"; primary: true; height: 52; onClicked: reloginNow() }
Btn { visible: counting; text: "Cancel"; height: 52; onClicked: cancelled = true } }
}
}
+19 -4
View File
@@ -47,10 +47,14 @@ rm -rf "$DEST.new"; mv "$tmp/app" "$DEST.new"
rm -rf "$DEST"; mv "$DEST.new" "$DEST"
say "Installed to $DEST ($(grep -m1 -oP '^VERSION=\K.*' "$DEST/steamify.sh"))."
if ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then
say "The app needs PySide6 (Qt for Python); installing it..."
missing=()
python3 -c 'import PySide6.QtQml' 2>/dev/null || missing+=(pyside6)
# The app's logo is an SVG: Qt needs its SVG plugin for it, and pyside6 doesn't pull it in.
pacman -Q qt6-svg >/dev/null 2>&1 || missing+=(qt6-svg)
if ((${#missing[@]})); then
say "The app needs ${missing[*]} (Qt for Python and its SVG support); installing..."
# Piped in via curl, stdin isn't the terminal: sudo asks on it anyway.
sudo pacman -S --needed --noconfirm pyside6 </dev/tty || die "Installing pyside6 failed."
sudo pacman -S --needed --noconfirm "${missing[@]}" </dev/tty || die "Installing ${missing[*]} failed."
fi
# Launcher entry (its id also names the app to the desktop portal). The
@@ -58,6 +62,17 @@ fi
# release: left alone.
mkdir -p "$APPS"
rm -f "$APPS/steamify-ui.desktop" # its name until 2.12.0
# The icon by name, from the user's icon theme, as a PNG: GNOME's dock can't always draw an SVG (blank icon: "Could not load a
# pixbuf from icon theme"), and a file path to one fails the same way.
ICON_NAME=io.github.theupriser.Steamify
ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor"
if [[ -f "$DEST/assets/steam-gaming-settings.png" ]]; then
install -D -m 644 "$DEST/assets/steam-gaming-settings.png" "$ICON_DIR/256x256/apps/$ICON_NAME.png"
gtk-update-icon-cache -q -f -t "$ICON_DIR" 2>/dev/null || true
ICON="$ICON_NAME"
else
ICON="$DEST/assets/steam-gaming-settings.svg"
fi
grep -q '^X-Steamify-Shortcut=true' "$APPS/io.github.theupriser.Steamify.desktop" 2>/dev/null ||
cat > "$APPS/io.github.theupriser.Steamify.desktop" << EOF
[Desktop Entry]
@@ -65,7 +80,7 @@ Type=Application
Name=Steamify CachyOS
Comment=Turn this PC into a SteamOS-style console
Exec=$DEST/ui/steamify-ui
Icon=$DEST/assets/steam-gaming-settings.svg
Icon=$ICON
Terminal=false
Categories=Game;Settings;
StartupWMClass=io.github.theupriser.Steamify
+3 -1
View File
@@ -351,7 +351,9 @@ class Backend(QObject):
# 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:
# The session this one is, for this login only: the default (gaming mode, after the conversion) would log in there.
session = os.environ.get("XDG_SESSION_DESKTOP") or "gnome"
if os.access(helper, os.X_OK) and subprocess.run(["sudo", "-n", helper, session], capture_output=True).returncode == 0:
return
QProcess.startDetached("gnome-session-quit", ["--logout", "--no-prompt"])