Compare commits

...
Author SHA1 Message Date
theupriser 063b5a8032 Turning everything off gives GNOME back the desktop and tray icon extensions Steamify added 2026-10-15 21:07:15 +02:00
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
theupriser b625ff98eb UI: a click on a row only selects it; the switch and the BIOS button act on their own click
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 20:38:23 +02:00
theupriser b9d14fa4de UI: a switch flips with one click (the first click only selected the row)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 20:37:39 +02:00
theupriser ad3b07097f UI: on a desktop keep the desktop's own scale (1.0), not a cap of 1.5
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 20:35:34 +02:00
theupriser e942f3264d 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
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-15 20:33:16 +02:00
theupriser 0bb5708769 GNOME theme: Restart GNOME button after applying the theme (relogin through GDM when set up, else a logout) 2026-10-15 20:29:13 +02:00
theupriser 0c46b209b9 Merge pull request #85 from theupriser/feat/gnome-settings-prebuilt
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 28s
GNOME theme: install the prebuilt GNOME Settings with the Desktop panel
2026-10-15 20:17:44 +02:00
theupriser 2905b45ef7 Release 2.13.0: version, changelog, GNOME theme feature version 2026-10-15 20:10:50 +02:00
theupriser 645b3314be GNOME theme: install the prebuilt GNOME Settings with the Desktop panel
Downloaded from the steamify-gnome-theme 'settings' release (built by GitHub
Actions on current Arch, rebuilt weekly), checked against its sha256, installed
with pacman -U --ask 4. A failure only warns and keeps the stock Settings;
disabling the theme puts the stock gnome-control-center back.
2026-10-15 19:49:56 +02:00
theupriser fd61d84017 Merge pull request #84 from theupriser/fix/gsn-smoke-test-pipefail
Sync git.upriser.nl mirror / sync (push) Skipped
gamescope-nvidia: smoke test judged by its log, not a pipe into grep -q
2026-10-15 18:09:30 +02:00
theupriser 2ca288de39 gamescope-nvidia: smoke test judged by its log, not a pipe into grep -q
grep -q quits at the first match, gamescope then dies on the broken pipe, and with
pipefail a working build counted as broken (intermittent: the generic build was
tried and failed too, gaming mode setup failed on an RTX 5080). Output goes to a
file now and timeout -k kills a hung shutdown.
2026-10-15 17:54:54 +02:00
theupriser dbaeaa14c9 App id io.github.theupriser.Steamify (GNOME matches windows to launchers only by reverse-DNS ids), own dark window frame on GNOME, launcher icon as a full path
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-14 15:44:37 +02:00
theupriser 7a84b0dbca Update notifier: Steamify's own tray icon (single-color on GNOME)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-14 15:18:26 +02:00
theupriser 0e0bf57ca8 Steamify launcher: StartupWMClass so GNOME's dock matches the app window; session tool records the last desktop from its shell 2026-10-14 15:17:17 +02:00
theupriser 68342e7a24 Preferred desktop for Switch to Desktop (kde/gnome/last), session tools for every login manager when GNOME is installed, GNOME theme texts in the app 2026-10-14 15:09:16 +02:00
theupriser 09ef0b1c71 GNOME theme: no arrow on menus 2026-10-14 15:04:32 +02:00
theupriser 8432f634ef GNOME theme: GTK colors follow the light/dark style; adw-gtk3 2026-10-14 15:00:44 +02:00
theupriser 4221ae6e21 GNOME theme: register the extensions' schemas for GNOME Settings 2026-10-14 14:54:27 +02:00
theupriser eaa3f28fdc GNOME theme: no trash on the desktop (it is in the dock) 2026-10-14 14:48:43 +02:00
theupriser ab3b581f45 GNOME theme: Ubuntu Dock and Tiling Assistant, SteamOS backgrounds in Settings, dock keeps Ubuntu's look, no settings launchers 2026-10-14 14:46:32 +02:00
theupriser ce1aa1bf7d GNOME theme: one Steamify Vapor extension carries the shell colors; no gnome-shell-extensions bundle; dock shape left to the user 2026-10-14 14:38:32 +02:00
theupriser 79048338bf GNOME theme: Dock Settings and Desktop Icons Settings launchers 2026-10-14 14:24:44 +02:00
theupriser c7f5f515a3 GNOME theme: shell theme and dock in Vapor colors, Ubuntu-style extensions, start on the desktop; never pick GNOME Classic 2026-10-14 14:22:58 +02:00
theupriser 12eb56220a GDM steam-set-session: don't exit at boot when nobody is logged in 2026-10-14 14:08:44 +02:00
theupriser 039f2691d8 GNOME: terminal for the Steamify shortcut and notifier, notifier runs on GNOME 2026-10-14 14:00:58 +02:00
theupriser 62a30e6095 GNOME: install Extension Manager 2026-10-14 13:55:23 +02:00
theupriser c5b59d9438 GNOME: AppIndicator extension for tray icons 2026-10-14 13:54:30 +02:00
theupriser 2b84e6afa6 GNOME: desktop icons through the Desktop Icons NG extension 2026-10-14 13:51:49 +02:00
theupriser ae12c1bb41 GNOME: pin the Return to Gaming Mode launcher to the dash; relogin waits for the runtime dir 2026-10-14 13:51:03 +02:00
theupriser fc28ea4c64 GDM relogin: stop GDM while the old user manager shuts down 2026-10-14 13:45:03 +02:00
theupriser 5dfc916b97 GNOME prototype: detect GNOME/GDM, GDM autologin and session switching 2026-10-14 13:40:13 +02:00
39 changed files with 1143 additions and 66 deletions

No files matched your search

+4 -3
View File
@@ -34,7 +34,7 @@ jobs:
.github/tools/bundle.sh dist/steamify.sh .github/tools/bundle.sh dist/steamify.sh
# The app: sources, not the single-file build (its backend mode runs # The app: sources, not the single-file build (its backend mode runs
# steamify.sh from its own folder). # 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 cp ui/steamify-app.sh dist/steamify-app.sh
- name: Check - name: Check
@@ -119,7 +119,7 @@ jobs:
## Changes in v$VERSION ## Changes in v$VERSION
$changes" $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 --target "$GITHUB_SHA" --title "Steamify CachyOS v$VERSION" --notes "$notes" --latest
# "latest" follows the newest version tag, for the older install URL # "latest" follows the newest version tag, for the older install URL
@@ -132,7 +132,7 @@ jobs:
else else
gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
fi 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, # 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 # 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.sh
dist/steamify-app.tar.gz dist/steamify-app.tar.gz
assets/steam-gaming-settings.svg 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 # 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. # master) names the ISO after the newest Steamify release, tags it, and the Gitea mirror builds and attaches the ISO.
+2 -2
View File
@@ -162,7 +162,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
- A release that adds, removes or renames a menu row also retakes the - A release that adds, removes or renames a menu row also retakes the
README screenshot (`assets/screenshot-menu-steam-machine.png`; NVIDIA PCs get the same menu, so there is no separate README screenshot (`assets/screenshot-menu-steam-machine.png`; NVIDIA PCs get the same menu, so there is no separate
one): the app from the branch on a Steam Machine, every row visible and the window only as tall as its options need, the header showing the new one): the app from the branch on a Steam Machine, every row visible and the window only as tall as its options need, the header showing the new
`VERSION`, 1600 px wide. Size the window with a KWin script (match the `steamify-ui` class exactly: a title match also hits `VERSION`, 1600 px wide. Size the window with a KWin script (match the `io.github.theupriser.Steamify` class exactly (`steamify-ui` until the GNOME work): a title match also hits
Konsole and editor windows) and crop the full-screen capture to the client rectangle; mind the display scale (the Steam Konsole and editor windows) and crop the full-screen capture to the client rectangle; mind the display scale (the Steam
Machine runs at 1.75). Machine runs at 1.75).
- Users install through `releases/latest/download/steamify.sh` - Users install through `releases/latest/download/steamify.sh`
@@ -259,7 +259,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
again. Scripted input that runs out quits without restarting (never again. Scripted input that runs out quits without restarting (never
restart on EOF: `ask_yn` would take its default). restart on EOF: `ask_yn` would take its default).
- Steamify shortcut (`launcher`): "Steamify CachyOS" (desktop and launcher, - Steamify shortcut (`launcher`): "Steamify CachyOS" (desktop and launcher,
id `steamify-ui.desktop`, the app's own id) runs `curl | bash` of id `io.github.theupriser.Steamify.desktop`, the app's own id; `steamify-ui.desktop` until the GNOME work) runs `curl | bash` of
`releases/latest/download/steamify-app.sh` without a terminal (in Konsole `releases/latest/download/steamify-app.sh` without a terminal (in Konsole
only while PySide6 is missing, for sudo); "Steamify Terminal" (launcher) only while PySide6 is missing, for sudo); "Steamify Terminal" (launcher)
runs `steamify.sh` in Konsole. Both always the newest release. The entry runs `steamify.sh` in Konsole. Both always the newest release. The entry
+16
View File
@@ -5,6 +5,22 @@ All notable changes, per version and per commit. Versions follow
`steamify.sh`. Versions before 0.7.0 were numbered afterwards, `steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request. one per merged pull request.
## 2.13.0 - 2026-10-15
- **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)**
- **fix: turning everything off gives GNOME back what Steamify added for the desktop and tray icons: Desktop Icons NG and AppIndicator (and Extension Manager), which the SteamOS conversion and now the GNOME theme both need. When the last of the two is turned off, the two extensions leave the user's list of enabled extensions and the packages Steamify installed (noted in `gnomeextras.list` before installing, so packages the user had stay) are removed; while the other one is still on they stay, and are enabled again (the theme's undo puts back the list it found). Before, nothing removed them. The theme installs them itself too**
## 2.12.0 - 2026-10-13 ## 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** - **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**
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><g fill="#bebebe"><path d="m 26.586,23.6632 8.104641,-0.4201 1.91027,-6.7552 -5.59099,-4.4419 -6.871493,5.8329 z"/><path d="M 31.027515,23.9946 35,23 l 1.976562,-2.9766 -4,-4 -4.949047,4.9712 z"/><path d="M 15.316486,35.7061 21.675457,35.065 23.0333,27.5143 16.403747,25.7462 12.603749,30.3195 Z"/><path d="m 16.985332,35.8717 3.972485,-0.4165 2.210937,-3.4453 -3.0625,-3.1563 -6.933422,3.9868 z"/><path d="m 30.5,11 c -4.142135,0 -7.499999,3.357864 -7.5,7.5 0.0017,0.201484 0.01143,0.402819 0.0293,0.603516 -1.289636,1.575722 -4.80544,5.546997 -5.113281,5.902343 -2.416761,0.03284 -4.577852,1.51258 -5.482422,3.753907 -1.237432,3.074241 0.2518,6.569519 3.326172,7.80664 3.074245,1.237417 6.56952,-0.251813 7.80664,-3.326172 0.274389,-0.686886 0.419405,-1.418587 0.427735,-2.158203 l 5.896484,-5.111328 C 30.093269,25.98873 30.296562,25.998503 30.5,26 34.642135,26 37.999999,22.642136 38,18.5 37.999999,14.357864 34.642135,11 30.5,11 Z m 0,3 c 2.48528,0 4.499999,2.014718 4.5,4.5 -10e-7,2.485282 -2.01472,4.5 -4.5,4.5 -2.48528,0 -4.499999,-2.014718 -4.5,-4.5 1e-6,-2.485282 2.01472,-4.5 4.5,-4.5 z M 18.046875,27 c 0.496175,0.0058 0.986949,0.103774 1.447266,0.289062 2.049689,0.825003 3.042263,3.155585 2.216797,5.205079 -0.825003,2.049668 -3.155579,3.04224 -5.205079,2.216797 C 14.45617,33.885935 13.463596,31.555353 14.289062,29.505859 14.905127,27.975627 16.397393,26.980525 18.046875,27 Z"/><circle cx="30.5" cy="18.4998" r="3.5"/><path d="M 5.0722656,22.564453 A 19,19 0 0 0 5,24 19,19 0 0 0 5.7695312,29.3125 l 11.1113278,4.470703 a 3,3 0 0 0 3.902344,-1.664062 3,3 0 0 0 -1.664062,-3.902344 z"/><path d="M 40.440,33.688 L 42.527,33.983 L 42.527,36.007 L 40.440,36.302 L 39.770,37.921 L 41.037,39.606 L 39.606,41.037 L 37.921,39.770 L 36.302,40.440 L 36.007,42.527 L 33.983,42.527 L 33.688,40.440 L 32.069,39.770 L 30.384,41.037 L 28.953,39.606 L 30.220,37.921 L 29.550,36.302 L 27.463,36.007 L 27.463,33.983 L 29.550,33.688 L 30.220,32.069 L 28.953,30.384 L 30.384,28.953 L 32.069,30.220 L 33.688,29.550 L 33.983,27.463 L 36.007,27.463 L 36.302,29.550 L 37.921,30.220 L 39.606,28.953 L 41.037,30.384 L 39.770,32.069 Z M 37.395,34.995 A 2.4,2.4 0 1 0 32.595,34.995 A 2.4,2.4 0 1 0 37.395,34.995 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+5 -3
View File
@@ -178,8 +178,7 @@ backend_apply() {
backend_sudo || return 1 backend_sudo || return 1
LOGIN_MANAGER=plasmalogin choose_login_manager
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
local -a failed=() local -a failed=()
for c in "${TO_DISABLE[@]}"; do for c in "${TO_DISABLE[@]}"; do
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
@@ -196,7 +195,10 @@ backend_apply() {
feature_record_unticked feature_record_unticked
os_version_refresh os_version_refresh
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true [[ " ${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() { backend_main() {
+6 -1
View File
@@ -17,13 +17,18 @@ boot_status() {
[[ -f "$BOOT_UNIT" ]] && systemctl is-enabled -q "$BOOT_UNIT_NAME" 2>/dev/null [[ -f "$BOOT_UNIT" ]] && systemctl is-enabled -q "$BOOT_UNIT_NAME" 2>/dev/null
} }
boot_desktop_arg() {
# Our session tool (installed with GNOME) resolves "desktop" to the preferred desktop; CachyOS's own takes a session name.
if [[ -f "$GDM_FIX_DIR/steam-set-session" ]]; then echo desktop; else echo plasma.desktop; fi
}
boot_enable() { boot_enable() {
info "Setting the PC to boot into the desktop..." info "Setting the PC to boot into the desktop..."
# The sync bridge (plasma-login-manager only; not there with SDDM) copies # The sync bridge (plasma-login-manager only; not there with SDDM) copies
# the session into the base config; run it right away rather than rely # the session into the base config; run it right away rather than rely
# on its path unit, which could still be pending when the login manager # on its path unit, which could still be pending when the login manager
# starts. # starts.
service_file "$BOOT_UNIT_NAME" SESSION_SYNC="$SESSION_SYNC" | sudo tee "$BOOT_UNIT" > /dev/null service_file "$BOOT_UNIT_NAME" SESSION_SYNC="$SESSION_SYNC" DESKTOP_SESSION="$(boot_desktop_arg)" | sudo tee "$BOOT_UNIT" > /dev/null
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable "$BOOT_UNIT_NAME" || { err "Enabling $BOOT_UNIT_NAME failed."; return 1; } sudo systemctl enable "$BOOT_UNIT_NAME" || { err "Enabling $BOOT_UNIT_NAME failed."; return 1; }
ok "Boots into the desktop from the next boot on (Return to Gaming Mode still works)." ok "Boots into the desktop from the next boot on (Return to Gaming Mode still works)."
+104 -2
View File
@@ -23,7 +23,10 @@ create_desktop_shortcut() {
# the shortcut can't sudo for interactively (Terminal=false), so it gets # the shortcut can't sudo for interactively (Terminal=false), so it gets
# a password-less rule scoped to exactly those two commands. # a password-less rule scoped to exactly those two commands.
local exec_line="steamos-session-select gamescope" local exec_line="steamos-session-select gamescope"
if [[ "$LOGIN_MANAGER" == "plasmalogin" ]]; then if [[ "$LOGIN_MANAGER" == "gdm" ]]; then
# Its own steamos-session-select (lib/login-manager.sh) restarts GDM, through the helper and rule gdm_fix_install adds.
:
elif [[ "$LOGIN_MANAGER" == "plasmalogin" ]]; then
exec_line="sh -c 'steamos-session-select gamescope && sudo -n /usr/bin/systemctl start sync-steamos-session.service && sudo -n /usr/bin/systemctl restart plasmalogin'" exec_line="sh -c 'steamos-session-select gamescope && sudo -n /usr/bin/systemctl start sync-steamos-session.service && sudo -n /usr/bin/systemctl restart plasmalogin'"
local sudoers_tmp local sudoers_tmp
sudoers_tmp="$(mktemp)" sudoers_tmp="$(mktemp)"
@@ -40,6 +43,9 @@ EOF
else else
sudo rm -f /etc/sudoers.d/gamescope-session-switch sudo rm -f /etc/sudoers.d/gamescope-session-switch
fi fi
# GNOME shows no icons on the desktop: the application grid gets the shortcut too.
gnome_app_shortcut=false
[[ "$(desktop_kind)" == gnome ]] && gnome_app_shortcut=true
# SteamOS itself uses Valve's "gaming-return" icon (Steam logo with a # SteamOS itself uses Valve's "gaming-return" icon (Steam logo with a
# return arrow; the Deck-style arrow is only used on Steam Deck # return arrow; the Deck-style arrow is only used on Steam Deck
@@ -66,6 +72,14 @@ EOF
# Fix permissions for the target user and KDE Plasma desktop ecosystem # Fix permissions for the target user and KDE Plasma desktop ecosystem
chmod +x "$shortcut_path" chmod +x "$shortcut_path"
if [[ "$gnome_app_shortcut" == true ]]; then
mkdir -p "$user_home/.local/share/applications"
cp "$shortcut_path" "$user_home/.local/share/applications/steamify-return-to-gaming-mode.desktop"
# GNOME only starts launchers it trusts.
gio set "$shortcut_path" metadata::trusted true 2>/dev/null || true
gnome_dash_pin
gnome_extensions_enable
fi
ok "Desktop shortcut created successfully." ok "Desktop shortcut created successfully."
} }
@@ -81,6 +95,94 @@ set_shortcut_icon() {
remove_desktop_shortcut() { remove_desktop_shortcut() {
local desktop_dir local desktop_dir
desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")" desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")"
rm -f "$desktop_dir/Return to Gaming Mode.desktop" rm -f "$desktop_dir/Return to Gaming Mode.desktop" "$HOME/.local/share/applications/steamify-return-to-gaming-mode.desktop"
gnome_dash_unpin
sudo rm -f /etc/sudoers.d/gamescope-session-switch sudo rm -f /etc/sudoers.d/gamescope-session-switch
} }
# GNOME draws no icons on the desktop: the shortcut is pinned to the dash
# (the favorites bar) as well. Only in a running session (gsettings needs its dconf).
GNOME_SHORTCUT_ID=steamify-return-to-gaming-mode.desktop
gnome_dash_pin() {
user_session && command -v gsettings >/dev/null || return 0
local favorites
favorites="$(gsettings get org.gnome.shell favorite-apps 2>/dev/null)" || return 0
[[ "$favorites" == *"$GNOME_SHORTCUT_ID"* ]] && return 0
favorites="$(sed "s/\]\$/, '$GNOME_SHORTCUT_ID']/; s/^\[, /[/; s/\[\], /[/" <<< "$favorites")"
[[ "$favorites" == "[ '$GNOME_SHORTCUT_ID']" ]] && favorites="['$GNOME_SHORTCUT_ID']"
gsettings set org.gnome.shell favorite-apps "$favorites"
}
gnome_dash_unpin() {
user_session && command -v gsettings >/dev/null || return 0
local favorites
favorites="$(gsettings get org.gnome.shell favorite-apps 2>/dev/null)" || return 0
[[ "$favorites" == *"$GNOME_SHORTCUT_ID"* ]] || return 0
favorites="$(sed -e "s/, '$GNOME_SHORTCUT_ID'//; s/'$GNOME_SHORTCUT_ID', //; s/'$GNOME_SHORTCUT_ID'//" <<< "$favorites")"
gsettings set org.gnome.shell favorite-apps "$favorites"
}
# GNOME draws no desktop icons and no tray icons (Steam's, for one) itself:
# the Desktop Icons NG and AppIndicator extensions, installed with the other
# GNOME packages (lib/packages.sh). Enabled in the user's settings; GNOME
# loads a new extension at the next login.
GNOME_EXTENSIONS=(ding@rastersoft.com appindicatorsupport@rgcjonas.gmail.com)
gnome_extensions_enable() {
user_session && command -v gsettings >/dev/null || return 0
local uuid enabled
for uuid in "${GNOME_EXTENSIONS[@]}"; do
[[ -d "/usr/share/gnome-shell/extensions/$uuid" ]] || continue
enabled="$(gsettings get org.gnome.shell enabled-extensions 2>/dev/null)" || return 0
[[ "$enabled" == *"$uuid"* ]] && continue
if [[ "$enabled" == "@as []" || "$enabled" == "[]" ]]; then enabled="['$uuid']"
else enabled="${enabled%]}, '$uuid']"; fi
gsettings set org.gnome.shell enabled-extensions "$enabled"
done
}
# The packages behind them. Needed by the SteamOS conversion (the desktop shortcut's icon, Steam's tray icon) and by the
# GNOME theme (its desktop and tray icons); a PC that has neither any more gets back what Steamify added.
GNOME_EXTRA_PKGS=(gnome-shell-extension-desktop-icons-ng gnome-shell-extension-appindicator extension-manager)
gnome_extras_note_missing() {
# Before they are installed: which of them Steamify brings itself (the others were the user's: they stay), one name per
# line in a file of its own, not a state value: kwriteconfig6 (kconfig) is one of the packages installed in that step.
# Already noted ones stay noted (they are installed by then, so they aren't "missing" again).
local p list="$STATE_DIR/gnomeextras.list"
mkdir -p "$STATE_DIR"
for p in "${GNOME_EXTRA_PKGS[@]}"; do
pacman -Q "$p" >/dev/null 2>&1 || grep -qxF "$p" "$list" 2>/dev/null || echo "$p" >> "$list"
done
}
gnome_extensions_disable() {
# The two out of the user's list of enabled extensions again.
user_session && command -v gsettings >/dev/null || return 0
local uuid enabled
for uuid in "${GNOME_EXTENSIONS[@]}"; do
enabled="$(gsettings get org.gnome.shell enabled-extensions 2>/dev/null)" || return 0
[[ "$enabled" == *"$uuid"* ]] || continue
enabled="$(sed -e "s/'$uuid', //; s/, '$uuid'//; s/'$uuid'//" <<<"$enabled")"
[[ "$enabled" == "[]" ]] && enabled="@as []"
gsettings set org.gnome.shell enabled-extensions "$enabled"
done
}
gnome_extras_release() {
# Called when the SteamOS conversion or the GNOME theme was turned off, after its own undoing. While the other one is
# still on, the extensions stay (and are enabled again: the theme's undo puts back the list it found). When neither is:
# they are disabled, and the packages Steamify installed are removed (a failed removal, because something else needs a
# package, keeps it).
[[ "$(desktop_kind)" == gnome ]] || return 0
if gaming_status || gnometheme_status; then gnome_extensions_enable; return 0; fi
gnome_extensions_disable
local p list="$STATE_DIR/gnomeextras.list"
for p in "${GNOME_EXTRA_PKGS[@]}"; do
grep -qxF "$p" "$list" 2>/dev/null || continue
pacman -Q "$p" >/dev/null 2>&1 || continue
sudo pacman -R --noconfirm "$p" >/dev/null 2>&1 || info "Keeping $p installed (another package needs it)."
done
rm -f "$list"
}
+68
View File
@@ -0,0 +1,68 @@
#!/bin/bash
# Which desktop Steamify is running on (KDE Plasma or GNOME), and the
# session the login manager has to start for it. GNOME (GDM) is a prototype.
# Sourced by steamify.sh; not meant to be run on its own.
desktop_kind() {
# gnome or kde: the running session first, else what the login manager
# is (GDM means GNOME), else KDE, which Steamify was made for.
case ":${STEAMIFY_DESKTOP:-${XDG_CURRENT_DESKTOP:-}}:" in
*:GNOME:*|*:gnome:*) echo gnome; return ;;
*:KDE:*|*:kde:*) echo kde; return ;;
esac
[[ "$(current_display_manager)" == gdm ]] && { echo gnome; return; }
echo kde
}
desktop_session_file() {
# The login manager's session entry for the desktop: plasma.desktop, or
# whichever GNOME one is installed (gnome.desktop, gnome-xorg.desktop).
local f
if [[ "$(desktop_kind)" == gnome ]]; then
# The plain session, not GNOME Classic (gnome-classic.desktop, from gnome-shell-extensions) or Xorg's.
for f in /usr/share/wayland-sessions/gnome.desktop /usr/share/xsessions/gnome.desktop \
/usr/share/wayland-sessions/gnome-wayland.desktop; do
[[ -f "$f" ]] && { basename "$f"; return; }
done
echo gnome.desktop
else
echo plasma.desktop
fi
}
choose_login_manager() {
# LOGIN_MANAGER for gaming_enable. With KDE and GNOME both installed the active login
# manager stays (switching it would be a surprise); otherwise gdm on GNOME, sddm with
# single user mode and plasmalogin without.
local current
current="$(current_display_manager)"
if desktop_installed gnome && desktop_installed kde; then
case "$current" in gdm|sddm|plasmalogin) LOGIN_MANAGER="$current"; return ;; esac
fi
if [[ "$(desktop_kind)" == gnome ]]; then LOGIN_MANAGER=gdm
elif [[ "${WANTED[single]:-0}" == 1 ]]; then LOGIN_MANAGER=sddm
else LOGIN_MANAGER=plasmalogin; fi
}
# Which desktop "Switch to Desktop" (and booting into the desktop) goes to when both are
# installed: kde, gnome, or last (the one you left for gamescope). Read by the session tool.
DESKTOP_PREF_FILE=/etc/steamify/desktop
desktop_pref_get() {
cat "$DESKTOP_PREF_FILE" 2>/dev/null || echo last
}
desktop_pref_set() {
printf '%s\n' "$1" | sudo install -Dm644 /dev/stdin "$DESKTOP_PREF_FILE"
}
desktop_installed() {
# desktop_installed kde|gnome: its session is installed (both can be: the
# menu then offers the theme of each).
local name=plasma f
[[ "$1" == gnome ]] && name=gnome
for f in /usr/share/wayland-sessions/$name*.desktop /usr/share/xsessions/$name*.desktop; do
[[ -f "$f" ]] && return 0
done
return 1
}
+9 -2
View File
@@ -136,8 +136,15 @@ gsn_fetch_package() {
gsn_smoke_test() { gsn_smoke_test() {
# The installed gamescope must start (headless, no screen needed): a build that crashes would leave gaming mode in a loop. # The installed gamescope must start (headless, no screen needed): a build that crashes would leave gaming mode in a loop.
# It segfaults while shutting down headless (also unpatched builds), so what counts is that it ran the program. # It segfaults or hangs while shutting down headless (also unpatched builds), so what counts is that it ran the program.
timeout 25 gamescope --backend headless -W 640 -H 360 -r 60 -- true 2>&1 | grep -q 'Primary child shut down' # Into a file, not a pipe into grep -q: grep quits at the first match, gamescope then dies on the broken pipe, and with
# pipefail that made a working build count as broken. -k: a hung shutdown ignores the first signal.
local log rc=1
log="$(mktemp)" || return 1
timeout -k 3 25 gamescope --backend headless -W 640 -H 360 -r 60 -- true >"$log" 2>&1
grep -q 'Primary child shut down' "$log" && rc=0
rm -f "$log"
return "$rc"
} }
gsn_build_package() { gsn_build_package() {
+243
View File
@@ -0,0 +1,243 @@
#!/bin/bash
# SteamOS look for GNOME, the counterpart of lib/vapor-theme.sh: the Vapor
# palette from CachyOS's cachyos-vapor package (also the Steam Deck
# wallpapers), as dark mode, a blue accent, the wallpaper and libadwaita/GTK4
# colors. Settings go through gsettings and its undo journal (lib/state.sh).
# Sourced by steamify.sh; not meant to be run on its own.
GNOME_THEME_MARK="/* Steamify GNOME theme */"
GNOME_THEME_CSS="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-4.0/gtk.css"
# The Ubuntu-style look: the dock (Ubuntu Dock is Dash to Dock) and our own extension,
# Steamify Vapor, which carries the shell colors (its stylesheet.css) and starts GNOME on
# the desktop. (Desktop Icons NG and AppIndicator come with the conversion, lib/desktop-shortcut.sh.)
GNOME_THEME_EXTENSIONS=(ubuntu-dock@ubuntu.com tiling-assistant@ubuntu.com steamify-vapor@steamify.github.io)
# Ubuntu's own builds, the ones Ubuntu 26.04 ships (they are the more mature): Ubuntu Dock from its source tag
# (built here: needs make, sassc and gettext) and Tiling Assistant (Ubuntu maintains it now) from its release.
DOCK_URL="https://github.com/micheleg/dash-to-dock/archive/refs/tags/ubuntu-dock-109ubuntu2.tar.gz"
DOCK_SHA256=599057fa27d922d4730bfc27c803b1b14b05f49a20e580e154e151b8481e5d41
TILING_URL="https://github.com/ubuntu/Tiling-Assistant/releases/download/v56/tiling-assistant@leleat-on-github.shell-extension.zip"
TILING_SHA256=961da0c6b8e5960770e68a06d71205765870435dbe8fe0ccc1ec50d4cc78265a
GNOME_USER_EXTENSIONS="${XDG_DATA_HOME:-$HOME/.local/share}/gnome-shell/extensions"
GNOME_THEME_WALLPAPER="/usr/share/wallpapers/Steam Deck Logo Default.jpg"
# GNOME Settings with the "GNOME Desktop" panel (dock, desktop icons, tiling): built by GitHub Actions on current Arch
# and rebuilt weekly (steamify-gnome-theme, settings/), so it isn't compiled here (a few minutes, 800 steps).
GNOME_SETTINGS_PKG=steamify-gnome-control-center
GNOME_SETTINGS_URL="https://github.com/theupriser/steamify-gnome-theme/releases/download/settings"
gnometheme_status() {
grep -qsF "$GNOME_THEME_MARK" "$GNOME_THEME_CSS"
}
gnometheme_extensions_install() {
# Ubuntu Dock and Tiling Assistant (pinned downloads, checked) and our own Steamify Vapor, in the user's own extensions folder.
local file work dir
dir="$GNOME_USER_EXTENSIONS/ubuntu-dock@ubuntu.com"
if [[ ! -f "$dir/metadata.json" ]]; then
file="$(mktemp --suffix=.tar.gz)"; work="$(mktemp -d)"
sudo pacman -S --needed --noconfirm make sassc gettext >/dev/null 2>&1
if curl -fsSL --max-time 60 "$DOCK_URL" -o "$file" && [[ "$(sha256sum < "$file")" == "$DOCK_SHA256 -" ]] &&
tar xzf "$file" -C "$work" --strip-components=1 &&
make -C "$work" install-local INSTALLBASE="$GNOME_USER_EXTENSIONS" >/dev/null 2>&1; then
:
else
warn "Couldn't set up Ubuntu Dock; the dock stays off."
fi
rm -rf "$file" "$work"
fi
dir="$GNOME_USER_EXTENSIONS/tiling-assistant@ubuntu.com"
if [[ ! -f "$dir/metadata.json" ]]; then
file="$(mktemp --suffix=.zip)"
if curl -fsSL --max-time 60 "$TILING_URL" -o "$file" && [[ "$(sha256sum < "$file")" == "$TILING_SHA256 -" ]]; then
rm -rf "$dir"; mkdir -p "$dir"
python3 -c 'import sys,zipfile;zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])' "$file" "$dir"
# Under the name Ubuntu gives it (GNOME Settings' desktop panel looks for it).
sed -i 's|"uuid": "tiling-assistant@leleat-on-github"|"uuid": "tiling-assistant@ubuntu.com"|' "$dir/metadata.json"
glib-compile-schemas "$dir/schemas" 2>/dev/null
else
warn "Couldn't download Tiling Assistant; it stays off."
fi
rm -f "$file"
fi
# GNOME Settings' Desktop panel finds the settings of the dock and of tiling in the default schema folders.
local schemas="${XDG_DATA_HOME:-$HOME/.local/share}/glib-2.0/schemas" ext
mkdir -p "$schemas"
for ext in ubuntu-dock@ubuntu.com tiling-assistant@ubuntu.com; do
cp "$GNOME_USER_EXTENSIONS/$ext"/schemas/*.gschema.xml "$schemas/" 2>/dev/null
done
glib-compile-schemas "$schemas" 2>/dev/null
dir="$GNOME_USER_EXTENSIONS/steamify-vapor@steamify.github.io"
mkdir -p "$dir"
patch_file steamify-vapor.js > "$dir/extension.js"
patch_file steamify-vapor.json > "$dir/metadata.json"
patch_file steamify-gnome-shell.css > "$dir/stylesheet.css"
}
gnometheme_backgrounds() {
# The SteamOS wallpapers of cachyos-vapor in GNOME Settings' background list: its XML list in the user's folder.
local xml="${XDG_DATA_HOME:-$HOME/.local/share}/gnome-background-properties/steamify-steamos.xml" f name
mkdir -p "$(dirname "$xml")"
{
printf '%s\n' '<?xml version="1.0"?>' '<!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd">' '<wallpapers>'
for f in /usr/share/wallpapers/*.jpg; do
[[ -f "$f" ]] || continue
name="$(basename "$f" .jpg)"
printf ' <wallpaper deleted="false">\n <name>SteamOS: %s</name>\n <filename>%s</filename>\n <filename-dark>%s</filename-dark>\n <options>zoom</options>\n <pcolor>#24272c</pcolor>\n <scolor>#24272c</scolor>\n <shade_type>solid</shade_type>\n </wallpaper>\n' "$name" "$f" "$f"
done
echo '</wallpapers>'
} > "$xml"
}
gnometheme_shell() {
# The top bar and dash colors (the Steamify Vapor extension), and the dock beside it.
gnometheme_extensions_install
gnometheme_backgrounds
# Like Ubuntu's dock: on the left, always there; its look is Ubuntu's own, left as it is. Its shape (a
# panel to the screen edge or floating) is left to the user: Dock Settings.
local d=org.gnome.shell.extensions.dash-to-dock
gset gnometheme $d dock-position "'LEFT'" || true
gset gnometheme $d dock-fixed true || true
gset gnometheme $d running-indicator-style "'DOTS'" || true
gset gnometheme $d click-action "'minimize-or-previews'" || true
# Trash is in the dock, not on the desktop (Ubuntu does the same).
gset gnometheme org.gnome.shell.extensions.ding show-trash false || true
gset gnometheme $d show-trash true || true
local uuid enabled
for uuid in "${GNOME_THEME_EXTENSIONS[@]}"; do
enabled="$(gs get org.gnome.shell enabled-extensions 2>/dev/null)" || return 0
[[ "$enabled" == *"$uuid"* ]] && continue
if [[ "$enabled" == "@as []" || "$enabled" == "[]" ]]; then enabled="['$uuid']"
else enabled="${enabled%]}, '$uuid']"; fi
gset gnometheme org.gnome.shell enabled-extensions "$enabled"
done
}
gnometheme_extras_install() {
# The desktop icons and the tray icons of the Ubuntu-style look: Desktop Icons NG and AppIndicator (and Extension Manager
# for them), the same the SteamOS conversion needs (lib/desktop-shortcut.sh). Not fatal: the theme works without them.
gnome_extras_note_missing
sudo pacman -S --needed --noconfirm "${GNOME_EXTRA_PKGS[@]}" >/dev/null 2>&1 ||
{ warn "Couldn't install Desktop Icons NG and AppIndicator: the desktop and the tray show no icons."; return 0; }
gnome_extensions_enable
}
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" "${XDG_SESSION_DESKTOP:-gnome}"; 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.
pacman -Q "$GNOME_SETTINGS_PKG" >/dev/null 2>&1 && return 0
pacman -Q gnome-control-center >/dev/null 2>&1 || return 0
local cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify" file="$GNOME_SETTINGS_PKG-x86_64.pkg.tar.zst" want got
mkdir -p "$cache" || return 0
info "Downloading GNOME Settings with the Desktop panel..."
want="$(curl -fsL --connect-timeout 10 --max-time 30 "$GNOME_SETTINGS_URL/$file.sha256" 2>/dev/null | awk 'NR==1 {print $1}')"
if [[ -z "$want" ]] ||
! curl -fsL --retry 2 --connect-timeout 15 --max-time 300 -o "$cache/$file.part" "$GNOME_SETTINGS_URL/$file"; then
rm -f "$cache/$file.part"
warn "Couldn't download GNOME Settings with the Desktop panel: the stock GNOME Settings stays."
return 0
fi
got="$(sha256sum "$cache/$file.part" | awk '{print $1}')"
if [[ "$got" != "$want" ]]; then
rm -f "$cache/$file.part"
warn "GNOME Settings with the Desktop panel didn't match its checksum: the stock GNOME Settings stays."
return 0
fi
mv -f "$cache/$file.part" "$cache/$file"
# --ask 4: pacman may replace the stock gnome-control-center it conflicts with.
if sudo pacman -U --noconfirm --ask 4 "$cache/$file"; then
state_set gnometheme installed_settings 1
ok "GNOME Settings with the Desktop panel installed (under Appearance, after the next login)."
else
warn "Installing GNOME Settings with the Desktop panel failed (its libraries may differ from this PC's): the stock GNOME Settings stays."
fi
return 0
}
gnometheme_enable() {
if ! pacman -Q cachyos-vapor >/dev/null 2>&1; then
info "Installing cachyos-vapor (its wallpapers and colors)..."
sudo pacman -S --needed --noconfirm cachyos-vapor || { err "Installing cachyos-vapor failed."; return 1; }
# Only remove it again on disable if we were the ones installing it.
state_set gnometheme installed_pkg 1
fi
info "Applying the SteamOS look to GNOME..."
gset gnometheme org.gnome.desktop.interface color-scheme "'prefer-dark'"
gset gnometheme org.gnome.desktop.interface accent-color "'blue'" || true
# adw-gtk3 follows the light/dark style of Settings > Appearance (CachyOS's adw-gtk3-dark would pin it dark).
[[ -d /usr/share/themes/adw-gtk3 ]] && gset gnometheme org.gnome.desktop.interface gtk-theme "'adw-gtk3'"
# Windows with minimize and maximize buttons, like Ubuntu (GNOME shows only close by default).
gset gnometheme org.gnome.desktop.wm.preferences button-layout "'appmenu:minimize,maximize,close'" || true
if [[ -f "$GNOME_THEME_WALLPAPER" ]]; then
local uri="file://${GNOME_THEME_WALLPAPER// /%20}"
gset gnometheme org.gnome.desktop.background picture-uri "'$uri'"
gset gnometheme org.gnome.desktop.background picture-uri-dark "'$uri'" || true
gset gnometheme org.gnome.desktop.background picture-options "'zoom'"
fi
# GTK4/libadwaita apps ignore GTK themes: their colors come from this file.
mkdir -p "$(dirname "$GNOME_THEME_CSS")"
if [[ -f "$GNOME_THEME_CSS" ]] && ! gnometheme_status; then
cp -a "$GNOME_THEME_CSS" "$STATE_DIR/gnometheme-gtk4.css"
fi
{ echo "$GNOME_THEME_MARK"; patch_file steamify-gnome-gtk4.css; } > "$GNOME_THEME_CSS"
gnometheme_shell
gnometheme_extras_install
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() {
info "Restoring GNOME's previous look..."
grevert gnometheme
# (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"
else
gnometheme_status && rm -f "$GNOME_THEME_CSS"
fi
if [[ -n "$(state_get gnometheme installed_pkg)" ]]; then
# Fails, and stays installed, when the KDE theme or another package needs it.
sudo pacman -R --noconfirm cachyos-vapor >/dev/null 2>&1 ||
info "Keeping cachyos-vapor installed (another package needs it)."
fi
if [[ -n "$(state_get gnometheme installed_settings)" ]]; then
# The stock GNOME Settings back (it conflicts with, and so replaces, ours).
sudo pacman -S --noconfirm --ask 4 gnome-control-center >/dev/null 2>&1 ||
info "Keeping GNOME Settings with the Desktop panel (the stock one couldn't be installed)."
fi
state_clear gnometheme
# (After the state is cleared and the stylesheet is gone: the theme itself is off for gnome_extras_release's check.)
gnome_extras_release
ok "Previous GNOME look restored."
}
+99 -2
View File
@@ -4,6 +4,11 @@
# Sourced by steamify.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
BASE_CONF="/etc/plasmalogin.conf" BASE_CONF="/etc/plasmalogin.conf"
GDM_CONF="/etc/gdm/custom.conf"
GDM_FIX_DIR=/usr/local/share/steamify-gdm
GDM_HOOK=/etc/pacman.d/hooks/95-steamify-gdm.hook
GDM_SUDOERS=/etc/sudoers.d/steamify-gdm-relogin
GDM_RELOGIN=/usr/local/libexec/steamify-gdm-relogin
# The "SteamOS conversion" menu item: gaming mode boot, the Return to # The "SteamOS conversion" menu item: gaming mode boot, the Return to
# Gaming Mode shortcut and Steam on the desktop (lib/steam-desktop.sh). # Gaming Mode shortcut and Steam on the desktop (lib/steam-desktop.sh).
@@ -14,6 +19,9 @@ gaming_status() {
plasmalogin) plasmalogin)
sed -n '/^\[Autologin\]/,/^\[/p' "$BASE_CONF" 2>/dev/null | grep -qx "User=$USER" && sed -n '/^\[Autologin\]/,/^\[/p' "$BASE_CONF" 2>/dev/null | grep -qx "User=$USER" &&
[[ -f /etc/systemd/system/sync-steamos-session.path ]] ;; [[ -f /etc/systemd/system/sync-steamos-session.path ]] ;;
gdm)
sed -n '/^\[daemon\]/,/^\[/p' /etc/gdm/custom.conf 2>/dev/null | grep -qx "AutomaticLogin=$USER" &&
[[ -f "$GDM_FIX_DIR/steam-set-session" ]] ;;
*) return 1 ;; *) return 1 ;;
esac esac
} }
@@ -108,7 +116,16 @@ gaming_enable() {
configure_autologin configure_autologin
install_session_sync install_session_sync
;; ;;
gdm)
# GNOME (prototype): GDM autologin, plus CachyOS's session tools made GDM-aware.
remove_sddm_autologin
remove_session_sync
switch_to_gdm || return 1
configure_gdm_autologin || return 1
;;
esac esac
# CachyOS's own session tools only go to Plasma and only know SDDM and plasma-login-manager: with GNOME installed ours replace them.
if [[ "$LOGIN_MANAGER" == gdm ]] || desktop_installed gnome; then session_tools_install || return 1; fi
# (NVIDIA with single user mode from before the conversion: its desktop autologin would pin the Plasma session.) # (NVIDIA with single user mode from before the conversion: its desktop autologin would pin the Plasma session.)
single_login_release single_login_release
create_desktop_shortcut create_desktop_shortcut
@@ -122,8 +139,13 @@ gaming_disable() {
info "Turning off booting into gaming mode..." info "Turning off booting into gaming mode..."
remove_sddm_autologin remove_sddm_autologin
remove_session_sync remove_session_sync
restore_plasmalogin_config session_tools_remove
switch_to_plasmalogin if [[ "$(current_display_manager)" == gdm ]]; then
restore_gdm_config
else
restore_plasmalogin_config
switch_to_plasmalogin
fi
remove_desktop_shortcut remove_desktop_shortcut
steam_disable steam_disable
gsn_disable gsn_disable
@@ -131,6 +153,7 @@ gaming_disable() {
os_name_disable os_name_disable
# NVIDIA with single user mode still on: nothing else logs in by itself any more. # NVIDIA with single user mode still on: nothing else logs in by itself any more.
nvidia_present && single_status && single_login_enable nvidia_present && single_status && single_login_enable
gnome_extras_release
ok "The PC boots to the normal login screen again (from the next boot)." ok "The PC boots to the normal login screen again (from the next boot)."
} }
@@ -297,3 +320,77 @@ install_session_sync() {
sudo cat "$BASE_CONF" sudo cat "$BASE_CONF"
echo echo
} }
switch_to_gdm() {
# Already the login manager on a GNOME install; this only makes sure it is installed and enabled.
[[ "$(current_display_manager)" == gdm ]] && return 0
info "Installing and enabling GDM..."
sudo pacman -S --needed --noconfirm gdm || { err "Installing gdm failed."; return 1; }
local current
current="$(systemctl show -p Id --value display-manager 2>/dev/null)"
[[ -n "$current" && "$current" != gdm.service ]] && sudo systemctl disable "$current"
sudo systemctl enable -f gdm.service || { err "Enabling gdm failed."; return 1; }
ok "GDM is the login manager from the next boot on."
}
configure_gdm_autologin() {
# [daemon] AutomaticLogin: GDM reads it when it starts, which is why the
# session tools restart GDM to log in again (see patches/steamify-gdm-session-select.sh).
info "Configuring GDM autologin for $TARGET_USER"
sudo mkdir -p /etc/gdm
sudo touch "$GDM_CONF"
[[ -f "$GDM_CONF.bak-steamify" ]] || sudo cp -a "$GDM_CONF" "$GDM_CONF.bak-steamify"
sudo awk '
/^\[daemon\]/ { print; indaemon=1; next }
/^\[/ { indaemon=0 }
indaemon && /^(AutomaticLoginEnable|AutomaticLogin)=/ { next }
{ print }
' "$GDM_CONF" | sudo tee "$GDM_CONF.tmp" > /dev/null
grep -q '^\[daemon\]' "$GDM_CONF.tmp" || printf '[daemon]\n' | sudo tee -a "$GDM_CONF.tmp" > /dev/null
sudo sed -i "/^\[daemon\]/a AutomaticLoginEnable=True\nAutomaticLogin=$TARGET_USER" "$GDM_CONF.tmp"
sudo mv "$GDM_CONF.tmp" "$GDM_CONF"
ok "GDM autologin configured."
}
restore_gdm_config() {
if [[ -f "$GDM_CONF.bak-steamify" ]]; then
sudo cp -a "$GDM_CONF.bak-steamify" "$GDM_CONF" && sudo rm -f "$GDM_CONF.bak-steamify"
else
sudo sed -i '/^AutomaticLoginEnable=/d;/^AutomaticLogin=/d' "$GDM_CONF" 2>/dev/null
fi
}
session_tools_install() {
# CachyOS's steam-set-session and steamos-session-select only know
# plasma-login-manager/SDDM and KDE. Ours replace them at the same paths
# (Steam's Switch to Desktop calls them by path); a pacman hook puts them
# back after every update of gamescope-session-cachyos.
info "Making the session switching GDM-aware..."
sudo mkdir -p "$GDM_FIX_DIR"
patch_file steamify-gdm-steam-set-session.sh | sudo install -Dm755 /dev/stdin "$GDM_FIX_DIR/steam-set-session" &&
patch_file steamify-gdm-session-select.sh |
sudo install -Dm755 /dev/stdin "$GDM_FIX_DIR/steamos-session-select" || { err "Installing the GDM session tools failed."; return 1; }
patch_file steamify-gdm.hook | fill DIR="$GDM_FIX_DIR" | sudo install -Dm644 /dev/stdin "$GDM_HOOK"
if [[ "$(current_display_manager)" == gdm || "$LOGIN_MANAGER" == gdm ]]; then
# steamos-session-select logs in again through this helper (no password: it runs from the gaming session and from a shortcut).
patch_file steamify-gdm-relogin.sh | sudo install -Dm755 /dev/stdin "$GDM_RELOGIN"
echo "$TARGET_USER ALL=(root) NOPASSWD: $GDM_RELOGIN" | sudo install -Dm440 /dev/stdin "$GDM_SUDOERS"
sudo visudo -cf "$GDM_SUDOERS" > /dev/null || { sudo rm -f "$GDM_SUDOERS"; err "The sudoers rule for GDM is invalid."; return 1; }
fi
for f in /usr/lib/steamos/steam-set-session /usr/bin/steamos-session-select; do
[[ -f "$f" && ! -f "$f.bak-steamify" ]] && sudo cp -a "$f" "$f.bak-steamify"
done
sudo install -m755 "$GDM_FIX_DIR/steam-set-session" /usr/lib/steamos/steam-set-session &&
sudo install -m755 "$GDM_FIX_DIR/steamos-session-select" /usr/bin/steamos-session-select || return 1
# Start in gamescope, as with the other login managers.
sudo /usr/lib/steamos/steam-set-session gamescope-session.desktop
ok "GDM autologin into gamescope is set up."
}
session_tools_remove() {
sudo rm -f "$GDM_HOOK" "$GDM_SUDOERS" "$GDM_RELOGIN"
for f in /usr/lib/steamos/steam-set-session /usr/bin/steamos-session-select; do
[[ -f "$f.bak-steamify" ]] && sudo mv "$f.bak-steamify" "$f"
done
sudo rm -rf "$GDM_FIX_DIR"
}
+8 -4
View File
@@ -5,7 +5,7 @@
# Menu order. Components are turned on in this order and off in reverse; # Menu order. Components are turned on in this order and off in reverse;
# gaming must come first (single user builds on it). # gaming must come first (single user builds on it).
COMPONENTS=(gaming boot silent glyphs nvidia bigpicture nvsilent theme single launcher steamgame notify extended_controller_support vram cec machine poweroff kpin hdmi bios) COMPONENTS=(gaming boot silent glyphs nvidia bigpicture nvsilent theme gnometheme single launcher steamgame notify extended_controller_support vram cec machine poweroff kpin hdmi bios)
# One-off actions rather than on/off components: never preselected, never # One-off actions rather than on/off components: never preselected, never
# re-applied, not listed as on or off. # re-applied, not listed as on or off.
ACTIONS=(bios) ACTIONS=(bios)
@@ -26,7 +26,7 @@ NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
# FEATURE_BASELINE. # FEATURE_BASELINE.
FEATURE_BASELINE=2.1.0 FEATURE_BASELINE=2.1.0
declare -A FEATURE_VERSION=( declare -A FEATURE_VERSION=(
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.12.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0 [gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.12.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [gnometheme]=2.13.0 [glyphs]=2.1.0 [single]=2.1.0
[launcher]=2.1.0 [cec]=2.7.0 [machine]=2.9.0 [poweroff]=2.2.0 [vram]=2.3.0 [notify]=2.5.0 [steamgame]=2.5.1 [extended_controller_support]=2.10.0 [launcher]=2.1.0 [cec]=2.7.0 [machine]=2.9.0 [poweroff]=2.2.0 [vram]=2.3.0 [notify]=2.5.0 [steamgame]=2.5.1 [extended_controller_support]=2.10.0
[kpin]=2.1.0 [hdmi]=2.1.0 [kpin]=2.1.0 [hdmi]=2.1.0
) )
@@ -39,6 +39,7 @@ declare -A LABEL=(
[nvsilent]="Start Steam silently: in the tray at login, no window (untick: Steam's normal window)" [nvsilent]="Start Steam silently: in the tray at login, no window (untick: Steam's normal window)"
[bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)" [bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)"
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)" [theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
[gnometheme]="Install SteamOS theme for GNOME: Vapor look (dark, blue accent, Steam Deck wallpaper)"
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode" [glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
[single]="Single user mode: no password, lock screen or log out (SDDM)" [single]="Single user mode: no password, lock screen or log out (SDDM)"
[launcher]="Steamify shortcut: the app on the desktop, Steamify Terminal in the launcher" [launcher]="Steamify shortcut: the app on the desktop, Steamify Terminal in the launcher"
@@ -60,6 +61,10 @@ component_available() {
# The SteamOS conversion is offered on NVIDIA PCs too: it installs the patched gamescope there (lib/gamescope-nvidia.sh) # The SteamOS conversion is offered on NVIDIA PCs too: it installs the patched gamescope there (lib/gamescope-nvidia.sh)
# and turns off "Gaming on NVIDIA" (lib/nvidia.sh), which is not offered any more (nvidia_available). # and turns off "Gaming on NVIDIA" (lib/nvidia.sh), which is not offered any more (nvidia_available).
nvidia|bigpicture|nvsilent) nvidia_available ;; nvidia|bigpicture|nvsilent) nvidia_available ;;
# Plasma's Vapor theme and single user mode (KDE Wallet, screen locker, Plasma's menu): when Plasma is installed.
# The GNOME theme: when GNOME is. With both installed, each shows.
theme|single) desktop_installed kde ;;
gnometheme) desktop_installed gnome ;;
machine|poweroff) machine_available ;; machine|poweroff) machine_available ;;
vram) vram_available ;; vram) vram_available ;;
# Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs; # Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs;
@@ -503,8 +508,7 @@ plan_changes() {
apply_changes() { apply_changes() {
local c failed=() local c failed=()
LOGIN_MANAGER=plasmalogin choose_login_manager
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
for c in "${TO_DISABLE[@]}"; do for c in "${TO_DISABLE[@]}"; do
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}" if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
+7 -3
View File
@@ -84,12 +84,16 @@ python_hid_shim_ensure() {
install_required_packages() { install_required_packages() {
# Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses. # Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses.
local -a MISSING local -a MISSING required=(gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei)
info "Checking required packages: gamescope-session-cachyos, steam, mangohud, xterm, ttf-liberation, wqy-zenhei, plasma-keyboard" # Plasma's on-screen keyboard; on GNOME kconfig instead, for the kreadconfig6/kwriteconfig6 Steamify's undo journal uses.
mapfile -t MISSING < <(pacman -T gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard) if [[ "$(desktop_kind)" == gnome ]]; then required+=(kconfig accountsservice gnome-shell-extension-desktop-icons-ng gnome-shell-extension-appindicator extension-manager); else required+=(plasma-keyboard); fi
info "Checking required packages: ${required[*]}"
mapfile -t MISSING < <(pacman -T "${required[@]}")
if [[ ${#MISSING[@]} -gt 0 ]]; then if [[ ${#MISSING[@]} -gt 0 ]]; then
python_hid_shim_ensure || return 1 python_hid_shim_ensure || return 1
# (What the GNOME extensions need is noted first: turning everything off removes what Steamify installed.)
[[ "$(desktop_kind)" == gnome ]] && gnome_extras_note_missing
info "Installing missing packages: ${MISSING[*]}" info "Installing missing packages: ${MISSING[*]}"
sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; return 1; } sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; return 1; }
else else
+34
View File
@@ -59,6 +59,40 @@ krevert() {
rm -f "$journal" rm -f "$journal"
} }
gs() {
# gsettings, also without a session (the installer's chroot): then in a
# session bus of its own, which writes the user's dconf database.
if user_session; then gsettings "$@"; else dbus-run-session -- gsettings "$@"; fi
}
gset() {
# gset <component> <schema> <key> <value>: gsettings (GNOME), recording the
# old value in the component's journal like kset does. <value> is in
# GVariant text form ('prefer-dark', true, 5). A key the schema lacks
# (newer GNOME only) is skipped, returning 1.
local component="$1" schema="$2" key="$3" value="$4"
local journal="$STATE_DIR/$component.gjournal" old id
gs get "$schema" "$key" >/dev/null 2>&1 || return 1
mkdir -p "$STATE_DIR"
id="$(printf '%s\t%s' "$schema" "$key")"
if ! grep -qxF -- "$id" <(cut -f1-2 "$journal" 2>/dev/null); then
old="$(gs get "$schema" "$key")"
printf '%s\t%s\n' "$id" "$(printf '%s' "$old" | base64 -w0)" >> "$journal"
fi
gs set "$schema" "$key" "$value"
}
grevert() {
# grevert <component>: put back every gsettings key the component changed.
local journal="$STATE_DIR/$1.gjournal" schema key old
[[ -f "$journal" ]] || return 0
while IFS=$'\t' read -r schema key old; do
old="$(printf '%s' "$old" | base64 -d)"
gs set "$schema" "$key" "$old" 2>/dev/null || gs reset "$schema" "$key" 2>/dev/null
done < <(tac "$journal")
rm -f "$journal"
}
has_journal() { has_journal() {
[[ -s "$STATE_DIR/$1.journal" ]] [[ -s "$STATE_DIR/$1.journal" ]]
} }
+2 -2
View File
@@ -9,8 +9,8 @@ steam_enable() {
steam_env_dir="$HOME/.config/environment.d" steam_env_dir="$HOME/.config/environment.d"
mkdir -p "$steam_env_dir" mkdir -p "$steam_env_dir"
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay # 2. Prevent KDE Wayland from blocking the virtual keyboard overlay (KWin only)
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf" [[ "$(desktop_kind)" == kde ]] && echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
ok "Steam UI parameters and keyboard fix deployed successfully." ok "Steam UI parameters and keyboard fix deployed successfully."
} }
+6
View File
@@ -30,11 +30,16 @@ notify_enable() {
info "Adding update notifications..." info "Adding update notifications..."
# PySide6 for the tray icon, notify-send for the notification's buttons. # PySide6 for the tray icon, notify-send for the notification's buttons.
python3 -c 'import PySide6.QtWidgets' 2>/dev/null || missing+=(pyside6) 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) command -v notify-send >/dev/null || missing+=(libnotify)
if ((${#missing[@]})); then if ((${#missing[@]})); then
sudo pacman -S --needed --noconfirm "${missing[@]}" || sudo pacman -S --needed --noconfirm "${missing[@]}" ||
{ err "Installing ${missing[*]} failed."; return 1; } { err "Installing ${missing[*]} failed."; return 1; }
fi fi
# The single-color icon for GNOME's top bar tray.
patch_file steamify-symbolic.svg | install -D -m 644 /dev/stdin \
"${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/symbolic/apps/steamify-symbolic.svg"
patch_file steamify-notifier.py | install_executable "$NOTIFY_SCRIPT" 755 || patch_file steamify-notifier.py | install_executable "$NOTIFY_SCRIPT" 755 ||
{ err "Couldn't write $NOTIFY_SCRIPT."; return 1; } { err "Couldn't write $NOTIFY_SCRIPT."; return 1; }
mkdir -p "$NOTIFY_UNITS" mkdir -p "$NOTIFY_UNITS"
@@ -51,6 +56,7 @@ notify_enable() {
notify_disable() { notify_disable() {
user_systemctl disable --now "$NOTIFY_NAME.timer" "$NOTIFY_NAME.service" >/dev/null 2>&1 user_systemctl disable --now "$NOTIFY_NAME.timer" "$NOTIFY_NAME.service" >/dev/null 2>&1
rm -f "$NOTIFY_UNITS/$NOTIFY_NAME.service" "$NOTIFY_UNITS/$NOTIFY_NAME.timer" "$NOTIFY_SCRIPT" rm -f "$NOTIFY_UNITS/$NOTIFY_NAME.service" "$NOTIFY_UNITS/$NOTIFY_NAME.timer" "$NOTIFY_SCRIPT"
rm -f "${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/symbolic/apps/steamify-symbolic.svg"
rmdir "$STEAMIFY_BIN" 2>/dev/null || true rmdir "$STEAMIFY_BIN" 2>/dev/null || true
user_systemctl daemon-reload user_systemctl daemon-reload
state_clear notify state_clear notify
+1 -1
View File
@@ -157,7 +157,7 @@ theme_disable() {
restart_plasmashell_if_stopped restart_plasmashell_if_stopped
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
if [[ -n "$(state_get theme installed_pkg)" ]]; then if [[ -n "$(state_get theme installed_pkg)" ]] && ! gnometheme_status; then
# Fails, and stays installed, when another package (e.g. # Fails, and stays installed, when another package (e.g.
# cachyos-handheld) depends on it. # cachyos-handheld) depends on it.
sudo pacman -R --noconfirm cachyos-vapor >/dev/null 2>&1 || sudo pacman -R --noconfirm cachyos-vapor >/dev/null 2>&1 ||
+46 -12
View File
@@ -14,14 +14,28 @@ WIZARD_ICON="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps/st
WIZARD_LAUNCHER="$STEAMIFY_BIN/run-wizard" WIZARD_LAUNCHER="$STEAMIFY_BIN/run-wizard"
WIZARD_APP_LAUNCHER="$STEAMIFY_BIN/run-app" WIZARD_APP_LAUNCHER="$STEAMIFY_BIN/run-app"
WIZARD_APPS="${XDG_DATA_HOME:-$HOME/.local/share}/applications" WIZARD_APPS="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
# steamify-ui: the id the app gives itself, so its window belongs to this # io.github.theupriser.Steamify: the id the app gives itself (reverse-DNS: GNOME matches windows only by such ids), so its window belongs to this
# entry. steamify-app.sh leaves an entry with X-Steamify-Shortcut alone. # entry. steamify-app.sh leaves an entry with X-Steamify-Shortcut alone.
WIZARD_DESKTOP_NAME="steamify-ui.desktop" WIZARD_DESKTOP_NAME="io.github.theupriser.Steamify.desktop"
# Until 2.12.0 the id was steamify-ui.
WIZARD_ID_BEFORE_ENTRY="steamify-ui.desktop"
WIZARD_APP_ENTRY="$WIZARD_APPS/$WIZARD_DESKTOP_NAME" WIZARD_APP_ENTRY="$WIZARD_APPS/$WIZARD_DESKTOP_NAME"
WIZARD_TERMINAL_ENTRY="$WIZARD_APPS/steamify-terminal.desktop" WIZARD_TERMINAL_ENTRY="$WIZARD_APPS/steamify-terminal.desktop"
# Before 2.0.1 the shortcut opened the terminal menu under this name. # Before 2.0.1 the shortcut opened the terminal menu under this name.
WIZARD_OLD_NAME="cachyos-gamescope-boot-wizard.desktop" WIZARD_OLD_NAME="cachyos-gamescope-boot-wizard.desktop"
wizard_terminal() {
# The command prefix that runs a program in a terminal window: Konsole on
# Plasma, on GNOME whichever of Ptyxis, Console or GNOME Terminal is there.
if [[ "$(desktop_kind)" == gnome ]]; then
if command -v ptyxis >/dev/null; then echo "ptyxis --"
elif command -v kgx >/dev/null; then echo "kgx -e"
else echo "gnome-terminal --"; fi
else
echo "konsole -e"
fi
}
wizard_desktop_dir() { wizard_desktop_dir() {
xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop" xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop"
} }
@@ -44,9 +58,16 @@ launcher_status() {
# A shortcut from before 2.0.1 (terminal only): the menu ticks the item, so a # A shortcut from before 2.0.1 (terminal only): the menu ticks the item, so a
# normal run replaces it. # normal run replaces it.
launcher_repair() { [[ -f "$WIZARD_APPS/$WIZARD_OLD_NAME" ]] && ! launcher_status; } launcher_repair() {
# Also the shortcut from before the reverse-DNS app id (its entry carries the marker): the menu ticks it, a normal run replaces it.
! launcher_status || return 1
[[ -f "$WIZARD_APPS/$WIZARD_OLD_NAME" ]] ||
grep -q '^X-Steamify-Shortcut=true' "$WIZARD_APPS/$WIZARD_ID_BEFORE_ENTRY" 2>/dev/null
}
launcher_enable() { launcher_enable() {
local terminal
terminal="$(wizard_terminal)"
info "Adding the Steamify shortcut (the app on the desktop and in the launcher, Steamify Terminal in the launcher)..." info "Adding the Steamify shortcut (the app on the desktop and in the launcher, Steamify Terminal in the launcher)..."
install_executable "$WIZARD_LAUNCHER" 755 << EOF install_executable "$WIZARD_LAUNCHER" 755 << EOF
#!/bin/bash #!/bin/bash
@@ -70,18 +91,18 @@ echo
EOF EOF
# The app, without a terminal. Only the first time, when PySide6 still # The app, without a terminal. Only the first time, when PySide6 still
# has to be installed, it runs in Konsole: sudo asks for the password there. # has to be installed, it runs in a terminal: sudo asks for the password there.
install_executable "$WIZARD_APP_LAUNCHER" 755 << EOF install_executable "$WIZARD_APP_LAUNCHER" 755 << EOF
#!/bin/bash #!/bin/bash
# Installed by Steamify CachyOS: start the newest release of its app. # Installed by Steamify CachyOS: start the newest release of its app.
set -o pipefail 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 konsole -e "\$0" --in-terminal exec $terminal "\$0" --in-terminal
fi fi
if ! curl -fsSL --max-time 30 "$WIZARD_APP_URL" | bash; then if ! curl -fsSL --max-time 30 "$WIZARD_APP_URL" | bash; then
msg="Couldn't download or start Steamify. Are you connected to the internet?" msg="Couldn't download or start Steamify. Are you connected to the internet?"
if [[ "\${1:-}" == --in-terminal ]]; then echo; echo "\$msg"; read -rp "Press Enter to close this window... " _ if [[ "\${1:-}" == --in-terminal ]]; then echo; echo "\$msg"; read -rp "Press Enter to close this window... " _
else kdialog --title Steamify --sorry "\$msg" 2>/dev/null; fi else kdialog --title Steamify --sorry "\$msg" 2>/dev/null || zenity --error --title Steamify --text "\$msg" 2>/dev/null; fi
exit 1 exit 1
fi fi
EOF EOF
@@ -89,9 +110,17 @@ EOF
# The icon comes with the release; without it, Steam's own icon. # The icon comes with the release; without it, Steam's own icon.
local icon=steam tmp local icon=steam tmp
tmp="$(mktemp)" 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" install -D -m 644 "$tmp" "$WIZARD_ICON"
icon=steamify # 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"
gtk-update-icon-cache -q -f -t "${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor" 2>/dev/null || true gtk-update-icon-cache -q -f -t "${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor" 2>/dev/null || true
else else
warn "Couldn't download the shortcut's icon; using Steam's icon instead." warn "Couldn't download the shortcut's icon; using Steam's icon instead."
@@ -107,19 +136,23 @@ Exec=$WIZARD_APP_LAUNCHER
Icon=$icon Icon=$icon
Terminal=false Terminal=false
Categories=Game;System;Settings; Categories=Game;System;Settings;
StartupWMClass=io.github.theupriser.Steamify
X-Steamify-Shortcut=true" X-Steamify-Shortcut=true"
printf '%s\n' "$entry" | install_executable "$WIZARD_APP_ENTRY" 644 printf '%s\n' "$entry" | install_executable "$WIZARD_APP_ENTRY" 644
printf '%s\n' "$entry" | install_executable "$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME" 755 printf '%s\n' "$entry" | install_executable "$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME" 755
# GNOME only starts desktop launchers it trusts.
gio set "$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME" metadata::trusted true 2>/dev/null || true
printf '%s\n' "[Desktop Entry] printf '%s\n' "[Desktop Entry]
Type=Application Type=Application
Name=Steamify Terminal Name=Steamify Terminal
Comment=Steamify CachyOS's menu in a terminal Comment=Steamify CachyOS's menu in a terminal
Exec=konsole -e $WIZARD_LAUNCHER Exec=$terminal $WIZARD_LAUNCHER
Icon=$icon Icon=$icon
Terminal=false Terminal=false
Categories=Game;System;Settings;" | install_executable "$WIZARD_TERMINAL_ENTRY" 644 Categories=Game;System;Settings;" | install_executable "$WIZARD_TERMINAL_ENTRY" 644
# The terminal-only shortcut from before 2.0.1. # The terminal-only shortcut from before 2.0.1, and the app's entry under its id before the reverse-DNS one.
rm -f "$(wizard_desktop_dir)/$WIZARD_OLD_NAME" "$WIZARD_APPS/$WIZARD_OLD_NAME" rm -f "$(wizard_desktop_dir)/$WIZARD_OLD_NAME" "$WIZARD_APPS/$WIZARD_OLD_NAME" \
"$(wizard_desktop_dir)/$WIZARD_ID_BEFORE_ENTRY" "$WIZARD_APPS/$WIZARD_ID_BEFORE_ENTRY"
kbuildsycoca6 >/dev/null 2>&1 || true kbuildsycoca6 >/dev/null 2>&1 || true
ok "Steamify shortcut added: \"Steamify CachyOS\" (the app) on the desktop and in the launcher, \"Steamify Terminal\" in the launcher." ok "Steamify shortcut added: \"Steamify CachyOS\" (the app) on the desktop and in the launcher, \"Steamify Terminal\" in the launcher."
} }
@@ -130,6 +163,7 @@ launcher_disable() {
grep -q '^X-Steamify-Shortcut=true' "$WIZARD_APP_ENTRY" 2>/dev/null && rm -f "$WIZARD_APP_ENTRY" grep -q '^X-Steamify-Shortcut=true' "$WIZARD_APP_ENTRY" 2>/dev/null && rm -f "$WIZARD_APP_ENTRY"
rm -f "$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME" "$WIZARD_TERMINAL_ENTRY" \ rm -f "$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME" "$WIZARD_TERMINAL_ENTRY" \
"$(wizard_desktop_dir)/$WIZARD_OLD_NAME" "$WIZARD_APPS/$WIZARD_OLD_NAME" \ "$(wizard_desktop_dir)/$WIZARD_OLD_NAME" "$WIZARD_APPS/$WIZARD_OLD_NAME" \
"$(wizard_desktop_dir)/$WIZARD_ID_BEFORE_ENTRY" "$WIZARD_APPS/$WIZARD_ID_BEFORE_ENTRY" \
"$WIZARD_LAUNCHER" "$WIZARD_APP_LAUNCHER" "$WIZARD_ICON" "$WIZARD_LAUNCHER" "$WIZARD_APP_LAUNCHER" "$WIZARD_ICON"
rmdir "$(dirname "$WIZARD_LAUNCHER")" 2>/dev/null || true rmdir "$(dirname "$WIZARD_LAUNCHER")" 2>/dev/null || true
kbuildsycoca6 >/dev/null 2>&1 || true kbuildsycoca6 >/dev/null 2>&1 || true
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
# Log the autologin user in again, into the session steam-set-session picked
# (Steamify prototype, GDM). Run as root through sudo, from inside the session
# that is being left. GDM logs in by itself only when the daemon starts, and
# a new session refuses to start while the old user manager still runs
# ("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" "${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.
systemctl stop gdm.service
loginctl terminate-user "$USER_NAME"
# (not `loginctl show-user`: it still lists the user about 10 s after it is gone)
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
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
# steamos-session-select for GNOME (Steamify prototype): CachyOS's own ends the
# session with a KDE-only D-Bus call and goes to Plasma only. GDM logs in by itself only when it
# starts, so after the new session is set the user is ended and GDM restarted
# (patches/steamify-gdm-relogin.sh): it then logs the autologin user straight into it.
set -e
DM_ID="$(systemctl show -p Id --value display-manager)"
DM_ID="${DM_ID%.service}"
relogin() {
# Out of the running session into the one that was set.
case "$DM_ID" in
gdm) sudo -n /usr/local/libexec/steamify-gdm-relogin ;;
*)
# sddm and plasma-login-manager log in again by themselves (Relogin=true): just end the session.
if [[ "${XDG_CURRENT_DESKTOP:-}" == *GNOME* ]]; then gnome-session-quit --no-prompt --logout
else qdbus6 org.kde.Shutdown /Shutdown org.kde.Shutdown.logout; fi ;;
esac
}
case "$1" in
persistent)
systemctl --user mask cachyos-gamescope-autologin.service
pkexec /usr/lib/steamos/steam-set-session desktop ;;
oneshot)
systemctl --user unmask cachyos-gamescope-autologin.service
pkexec /usr/lib/steamos/steam-set-session gamescope-session.desktop ;;
gamescope)
pkexec /usr/lib/steamos/steam-set-session gamescope-session.desktop
relogin ;;
desktop|plasma|gnome)
# desktop: the preferred one; plasma or gnome: that one.
case "$1" in plasma) target=plasma.desktop ;; gnome) target=gnome.desktop ;; *) target=desktop ;; esac
pkexec /usr/lib/steamos/steam-set-session "$target"
steam -shutdown || true
systemctl --user stop gamescope-session.target || true
# Under GDM nothing logs in again by itself after the session ends.
[[ "$DM_ID" == gdm ]] && relogin ;;
*)
[[ -n "$1" ]] && echo -e "Unknown session '$1'\n"
echo "Usage: $(basename "$0") persistent|oneshot|gamescope|desktop"
exit 1 ;;
esac
if [[ -e /dev/input/.hidden ]]; then
pkexec mv /dev/input/.hidden/* /dev/input
pkexec rm -r /dev/input/.hidden
fi
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# steam-set-session with GDM support (Steamify prototype). CachyOS's own only
# knows plasma-login-manager and SDDM and quietly does nothing on GDM. GDM
# has no autologin fragment: it takes the user's session from
# AccountsService, so that is where the session goes.
set -e
DM_ID="$(systemctl show -p Id --value display-manager)"
DM_ID="${DM_ID%.service}"
SESSION="${1:-gamescope-session.desktop}"
# "desktop" is the desktop to go back to: the one chosen in Steamify (/etc/steamify/desktop: kde, gnome or last),
# else the one used last (recorded below when leaving it for gamescope), else whichever is installed.
have_session() { [[ -f "/usr/share/wayland-sessions/$1" || -f "/usr/share/xsessions/$1" ]]; }
resolve_desktop() {
local pref last
pref="$(cat /etc/steamify/desktop 2>/dev/null || true)"
last="$(cat /var/lib/steamify/last-desktop 2>/dev/null || true)"
[[ -z "$pref" || "$pref" == last ]] && pref="$last"
case "$pref" in
gnome) have_session gnome.desktop && { echo gnome.desktop; return; } ;;
kde) have_session plasma.desktop && { echo plasma.desktop; return; } ;;
esac
have_session plasma.desktop && { echo plasma.desktop; return; }
echo gnome.desktop
}
remember_desktop() {
# Leaving a desktop for gamescope: which desktop it was, from the shell the user is running.
local uid="${PKEXEC_UID:-}" kind
[[ -n "$uid" ]] || return 0
if pgrep -u "$uid" -x gnome-shell >/dev/null; then kind=gnome
elif pgrep -u "$uid" -x plasmashell >/dev/null; then kind=kde
else return 0; fi
mkdir -p /var/lib/steamify && echo "$kind" > /var/lib/steamify/last-desktop
}
[[ "$SESSION" == desktop ]] && SESSION="$(resolve_desktop)"
[[ "$SESSION" == gamescope-session* ]] && remember_desktop
case "$DM_ID" in
plasmalogin) CONF_FILE="/etc/plasmalogin.conf.d/zz-steamos-autologin.conf" ;;
sddm) CONF_FILE="/etc/sddm.conf.d/zz-steamos-autologin.conf" ;;
gdm)
if [[ -n "${PKEXEC_UID:-}" ]]; then TARGET_USER="$(id -un "$PKEXEC_UID")"
else TARGET_USER="${SUDO_USER:-}"; fi
# (|| true: logname fails without a login, and set -e would end the script before the fallback.)
[[ -n "$TARGET_USER" ]] || TARGET_USER="$(logname 2>/dev/null || true)"
# At boot (steamify-boot-desktop.service) nobody is logged in: the autologin user.
[[ -n "$TARGET_USER" ]] || TARGET_USER="$(sed -n 's/^AutomaticLogin=//p' /etc/gdm/custom.conf 2>/dev/null)"
[[ -n "$TARGET_USER" ]] || exit 0
ACCOUNT="/var/lib/AccountsService/users/$TARGET_USER"
SESSION_ID="${SESSION%.desktop}"
mkdir -p /var/lib/AccountsService/users
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=$SESSION_ID|" "$ACCOUNT"
else sed -i "/^\[User\]/a $key=$SESSION_ID" "$ACCOUNT"; fi
done
systemctl restart accounts-daemon 2>/dev/null || true
exit 0 ;;
esac
# Any other login manager: nothing to do.
[[ -n "${CONF_FILE:-}" ]] || exit 0
mkdir -p "$(dirname "$CONF_FILE")"
printf '[Autologin]\nSession=%s\n' "$SESSION" > "$CONF_FILE"
+10
View File
@@ -0,0 +1,10 @@
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = gamescope-session-cachyos
[Action]
Description = Steamify: GDM support for gamescope-session-cachyos...
When = PostTransaction
Exec = /bin/sh -c 'install -m755 @DIR@/steam-set-session /usr/lib/steamos/steam-set-session && install -m755 @DIR@/steamos-session-select /usr/bin/steamos-session-select'
+29
View File
@@ -0,0 +1,29 @@
/* Steamify Vapor: libadwaita/GTK4 colors. In the dark style: Vapor's palette (cachyos-vapor, Vapor.colors);
* in the light style (Settings > Appearance): libadwaita's own light colors, with Vapor's blue accent. */
@define-color accent_bg_color #2a8fd0;
@define-color accent_fg_color #ffffff;
@define-color accent_color #1b6ea7;
@media (prefers-color-scheme: dark) {
@define-color accent_bg_color #569fcc;
@define-color accent_fg_color #1f1f26;
@define-color accent_color #3daee9;
@define-color window_bg_color #24272c;
@define-color window_fg_color #eff0f1;
@define-color view_bg_color #151c23;
@define-color view_fg_color #eff0f1;
@define-color headerbar_bg_color #24272c;
@define-color headerbar_fg_color #eff0f1;
@define-color card_bg_color #2a2e32;
@define-color popover_bg_color #2a2e32;
@define-color sidebar_bg_color #1f2328;
@define-color dialog_bg_color #2a2e32;
}
/* Menus without the arrow pointing at the cursor (like Ubuntu's). */
popover.menu > arrow {
min-width: 0;
min-height: 0;
background: none;
border: none;
box-shadow: none;
}
+44
View File
@@ -0,0 +1,44 @@
/* Steamify Vapor: the SteamOS look for GNOME Shell, loaded by the Steamify Vapor
* extension on top of the stock theme: the palette of the Vapor theme (KDE Plasma's SteamOS look, cachyos-vapor). */
/* Top bar: Vapor's panel color. */
#panel,
#panel.lock-screen,
#panel.unlock-screen {
background-color: #24272c;
}
#panel .panel-button { color: #eff0f1; }
#panel .panel-button:hover,
#panel .panel-button:focus,
#panel .panel-button:active,
#panel .panel-button:checked,
#panel .panel-button:overview {
background-color: rgba(86, 159, 204, 0.35);
color: #eff0f1;
}
/* Popups and the calendar (the dock keeps Ubuntu's own look). */
.popup-menu-content,
.candidate-popup-content,
.message-list-clear-button {
background-color: #24272c;
}
.popup-menu-item:hover,
.popup-menu-item:focus,
.popup-menu-item:active {
background-color: rgba(86, 159, 204, 0.35);
}
/* Selection and focus in Vapor's blue. */
.app-well-app:focus .overview-icon,
.app-well-app:selected .overview-icon,
.overview-tile:focus,
.overview-tile:selected,
.search-entry:focus {
background-color: rgba(86, 159, 204, 0.3);
border-color: #3daee9;
}
.button:focus,
.check-box:focus StBin { border-color: #3daee9; }
.slider { color: #3daee9; }
.workspace-thumbnail-indicator { border-color: #3daee9; }
+34 -8
View File
@@ -8,6 +8,7 @@
import configparser import configparser
import json import json
import os import os
import shutil
import subprocess import subprocess
import sys import sys
import threading import threading
@@ -56,10 +57,35 @@ def pending(new):
def desktop(): def desktop():
# Only on the Plasma desktop: gamescope doesn't show notifications or a # Only on the Plasma or GNOME desktop: gamescope doesn't show notifications
# tray, so an update found in gaming mode waits for the next desktop login. # or a tray, so an update found in gaming mode waits for the next desktop login.
return subprocess.run(["pgrep", "-u", str(os.getuid()), "-x", "plasmashell"], return any(subprocess.run(["pgrep", "-u", str(os.getuid()), "-x", shell],
stdout=subprocess.DEVNULL).returncode == 0 stdout=subprocess.DEVNULL).returncode == 0
for shell in ("plasmashell", "gnome-shell"))
def terminal():
# The command prefix that runs a program in a terminal window.
for program, prefix in (("konsole", ["konsole", "-e"]), ("ptyxis", ["ptyxis", "--"]),
("kgx", ["kgx", "-e"]), ("gnome-terminal", ["gnome-terminal", "--"])):
if shutil.which(program):
return prefix
return ["xterm", "-e"]
def tray_icon():
# Steamify's icon: the single-color one in GNOME's top bar (it draws its tray icons that way),
# the colored one elsewhere; Steam's own only when neither is installed.
from PySide6.QtGui import QIcon
icons = os.path.join(DATA, "icons", "hicolor")
names = ["symbolic/apps/steamify-symbolic.svg", "scalable/apps/steamify.svg"]
if "GNOME" not in os.environ.get("XDG_CURRENT_DESKTOP", ""):
names.reverse()
for name in names:
path = os.path.join(icons, name)
if os.path.exists(path):
return QIcon(path)
return QIcon.fromTheme("steamify", QIcon.fromTheme("steam"))
def main(): def main():
@@ -81,9 +107,9 @@ def main():
app = QApplication(sys.argv[:1]) app = QApplication(sys.argv[:1])
app.setApplicationName("Steamify") app.setApplicationName("Steamify")
app.setDesktopFileName("steamify-ui") app.setDesktopFileName("io.github.theupriser.Steamify")
app.setQuitOnLastWindowClosed(False) app.setQuitOnLastWindowClosed(False)
icon = QIcon.fromTheme("steamify", QIcon.fromTheme("steam")) icon = tray_icon()
def open_app(): def open_app():
# The way Steamify was last used (the app or the terminal menu), the # The way Steamify was last used (the app or the terminal menu), the
@@ -91,8 +117,8 @@ def main():
# ends the reminder. In its own unit: this one's processes are # ends the reminder. In its own unit: this one's processes are
# stopped when the check exits. # stopped when the check exits.
if state("frontend") == "terminal": if state("frontend") == "terminal":
cmd = ["konsole", "-e", RUN_TERMINAL] if os.access(RUN_TERMINAL, os.X_OK) else \ cmd = [*terminal(), RUN_TERMINAL] if os.access(RUN_TERMINAL, os.X_OK) else \
["konsole", "-e", "bash", "-c", [*terminal(), "bash", "-c",
f"set -o pipefail; curl -fsSL --max-time 30 {RELEASE}/steamify.sh | bash; read -rp 'Press Enter to close. ' _"] f"set -o pipefail; curl -fsSL --max-time 30 {RELEASE}/steamify.sh | bash; read -rp 'Press Enter to close. ' _"]
else: else:
cmd = [RUN_APP] if os.access(RUN_APP, os.X_OK) else \ cmd = [RUN_APP] if os.access(RUN_APP, os.X_OK) else \
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><g fill="#bebebe"><path d="m 26.586,23.6632 8.104641,-0.4201 1.91027,-6.7552 -5.59099,-4.4419 -6.871493,5.8329 z"/><path d="M 31.027515,23.9946 35,23 l 1.976562,-2.9766 -4,-4 -4.949047,4.9712 z"/><path d="M 15.316486,35.7061 21.675457,35.065 23.0333,27.5143 16.403747,25.7462 12.603749,30.3195 Z"/><path d="m 16.985332,35.8717 3.972485,-0.4165 2.210937,-3.4453 -3.0625,-3.1563 -6.933422,3.9868 z"/><path d="m 30.5,11 c -4.142135,0 -7.499999,3.357864 -7.5,7.5 0.0017,0.201484 0.01143,0.402819 0.0293,0.603516 -1.289636,1.575722 -4.80544,5.546997 -5.113281,5.902343 -2.416761,0.03284 -4.577852,1.51258 -5.482422,3.753907 -1.237432,3.074241 0.2518,6.569519 3.326172,7.80664 3.074245,1.237417 6.56952,-0.251813 7.80664,-3.326172 0.274389,-0.686886 0.419405,-1.418587 0.427735,-2.158203 l 5.896484,-5.111328 C 30.093269,25.98873 30.296562,25.998503 30.5,26 34.642135,26 37.999999,22.642136 38,18.5 37.999999,14.357864 34.642135,11 30.5,11 Z m 0,3 c 2.48528,0 4.499999,2.014718 4.5,4.5 -10e-7,2.485282 -2.01472,4.5 -4.5,4.5 -2.48528,0 -4.499999,-2.014718 -4.5,-4.5 1e-6,-2.485282 2.01472,-4.5 4.5,-4.5 z M 18.046875,27 c 0.496175,0.0058 0.986949,0.103774 1.447266,0.289062 2.049689,0.825003 3.042263,3.155585 2.216797,5.205079 -0.825003,2.049668 -3.155579,3.04224 -5.205079,2.216797 C 14.45617,33.885935 13.463596,31.555353 14.289062,29.505859 14.905127,27.975627 16.397393,26.980525 18.046875,27 Z"/><circle cx="30.5" cy="18.4998" r="3.5"/><path d="M 5.0722656,22.564453 A 19,19 0 0 0 5,24 19,19 0 0 0 5.7695312,29.3125 l 11.1113278,4.470703 a 3,3 0 0 0 3.902344,-1.664062 3,3 0 0 0 -1.664062,-3.902344 z"/><path d="M 40.440,33.688 L 42.527,33.983 L 42.527,36.007 L 40.440,36.302 L 39.770,37.921 L 41.037,39.606 L 39.606,41.037 L 37.921,39.770 L 36.302,40.440 L 36.007,42.527 L 33.983,42.527 L 33.688,40.440 L 32.069,39.770 L 30.384,41.037 L 28.953,39.606 L 30.220,37.921 L 29.550,36.302 L 27.463,36.007 L 27.463,33.983 L 29.550,33.688 L 30.220,32.069 L 28.953,30.384 L 30.384,28.953 L 32.069,30.220 L 33.688,29.550 L 33.983,27.463 L 36.007,27.463 L 36.302,29.550 L 37.921,30.220 L 39.606,28.953 L 41.037,30.384 L 39.770,32.069 Z M 37.395,34.995 A 2.4,2.4 0 1 0 32.595,34.995 A 2.4,2.4 0 1 0 37.395,34.995 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+28
View File
@@ -0,0 +1,28 @@
// Steamify Vapor (Steamify GNOME theme). Its stylesheet.css, which GNOME Shell loads
// by itself, is the SteamOS look. This code: GNOME opens the overview after login;
// Ubuntu starts on the desktop. So does this: the overview stays closed while the
// shell starts up, and is available again right after.
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
export default class SteamifyDesktopStart extends Extension {
enable() {
if (!Main.layoutManager._startingUp)
return;
this._hadOverview = Main.sessionMode.hasOverview;
Main.sessionMode.hasOverview = false;
this._startupId = Main.layoutManager.connect('startup-complete', () => {
Main.sessionMode.hasOverview = this._hadOverview;
Main.overview.hide();
});
}
disable() {
if (this._startupId) {
Main.layoutManager.disconnect(this._startupId);
this._startupId = null;
}
if (this._hadOverview !== undefined)
Main.sessionMode.hasOverview = this._hadOverview;
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"uuid": "steamify-vapor@steamify.github.io",
"name": "Steamify Vapor",
"description": "The SteamOS (Vapor) look for the shell, and log in to the desktop, not the overview.",
"shell-version": [
"45",
"46",
"47",
"48",
"49",
"50",
"51"
]
}
+2 -2
View File
@@ -1,10 +1,10 @@
[Unit] [Unit]
Description=Steamify CachyOS: boot into the desktop instead of gaming mode Description=Steamify CachyOS: boot into the desktop instead of gaming mode
Before=display-manager.service sddm.service plasmalogin.service Before=display-manager.service sddm.service plasmalogin.service gdm.service
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=/usr/lib/steamos/steam-set-session plasma.desktop ExecStart=/usr/lib/steamos/steam-set-session @DESKTOP_SESSION@
ExecStart=/bin/sh -c '[ -x @SESSION_SYNC@ ] && exec @SESSION_SYNC@ || true' ExecStart=/bin/sh -c '[ -x @SESSION_SYNC@ ] && exec @SESSION_SYNC@ || true'
[Install] [Install]
+12 -2
View File
@@ -15,11 +15,11 @@
set -uo pipefail set -uo pipefail
# Release version, see CHANGELOG.md. # Release version, see CHANGELOG.md.
VERSION=2.12.0 VERSION=2.13.0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia gamescope-nvidia controllers cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do for lib in common state desktop packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia gamescope-nvidia controllers cec boot-session vapor-theme gnome-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
# shellcheck source=/dev/null # shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh" source "$SCRIPT_DIR/lib/$lib.sh"
done done
@@ -137,6 +137,16 @@ if [[ "$LIST" == true ]]; then
exit 0 exit 0
fi fi
# --desktop kde|gnome|last: which desktop "Switch to Desktop" goes to (when both are installed).
if [[ "${1:-}" == --desktop ]]; then
case "${2:-}" in
kde|gnome|last) ;;
*) err "--desktop takes kde, gnome or last (the one you used last)"; exit 1 ;;
esac
desktop_pref_set "$2" || exit 1
ok "Switch to Desktop goes to: $2."
exit 0
fi
if [[ "$DEFAULTS" == true ]]; then if [[ "$DEFAULTS" == true ]]; then
RESTART_FOR_LOGIN=false RESTART_FOR_LOGIN=false
REAPPLY=false REAPPLY=false
+4 -2
View File
@@ -18,6 +18,7 @@ Item {
property var steps: ({}) // id -> "wait"|"run"|"ok"|"fail" property var steps: ({}) // id -> "wait"|"run"|"ok"|"fail"
property var failed: [] property var failed: []
property bool restartNeeded: false 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" // Review & apply only when something would change; "Re-apply what's on"
// is for when nothing did. // is for when nothing did.
readonly property bool canApply: computePlan().length > 0 readonly property bool canApply: computePlan().length > 0
@@ -123,7 +124,7 @@ Item {
} }
// --- Password and apply --- // --- 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) { function startApply(password) {
resetRun(); resetRun();
var s = {}; var s = {};
@@ -203,7 +204,7 @@ Item {
// A successful BIOS check ends with bios-ready instead, keeping // A successful BIOS check ends with bios-ready instead, keeping
// the password for the flash. // the password for the flash.
app.sessionPassword = ""; 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.runError = app.errorText(ev.error);
app.screen = "done"; app.screen = "done";
} }
@@ -249,6 +250,7 @@ Item {
else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; } else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; }
} else if (screen === "done") { } else if (screen === "done") {
if (a === "accept" && restartNeeded) backend.restart(); if (a === "accept" && restartNeeded) backend.restart();
else if (a === "accept" && reloginNeeded) backend.restartGnome();
else if (a === "back" || a === "accept") backToMenu(); else if (a === "back" || a === "accept") backToMenu();
} }
} }
+19 -2
View File
@@ -7,9 +7,24 @@ import QtQuick.Controls.Basic
Item { Item {
required property AppState app required property AppState app
readonly property bool problem: app.failed.length > 0 || app.runError !== "" 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: { readonly property string message: {
if (app.runError) return app.runError; 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." : "")
+ (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."; 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. ") 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."; + "Keep the power on and don't touch the machine until it has fully started again, even if the screen stays black.";
@@ -33,6 +48,8 @@ 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 } } } 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 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 { 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: 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 } }
} }
} }
+34 -1
View File
@@ -4,6 +4,8 @@ import QtQuick
Rectangle { Rectangle {
required property AppState app required property AppState app
required property string iconUrl required property string iconUrl
// Our own window frame (GNOME): drag the header, double-click to maximize, and the buttons at the right.
property bool customFrame: false
readonly property var titles: ({ review: "Review", password: "Password", applying: app.biosRun ? "BIOS update" : "Applying", readonly property var titles: ({ review: "Review", password: "Password", applying: app.biosRun ? "BIOS update" : "Applying",
done: "Done", bios1: "BIOS update", bios2: "BIOS update" }) done: "Done", bios1: "BIOS update", bios2: "BIOS update" })
width: parent.width; height: 72; color: Theme.bar width: parent.width; height: 72; color: Theme.bar
@@ -18,8 +20,39 @@ Rectangle {
} }
Rectangle { Rectangle {
visible: app.status.steamMachine === true visible: app.status.steamMachine === true
anchors.right: parent.right; anchors.rightMargin: 40; anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right; anchors.rightMargin: customFrame ? 190 : 40; anchors.verticalCenter: parent.verticalCenter
height: 34; radius: 17; color: "#1b2330"; width: mt.implicitWidth + 28 height: 34; radius: 17; color: "#1b2330"; width: mt.implicitWidth + 28
Text { id: mt; anchors.centerIn: parent; text: "Steam Machine · CachyOS"; color: Theme.label; font.family: Theme.body; font.pixelSize: 14 } Text { id: mt; anchors.centerIn: parent; text: "Steam Machine · CachyOS"; color: Theme.label; font.family: Theme.body; font.pixelSize: 14 }
} }
// Moving the window (and maximizing by double-click) is the part of the header left of the buttons.
Item {
visible: customFrame
anchors { left: parent.left; top: parent.top; bottom: parent.bottom; right: controls.left }
DragHandler { target: null; onActiveChanged: if (active) Window.window.startSystemMove() }
TapHandler { onDoubleTapped: Window.window.visibility === Window.Maximized ? Window.window.showNormal() : Window.window.showMaximized() }
}
Row {
id: controls
visible: customFrame
anchors.right: parent.right; anchors.rightMargin: 16; anchors.verticalCenter: parent.verticalCenter; spacing: 6
Repeater {
model: [ { g: "\u2013", a: "min" }, { g: "\u25a1", a: "max" }, { g: "\u2715", a: "close" } ]
Rectangle {
required property var modelData
width: 44; height: 44; radius: 22
color: area.containsMouse ? (modelData.a === "close" ? "#c0392b" : Theme.line) : "transparent"
Text { anchors.centerIn: parent; text: modelData.g; color: Theme.text; font.family: Theme.body; font.pixelSize: 20 }
MouseArea {
id: area
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
onClicked: {
var w = Window.window
if (modelData.a === "min") w.showMinimized()
else if (modelData.a === "max") { if (w.visibility === Window.Maximized) w.showNormal(); else w.showMaximized() }
else Qt.quit()
}
}
}
}
}
} }
+24 -2
View File
@@ -11,12 +11,15 @@ ApplicationWindow {
required property var backend required property var backend
required property var gamepad required property var gamepad
required property bool fullscreen required property bool fullscreen
// The app draws the window frame (GNOME): the header carries the buttons, the edges resize.
required property bool customFrame
required property string iconUrl required property string iconUrl
required property bool fromSteam required property bool fromSteam
Component.onCompleted: Input.fromSteam = fromSteam Component.onCompleted: Input.fromSteam = fromSteam
width: 1280; height: 720 width: 1280; height: 720
minimumWidth: 960; minimumHeight: 540 minimumWidth: 960; minimumHeight: 540
flags: customFrame ? (Qt.Window | Qt.FramelessWindowHint) : Qt.Window
visibility: fullscreen ? Window.FullScreen : Window.Windowed visibility: fullscreen ? Window.FullScreen : Window.Windowed
title: "Steamify" title: "Steamify"
color: Theme.bg color: Theme.bg
@@ -33,7 +36,11 @@ ApplicationWindow {
// own shape, so the bars reach the edges and the content fills it. // own shape, so the bars reach the edges and the content fills it.
Item { Item {
id: stage id: stage
readonly property real k: Math.min(win.width / 1280, win.height / 720) // 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 width: win.width / k; height: win.height / k
transformOrigin: Item.TopLeft transformOrigin: Item.TopLeft
scale: k scale: k
@@ -41,7 +48,7 @@ ApplicationWindow {
Keys.onPressed: function (e) { steamify.keyPressed(e); } Keys.onPressed: function (e) { steamify.keyPressed(e); }
Keys.onReleased: function (e) { steamify.keyReleased(e); } Keys.onReleased: function (e) { steamify.keyReleased(e); }
Header { id: header; app: steamify; iconUrl: win.iconUrl } Header { id: header; app: steamify; iconUrl: win.iconUrl; customFrame: win.customFrame }
Item { Item {
id: content id: content
@@ -55,4 +62,19 @@ ApplicationWindow {
DoneScreen { anchors.fill: parent; app: steamify; visible: steamify.screen === "done" } DoneScreen { anchors.fill: parent; app: steamify; visible: steamify.screen === "done" }
} }
} }
// Resize handles around a window with our own frame.
component Edge: MouseArea {
required property int edges
hoverEnabled: true
onPressed: win.startSystemResize(edges)
}
Edge { visible: win.customFrame; edges: Qt.LeftEdge; width: 6; anchors { left: parent.left; top: parent.top; bottom: parent.bottom; topMargin: 8; bottomMargin: 8 } cursorShape: Qt.SizeHorCursor }
Edge { visible: win.customFrame; edges: Qt.RightEdge; width: 6; anchors { right: parent.right; top: parent.top; bottom: parent.bottom; topMargin: 8; bottomMargin: 8 } cursorShape: Qt.SizeHorCursor }
Edge { visible: win.customFrame; edges: Qt.TopEdge; height: 6; anchors { top: parent.top; left: parent.left; right: parent.right; leftMargin: 8; rightMargin: 8 } cursorShape: Qt.SizeVerCursor }
Edge { visible: win.customFrame; edges: Qt.BottomEdge; height: 6; anchors { bottom: parent.bottom; left: parent.left; right: parent.right; leftMargin: 8; rightMargin: 8 } cursorShape: Qt.SizeVerCursor }
Edge { visible: win.customFrame; edges: Qt.LeftEdge | Qt.TopEdge; width: 8; height: 8; anchors { left: parent.left; top: parent.top } cursorShape: Qt.SizeFDiagCursor }
Edge { visible: win.customFrame; edges: Qt.RightEdge | Qt.TopEdge; width: 8; height: 8; anchors { right: parent.right; top: parent.top } cursorShape: Qt.SizeBDiagCursor }
Edge { visible: win.customFrame; edges: Qt.LeftEdge | Qt.BottomEdge; width: 8; height: 8; anchors { left: parent.left; bottom: parent.bottom } cursorShape: Qt.SizeBDiagCursor }
Edge { visible: win.customFrame; edges: Qt.RightEdge | Qt.BottomEdge; width: 8; height: 8; anchors { right: parent.right; bottom: parent.bottom } cursorShape: Qt.SizeFDiagCursor }
} }
+9 -3
View File
@@ -31,7 +31,9 @@ Item {
color: row.selected ? Theme.cardSel : Theme.card color: row.selected ? Theme.cardSel : Theme.card
border.width: row.selected ? 2 : 0; border.color: Theme.accent border.width: row.selected ? 2 : 0; border.color: Theme.accent
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor
onClicked: { Input.type = "keyboard"; if (row.selected) app.act("accept"); else app.sel = row.rowIndex; } } // 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 } Text { visible: !!row.it.parent; x: 20; anchors.verticalCenter: parent.verticalCenter; text: "└"; color: "#56627a"; font.pixelSize: 18 }
Column { Column {
@@ -67,14 +69,18 @@ Item {
Rectangle { id: sw; visible: row.it.kind === "toggle"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter 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" width: 46; height: 26; radius: 13; color: row.on ? Theme.accent : "#343f50"
Behavior on color { ColorAnimation { duration: 120 } } 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 { 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 } 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) // action (Update BIOS)
Rectangle { visible: row.it.kind === "action"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter 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 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 { 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"); } } }
} }
} }
} }
+3
View File
@@ -25,6 +25,9 @@ QtObject {
theme: { label: "SteamOS theme", hint: "Vapor look for the desktop, Add to Steam", theme: { label: "SteamOS theme", hint: "Vapor look for the desktop, Add to Steam",
body: "The Vapor look of SteamOS for the desktop, with its panel, launcher icon and wallpaper.", body: "The Vapor look of SteamOS for the desktop, with its panel, launcher icon and wallpaper.",
changes: ["cachyos-vapor global theme", "Add to Steam in right-click menus", "Nested Desktop, Steam keyboard window rule"] }, changes: ["cachyos-vapor global theme", "Add to Steam in right-click menus", "Nested Desktop, Steam keyboard window rule"] },
gnometheme: { label: "SteamOS theme for GNOME", hint: "Vapor look, Ubuntu-style dock and desktop icons",
body: "The Vapor look of SteamOS for GNOME: dark or light style with Vapor's blue, the Steam Deck wallpapers in Settings, the top bar in Vapor's panel color, and the extensions Ubuntu uses (Ubuntu Dock, desktop icons, tray icons, tiling).",
changes: ["Colors for GTK4 apps and the shell (a stylesheet extension)", "Ubuntu Dock, Tiling Assistant, Desktop Icons NG, AppIndicator", "SteamOS wallpapers in GNOME Settings", "Window buttons with minimize and maximize", "Off: your settings and files are put back"] },
glyphs: { label: "Steam Deck/Machine icons", hint: "Deck button icons in gaming mode", glyphs: { label: "Steam Deck/Machine icons", hint: "Deck button icons in gaming mode",
body: "Steam shows Steam Deck button icons in gaming mode.", changes: ["STEAM_GAMEPADUI_ARGS -steamos3"] }, body: "Steam shows Steam Deck button icons in gaming mode.", changes: ["STEAM_GAMEPADUI_ARGS -steamos3"] },
single: { label: "Single user mode", hint: "No password, lock screen or wallet prompts", single: { label: "Single user mode", hint: "No password, lock screen or wallet prompts",
+23 -6
View File
@@ -47,26 +47,43 @@ rm -rf "$DEST.new"; mv "$tmp/app" "$DEST.new"
rm -rf "$DEST"; mv "$DEST.new" "$DEST" rm -rf "$DEST"; mv "$DEST.new" "$DEST"
say "Installed to $DEST ($(grep -m1 -oP '^VERSION=\K.*' "$DEST/steamify.sh"))." say "Installed to $DEST ($(grep -m1 -oP '^VERSION=\K.*' "$DEST/steamify.sh"))."
if ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then missing=()
say "The app needs PySide6 (Qt for Python); installing it..." 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. # 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 fi
# Launcher entry (its id also names the app to the desktop portal). The # Launcher entry (its id also names the app to the desktop portal). The
# Steamify shortcut's entry has the same id and always starts the newest # Steamify shortcut's entry has the same id and always starts the newest
# release: left alone. # release: left alone.
mkdir -p "$APPS" mkdir -p "$APPS"
grep -q '^X-Steamify-Shortcut=true' "$APPS/steamify-ui.desktop" 2>/dev/null || rm -f "$APPS/steamify-ui.desktop" # its name until 2.12.0
cat > "$APPS/steamify-ui.desktop" << EOF # 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] [Desktop Entry]
Type=Application Type=Application
Name=Steamify CachyOS Name=Steamify CachyOS
Comment=Turn this PC into a SteamOS-style console Comment=Turn this PC into a SteamOS-style console
Exec=$DEST/ui/steamify-ui Exec=$DEST/ui/steamify-ui
Icon=$DEST/assets/steam-gaming-settings.svg Icon=$ICON
Terminal=false Terminal=false
Categories=Game;Settings; Categories=Game;Settings;
StartupWMClass=io.github.theupriser.Steamify
EOF EOF
command -v update-desktop-database >/dev/null && update-desktop-database "$APPS" 2>/dev/null || true command -v update-desktop-database >/dev/null && update-desktop-database "$APPS" 2>/dev/null || true
+19 -1
View File
@@ -345,6 +345,18 @@ class Backend(QObject):
def restart(self): def restart(self):
QProcess.startDetached("systemctl", ["reboot"]) 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"
# 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"])
def shutdown(self): def shutdown(self):
if self._proc and self._proc.state() != QProcess.NotRunning: if self._proc and self._proc.state() != QProcess.NotRunning:
self._proc.kill() self._proc.kill()
@@ -362,8 +374,13 @@ def main():
fullscreen = "--fullscreen" in sys.argv or (in_gamescope and "--windowed" not in sys.argv) fullscreen = "--fullscreen" in sys.argv or (in_gamescope and "--windowed" not in sys.argv)
app = QGuiApplication(sys.argv) app = QGuiApplication(sys.argv)
# GNOME draws no frame around a Qt window (Qt's own is light grey, whatever the style): the app draws
# its own, in its colors. Other desktops keep theirs (KWin's follows the Plasma colors).
custom_frame = (not fullscreen and app.platformName() == "wayland"
and "GNOME" in os.environ.get("XDG_CURRENT_DESKTOP", "").upper())
app.setApplicationName("Steamify") app.setApplicationName("Steamify")
app.setDesktopFileName("steamify-ui") # A reverse-DNS id: GNOME on Wayland only matches a window to its launcher (icon in the dock) by such an id.
app.setDesktopFileName("io.github.theupriser.Steamify")
icon = os.path.join(HERE, "..", "assets", "steam-gaming-settings.svg") icon = os.path.join(HERE, "..", "assets", "steam-gaming-settings.svg")
if os.path.isfile(icon): if os.path.isfile(icon):
app.setWindowIcon(QIcon(icon)) app.setWindowIcon(QIcon(icon))
@@ -373,6 +390,7 @@ def main():
engine = QQmlApplicationEngine() engine = QQmlApplicationEngine()
engine.setInitialProperties({"backend": backend, "gamepad": pad, engine.setInitialProperties({"backend": backend, "gamepad": pad,
"fullscreen": fullscreen, "fullscreen": fullscreen,
"customFrame": custom_frame,
# Started from Steam: its keys are a Steam # Started from Steam: its keys are a Steam
# Controller's buttons (desktop layout). # Controller's buttons (desktop layout).
"fromSteam": bool(os.environ.get("SteamGameId")), "fromSteam": bool(os.environ.get("SteamGameId")),