mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-15 21:15:24 +02:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b625ff98eb | ||
|
|
b9d14fa4de | ||
|
|
ad3b07097f | ||
|
|
e942f3264d | ||
|
|
0bb5708769 | ||
|
|
0c46b209b9 | ||
|
|
2905b45ef7 | ||
|
|
645b3314be | ||
|
|
fd61d84017 | ||
|
|
2ca288de39 | ||
|
|
dbaeaa14c9 | ||
|
|
7a84b0dbca | ||
|
|
0e0bf57ca8 | ||
|
|
68342e7a24 | ||
|
|
09ef0b1c71 | ||
|
|
8432f634ef | ||
|
|
4221ae6e21 | ||
|
|
eaa3f28fdc | ||
|
|
ab3b581f45 | ||
|
|
ce1aa1bf7d | ||
|
|
79048338bf | ||
|
|
c7f5f515a3 | ||
|
|
12eb56220a | ||
|
|
039f2691d8 | ||
|
|
62a30e6095 | ||
|
|
c5b59d9438 | ||
|
|
2b84e6afa6 | ||
|
|
ae12c1bb41 | ||
|
|
fc28ea4c64 | ||
|
|
5dfc916b97 |
No files matched your search
@@ -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
|
||||
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
|
||||
`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
|
||||
Machine runs at 1.75).
|
||||
- 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
|
||||
restart on EOF: `ask_yn` would take its default).
|
||||
- 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
|
||||
only while PySide6 is missing, for sudo); "Steamify Terminal" (launcher)
|
||||
runs `steamify.sh` in Konsole. Both always the newest release. The entry
|
||||
|
||||
@@ -5,6 +5,16 @@ All notable changes, per version and per commit. Versions follow
|
||||
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
|
||||
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)**
|
||||
|
||||
- **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**
|
||||
|
||||
## 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**
|
||||
|
||||
@@ -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
@@ -178,8 +178,7 @@ backend_apply() {
|
||||
|
||||
backend_sudo || return 1
|
||||
|
||||
LOGIN_MANAGER=plasmalogin
|
||||
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||
choose_login_manager
|
||||
local -a failed=()
|
||||
for c in "${TO_DISABLE[@]}"; do
|
||||
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||
@@ -196,7 +195,10 @@ backend_apply() {
|
||||
feature_record_unticked
|
||||
os_version_refresh
|
||||
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true
|
||||
backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false)"
|
||||
# (Only when the theme itself worked: a failed one has nothing to log in again for.)
|
||||
local relogin=false
|
||||
[[ " ${failed[*]:-} " != *" gnometheme "* ]] && gnometheme_relogin_needed "${TO_DISABLE[@]}" "${TO_ENABLE[@]}" && relogin=true
|
||||
backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false),\"relogin\":$relogin"
|
||||
}
|
||||
|
||||
backend_main() {
|
||||
|
||||
+6
-1
@@ -17,13 +17,18 @@ boot_status() {
|
||||
[[ -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() {
|
||||
info "Setting the PC to boot into the desktop..."
|
||||
# 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
|
||||
# on its path unit, which could still be pending when the login manager
|
||||
# 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 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)."
|
||||
|
||||
+59
-2
@@ -23,7 +23,10 @@ create_desktop_shortcut() {
|
||||
# the shortcut can't sudo for interactively (Terminal=false), so it gets
|
||||
# a password-less rule scoped to exactly those two commands.
|
||||
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'"
|
||||
local sudoers_tmp
|
||||
sudoers_tmp="$(mktemp)"
|
||||
@@ -40,6 +43,9 @@ EOF
|
||||
else
|
||||
sudo rm -f /etc/sudoers.d/gamescope-session-switch
|
||||
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
|
||||
# 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
|
||||
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."
|
||||
}
|
||||
@@ -81,6 +95,49 @@ set_shortcut_icon() {
|
||||
remove_desktop_shortcut() {
|
||||
local desktop_dir
|
||||
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
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -136,8 +136,15 @@ gsn_fetch_package() {
|
||||
|
||||
gsn_smoke_test() {
|
||||
# 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.
|
||||
timeout 25 gamescope --backend headless -W 640 -H 360 -r 60 -- true 2>&1 | grep -q 'Primary child shut down'
|
||||
# It segfaults or hangs while shutting down headless (also unpatched builds), so what counts is that it ran the program.
|
||||
# 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() {
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
#!/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_relogin_needed() {
|
||||
# A GNOME session is running and this run changed the theme: GNOME Shell on Wayland can't be restarted in place, so
|
||||
# the new extensions, the dock and the Settings panel show after the user logs in again.
|
||||
[[ " $* " == *" gnometheme "* ]] && pgrep -u "$(id -u)" -x gnome-shell >/dev/null 2>&1
|
||||
}
|
||||
|
||||
gnometheme_relogin() {
|
||||
# End the GNOME session. With GDM autologin set up by the conversion, steamify-gdm-relogin logs the user straight back
|
||||
# into the session they were in (GDM takes it from AccountsService; the session is not changed here, unlike
|
||||
# steamos-session-select, which would make GNOME the one gaming mode's boot goes back to). Without that helper there
|
||||
# is no autologin: a plain logout, and the user logs in again.
|
||||
local helper=/usr/local/libexec/steamify-gdm-relogin
|
||||
if [[ -x "$helper" ]] && sudo -n "$helper"; then return 0; fi
|
||||
gnome-session-quit --logout --no-prompt
|
||||
}
|
||||
|
||||
gnometheme_settings_install() {
|
||||
# The prebuilt GNOME Settings with the Desktop panel, replacing the stock one. Never fatal: the theme works without the
|
||||
# panel, so a failed download or install keeps the stock Settings and says so.
|
||||
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_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"
|
||||
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
|
||||
ok "Previous GNOME look restored."
|
||||
}
|
||||
@@ -4,6 +4,11 @@
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
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
|
||||
# Gaming Mode shortcut and Steam on the desktop (lib/steam-desktop.sh).
|
||||
@@ -14,6 +19,9 @@ gaming_status() {
|
||||
plasmalogin)
|
||||
sed -n '/^\[Autologin\]/,/^\[/p' "$BASE_CONF" 2>/dev/null | grep -qx "User=$USER" &&
|
||||
[[ -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 ;;
|
||||
esac
|
||||
}
|
||||
@@ -108,7 +116,16 @@ gaming_enable() {
|
||||
configure_autologin
|
||||
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
|
||||
# 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.)
|
||||
single_login_release
|
||||
create_desktop_shortcut
|
||||
@@ -122,8 +139,13 @@ gaming_disable() {
|
||||
info "Turning off booting into gaming mode..."
|
||||
remove_sddm_autologin
|
||||
remove_session_sync
|
||||
session_tools_remove
|
||||
if [[ "$(current_display_manager)" == gdm ]]; then
|
||||
restore_gdm_config
|
||||
else
|
||||
restore_plasmalogin_config
|
||||
switch_to_plasmalogin
|
||||
fi
|
||||
remove_desktop_shortcut
|
||||
steam_disable
|
||||
gsn_disable
|
||||
@@ -297,3 +319,77 @@ install_session_sync() {
|
||||
sudo cat "$BASE_CONF"
|
||||
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
@@ -5,7 +5,7 @@
|
||||
|
||||
# Menu order. Components are turned on in this order and off in reverse;
|
||||
# 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
|
||||
# re-applied, not listed as on or off.
|
||||
ACTIONS=(bios)
|
||||
@@ -26,7 +26,7 @@ NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
|
||||
# FEATURE_BASELINE.
|
||||
FEATURE_BASELINE=2.1.0
|
||||
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
|
||||
[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)"
|
||||
[bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)"
|
||||
[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"
|
||||
[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"
|
||||
@@ -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)
|
||||
# and turns off "Gaming on NVIDIA" (lib/nvidia.sh), which is not offered any more (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 ;;
|
||||
vram) vram_available ;;
|
||||
# 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() {
|
||||
local c failed=()
|
||||
LOGIN_MANAGER=plasmalogin
|
||||
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||
choose_login_manager
|
||||
|
||||
for c in "${TO_DISABLE[@]}"; do
|
||||
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
|
||||
|
||||
+5
-3
@@ -84,9 +84,11 @@ python_hid_shim_ensure() {
|
||||
|
||||
install_required_packages() {
|
||||
# Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses.
|
||||
local -a MISSING
|
||||
info "Checking required packages: gamescope-session-cachyos, steam, mangohud, xterm, ttf-liberation, wqy-zenhei, plasma-keyboard"
|
||||
mapfile -t MISSING < <(pacman -T gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard)
|
||||
local -a MISSING required=(gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei)
|
||||
# Plasma's on-screen keyboard; on GNOME kconfig instead, for the kreadconfig6/kwriteconfig6 Steamify's undo journal uses.
|
||||
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
|
||||
python_hid_shim_ensure || return 1
|
||||
|
||||
@@ -59,6 +59,40 @@ krevert() {
|
||||
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() {
|
||||
[[ -s "$STATE_DIR/$1.journal" ]]
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ steam_enable() {
|
||||
steam_env_dir="$HOME/.config/environment.d"
|
||||
mkdir -p "$steam_env_dir"
|
||||
|
||||
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay
|
||||
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
|
||||
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay (KWin only)
|
||||
[[ "$(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."
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ notify_enable() {
|
||||
sudo pacman -S --needed --noconfirm "${missing[@]}" ||
|
||||
{ err "Installing ${missing[*]} failed."; return 1; }
|
||||
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 ||
|
||||
{ err "Couldn't write $NOTIFY_SCRIPT."; return 1; }
|
||||
mkdir -p "$NOTIFY_UNITS"
|
||||
@@ -51,6 +54,7 @@ notify_enable() {
|
||||
notify_disable() {
|
||||
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 "${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/symbolic/apps/steamify-symbolic.svg"
|
||||
rmdir "$STEAMIFY_BIN" 2>/dev/null || true
|
||||
user_systemctl daemon-reload
|
||||
state_clear notify
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ theme_disable() {
|
||||
restart_plasmashell_if_stopped
|
||||
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.
|
||||
# cachyos-handheld) depends on it.
|
||||
sudo pacman -R --noconfirm cachyos-vapor >/dev/null 2>&1 ||
|
||||
|
||||
+37
-10
@@ -14,14 +14,28 @@ WIZARD_ICON="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps/st
|
||||
WIZARD_LAUNCHER="$STEAMIFY_BIN/run-wizard"
|
||||
WIZARD_APP_LAUNCHER="$STEAMIFY_BIN/run-app"
|
||||
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.
|
||||
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_TERMINAL_ENTRY="$WIZARD_APPS/steamify-terminal.desktop"
|
||||
# Before 2.0.1 the shortcut opened the terminal menu under this name.
|
||||
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() {
|
||||
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
|
||||
# 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() {
|
||||
local terminal
|
||||
terminal="$(wizard_terminal)"
|
||||
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
|
||||
#!/bin/bash
|
||||
@@ -70,18 +91,18 @@ echo
|
||||
EOF
|
||||
|
||||
# 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
|
||||
#!/bin/bash
|
||||
# Installed by Steamify CachyOS: start the newest release of its app.
|
||||
set -o pipefail
|
||||
if [[ "\${1:-}" != --in-terminal ]] && ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then
|
||||
exec konsole -e "\$0" --in-terminal
|
||||
exec $terminal "\$0" --in-terminal
|
||||
fi
|
||||
if ! curl -fsSL --max-time 30 "$WIZARD_APP_URL" | bash; then
|
||||
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... " _
|
||||
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
|
||||
fi
|
||||
EOF
|
||||
@@ -91,7 +112,8 @@ EOF
|
||||
tmp="$(mktemp)"
|
||||
if 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"
|
||||
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
|
||||
else
|
||||
warn "Couldn't download the shortcut's icon; using Steam's icon instead."
|
||||
@@ -107,19 +129,23 @@ Exec=$WIZARD_APP_LAUNCHER
|
||||
Icon=$icon
|
||||
Terminal=false
|
||||
Categories=Game;System;Settings;
|
||||
StartupWMClass=io.github.theupriser.Steamify
|
||||
X-Steamify-Shortcut=true"
|
||||
printf '%s\n' "$entry" | install_executable "$WIZARD_APP_ENTRY" 644
|
||||
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]
|
||||
Type=Application
|
||||
Name=Steamify Terminal
|
||||
Comment=Steamify CachyOS's menu in a terminal
|
||||
Exec=konsole -e $WIZARD_LAUNCHER
|
||||
Exec=$terminal $WIZARD_LAUNCHER
|
||||
Icon=$icon
|
||||
Terminal=false
|
||||
Categories=Game;System;Settings;" | install_executable "$WIZARD_TERMINAL_ENTRY" 644
|
||||
# The terminal-only shortcut from before 2.0.1.
|
||||
rm -f "$(wizard_desktop_dir)/$WIZARD_OLD_NAME" "$WIZARD_APPS/$WIZARD_OLD_NAME"
|
||||
# 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" \
|
||||
"$(wizard_desktop_dir)/$WIZARD_ID_BEFORE_ENTRY" "$WIZARD_APPS/$WIZARD_ID_BEFORE_ENTRY"
|
||||
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."
|
||||
}
|
||||
@@ -130,6 +156,7 @@ launcher_disable() {
|
||||
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" \
|
||||
"$(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"
|
||||
rmdir "$(dirname "$WIZARD_LAUNCHER")" 2>/dev/null || true
|
||||
kbuildsycoca6 >/dev/null 2>&1 || true
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/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.
|
||||
if [[ "$1" != --work ]]; then
|
||||
[[ -n "${SUDO_USER:-}" ]] || exit 1
|
||||
exec systemd-run --no-block --collect --unit=steamify-gdm-relogin "$0" --work "$SUDO_USER"
|
||||
fi
|
||||
USER_NAME="$2"
|
||||
# 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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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'
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -8,6 +8,7 @@
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
@@ -56,10 +57,35 @@ def pending(new):
|
||||
|
||||
|
||||
def desktop():
|
||||
# Only on the Plasma desktop: gamescope doesn't show notifications 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"],
|
||||
# Only on the Plasma or GNOME desktop: gamescope doesn't show notifications
|
||||
# or a tray, so an update found in gaming mode waits for the next desktop login.
|
||||
return any(subprocess.run(["pgrep", "-u", str(os.getuid()), "-x", shell],
|
||||
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():
|
||||
@@ -81,9 +107,9 @@ def main():
|
||||
|
||||
app = QApplication(sys.argv[:1])
|
||||
app.setApplicationName("Steamify")
|
||||
app.setDesktopFileName("steamify-ui")
|
||||
app.setDesktopFileName("io.github.theupriser.Steamify")
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
icon = QIcon.fromTheme("steamify", QIcon.fromTheme("steam"))
|
||||
icon = tray_icon()
|
||||
|
||||
def open_app():
|
||||
# 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
|
||||
# stopped when the check exits.
|
||||
if state("frontend") == "terminal":
|
||||
cmd = ["konsole", "-e", RUN_TERMINAL] if os.access(RUN_TERMINAL, os.X_OK) else \
|
||||
["konsole", "-e", "bash", "-c",
|
||||
cmd = [*terminal(), RUN_TERMINAL] if os.access(RUN_TERMINAL, os.X_OK) else \
|
||||
[*terminal(), "bash", "-c",
|
||||
f"set -o pipefail; curl -fsSL --max-time 30 {RELEASE}/steamify.sh | bash; read -rp 'Press Enter to close. ' _"]
|
||||
else:
|
||||
cmd = [RUN_APP] if os.access(RUN_APP, os.X_OK) else \
|
||||
|
||||
@@ -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 |
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
[Unit]
|
||||
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]
|
||||
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'
|
||||
|
||||
[Install]
|
||||
|
||||
+12
-2
@@ -15,11 +15,11 @@
|
||||
set -uo pipefail
|
||||
|
||||
# Release version, see CHANGELOG.md.
|
||||
VERSION=2.12.0
|
||||
VERSION=2.13.0
|
||||
|
||||
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
|
||||
source "$SCRIPT_DIR/lib/$lib.sh"
|
||||
done
|
||||
@@ -137,6 +137,16 @@ if [[ "$LIST" == true ]]; then
|
||||
exit 0
|
||||
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
|
||||
RESTART_FOR_LOGIN=false
|
||||
REAPPLY=false
|
||||
|
||||
+4
-2
@@ -18,6 +18,7 @@ Item {
|
||||
property var steps: ({}) // id -> "wait"|"run"|"ok"|"fail"
|
||||
property var failed: []
|
||||
property bool restartNeeded: false
|
||||
property bool reloginNeeded: false // GNOME loads the new theme's extensions at the next login
|
||||
// Review & apply only when something would change; "Re-apply what's on"
|
||||
// is for when nothing did.
|
||||
readonly property bool canApply: computePlan().length > 0
|
||||
@@ -123,7 +124,7 @@ Item {
|
||||
}
|
||||
|
||||
// --- Password and apply ---
|
||||
function resetRun() { steps = {}; failed = []; restartNeeded = false; runError = ""; doneCount = 0; logModel.clear(); }
|
||||
function resetRun() { steps = {}; failed = []; restartNeeded = false; reloginNeeded = false; runError = ""; doneCount = 0; logModel.clear(); }
|
||||
function startApply(password) {
|
||||
resetRun();
|
||||
var s = {};
|
||||
@@ -203,7 +204,7 @@ Item {
|
||||
// A successful BIOS check ends with bios-ready instead, keeping
|
||||
// the password for the flash.
|
||||
app.sessionPassword = "";
|
||||
app.failed = ev.failed || []; app.restartNeeded = !!ev.restart;
|
||||
app.failed = ev.failed || []; app.restartNeeded = !!ev.restart; app.reloginNeeded = !!ev.relogin;
|
||||
app.runError = app.errorText(ev.error);
|
||||
app.screen = "done";
|
||||
}
|
||||
@@ -249,6 +250,7 @@ Item {
|
||||
else if (a === "release") { holding = false; if (holdProgress < 1) holdProgress = 0; }
|
||||
} else if (screen === "done") {
|
||||
if (a === "accept" && restartNeeded) backend.restart();
|
||||
else if (a === "accept" && reloginNeeded) backend.restartGnome();
|
||||
else if (a === "back" || a === "accept") backToMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ Item {
|
||||
readonly property bool problem: app.failed.length > 0 || app.runError !== ""
|
||||
readonly property string message: {
|
||||
if (app.runError) return app.runError;
|
||||
if (!app.biosRun) return app.plan.length + (app.plan.length === 1 ? " change" : " changes") + " applied." + (app.restartNeeded ? " Some take effect after a restart." : "");
|
||||
if (!app.biosRun) return app.plan.length + (app.plan.length === 1 ? " change" : " changes") + " applied." + (app.restartNeeded ? " Some take effect after a restart." : "")
|
||||
+ (app.reloginNeeded && !app.restartNeeded ? " GNOME shows the dock, extensions and Settings panel after you log in again: Restart GNOME does that." : "");
|
||||
if (app.failed.length) return "The BIOS was not changed.";
|
||||
return (app.bios && app.bios.dryRun ? "Dry run: nothing was flashed. " : "BIOS " + (app.biosInfo.newest || "") + " is staged and is written during the restart. ")
|
||||
+ "Keep the power on and don't touch the machine until it has fully started again, even if the screen stays black.";
|
||||
@@ -33,6 +34,7 @@ Item {
|
||||
delegate: Text { required property string line; width: details.width - 12; text: line; color: "#aab5c4"; font.family: Theme.mono; font.pixelSize: 13; wrapMode: Text.WrapAnywhere } } }
|
||||
Row { anchors.horizontalCenter: parent.horizontalCenter; spacing: 14; topPadding: 8
|
||||
Btn { k: Input.g.back; text: "Back to the menu"; height: 52; onClicked: app.act("back") }
|
||||
Btn { visible: app.restartNeeded; k: Input.g.ok; text: "Restart now"; primary: true; height: 52; onClicked: app.backend.restart() } }
|
||||
Btn { visible: app.restartNeeded; k: Input.g.ok; text: "Restart now"; primary: true; height: 52; onClicked: app.backend.restart() }
|
||||
Btn { visible: app.reloginNeeded && !app.restartNeeded; k: Input.g.ok; text: "Restart GNOME"; primary: true; height: 52; onClicked: app.backend.restartGnome() } }
|
||||
}
|
||||
}
|
||||
+34
-1
@@ -4,6 +4,8 @@ import QtQuick
|
||||
Rectangle {
|
||||
required property AppState app
|
||||
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",
|
||||
done: "Done", bios1: "BIOS update", bios2: "BIOS update" })
|
||||
width: parent.width; height: 72; color: Theme.bar
|
||||
@@ -18,8 +20,39 @@ Rectangle {
|
||||
}
|
||||
Rectangle {
|
||||
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
|
||||
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
@@ -11,12 +11,15 @@ ApplicationWindow {
|
||||
required property var backend
|
||||
required property var gamepad
|
||||
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 bool fromSteam
|
||||
Component.onCompleted: Input.fromSteam = fromSteam
|
||||
|
||||
width: 1280; height: 720
|
||||
minimumWidth: 960; minimumHeight: 540
|
||||
flags: customFrame ? (Qt.Window | Qt.FramelessWindowHint) : Qt.Window
|
||||
visibility: fullscreen ? Window.FullScreen : Window.Windowed
|
||||
title: "Steamify"
|
||||
color: Theme.bg
|
||||
@@ -33,7 +36,11 @@ ApplicationWindow {
|
||||
// own shape, so the bars reach the edges and the content fills it.
|
||||
Item {
|
||||
id: stage
|
||||
readonly property real k: Math.min(win.width / 1280, win.height / 720)
|
||||
// On a desktop (a window, however large) the UI keeps the desktop's own scale: 1 here is one logical pixel, which Qt
|
||||
// multiplies by the scale of GNOME or KDE, like any other app (a 1440p or ultrawide monitor would otherwise show
|
||||
// everything twice as big). Only a window smaller than the layout shrinks it. The TV mode (--fullscreen, gamescope)
|
||||
// scales to the screen: it is read from the couch.
|
||||
readonly property real k: Math.min(win.width / 1280, win.height / 720, win.fullscreen ? Infinity : 1)
|
||||
width: win.width / k; height: win.height / k
|
||||
transformOrigin: Item.TopLeft
|
||||
scale: k
|
||||
@@ -41,7 +48,7 @@ ApplicationWindow {
|
||||
Keys.onPressed: function (e) { steamify.keyPressed(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 {
|
||||
id: content
|
||||
@@ -55,4 +62,19 @@ ApplicationWindow {
|
||||
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
@@ -31,7 +31,9 @@ Item {
|
||||
color: row.selected ? Theme.cardSel : Theme.card
|
||||
border.width: row.selected ? 2 : 0; border.color: Theme.accent
|
||||
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: { Input.type = "keyboard"; 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 }
|
||||
Column {
|
||||
@@ -67,14 +69,18 @@ Item {
|
||||
Rectangle { id: sw; visible: row.it.kind === "toggle"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter
|
||||
width: 46; height: 26; radius: 13; color: row.on ? Theme.accent : "#343f50"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Rectangle { width: 20; height: 20; radius: 10; color: "#f4f7fb"; y: 3; x: row.on ? 23 : 3; Behavior on x { NumberAnimation { duration: 120 } } } }
|
||||
Rectangle { width: 20; height: 20; radius: 10; color: "#f4f7fb"; y: 3; x: row.on ? 23 : 3; Behavior on x { NumberAnimation { duration: 120 } } }
|
||||
MouseArea { anchors.fill: parent; anchors.margins: -6; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: { Input.type = "keyboard"; app.sel = row.rowIndex; app.act("accept"); } } }
|
||||
Text { visible: row.it.kind === "toggle"; anchors.right: sw.left; anchors.rightMargin: 12; anchors.verticalCenter: parent.verticalCenter
|
||||
text: row.it.on ? "now on" : "now off"; color: row.it.on ? Theme.good : Theme.faint; font.family: Theme.mono; font.pixelSize: 12 }
|
||||
// action (Update BIOS)
|
||||
Rectangle { visible: row.it.kind === "action"; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter
|
||||
width: at.implicitWidth + 24; height: 30; radius: 8; color: Theme.warnBg
|
||||
Text { id: at; anchors.centerIn: parent; color: Theme.warn; font.family: Theme.body; font.pixelSize: 13; font.weight: Font.DemiBold
|
||||
text: app.biosChecking ? "Checking…" : app.bios && app.bios.selectable ? "Update…" : (app.bios && app.bios.newest ? "Up to date" : "Unavailable") } }
|
||||
text: app.biosChecking ? "Checking…" : app.bios && app.bios.selectable ? "Update…" : (app.bios && app.bios.newest ? "Up to date" : "Unavailable") }
|
||||
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: { Input.type = "keyboard"; app.sel = row.rowIndex; app.act("accept"); } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ QtObject {
|
||||
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.",
|
||||
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",
|
||||
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",
|
||||
|
||||
+4
-2
@@ -57,8 +57,9 @@ fi
|
||||
# Steamify shortcut's entry has the same id and always starts the newest
|
||||
# release: left alone.
|
||||
mkdir -p "$APPS"
|
||||
grep -q '^X-Steamify-Shortcut=true' "$APPS/steamify-ui.desktop" 2>/dev/null ||
|
||||
cat > "$APPS/steamify-ui.desktop" << EOF
|
||||
rm -f "$APPS/steamify-ui.desktop" # its name until 2.12.0
|
||||
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]
|
||||
Type=Application
|
||||
Name=Steamify CachyOS
|
||||
@@ -67,6 +68,7 @@ Exec=$DEST/ui/steamify-ui
|
||||
Icon=$DEST/assets/steam-gaming-settings.svg
|
||||
Terminal=false
|
||||
Categories=Game;Settings;
|
||||
StartupWMClass=io.github.theupriser.Steamify
|
||||
EOF
|
||||
command -v update-desktop-database >/dev/null && update-desktop-database "$APPS" 2>/dev/null || true
|
||||
|
||||
|
||||
+17
-1
@@ -345,6 +345,16 @@ class Backend(QObject):
|
||||
def restart(self):
|
||||
QProcess.startDetached("systemctl", ["reboot"])
|
||||
|
||||
@Slot()
|
||||
def restartGnome(self):
|
||||
# GNOME Shell on Wayland can't restart in place: end the session. With GDM autologin (the SteamOS conversion)
|
||||
# Steamify's helper logs the user straight back in; else a plain logout.
|
||||
import subprocess
|
||||
helper = "/usr/local/libexec/steamify-gdm-relogin"
|
||||
if os.access(helper, os.X_OK) and subprocess.run(["sudo", "-n", helper], capture_output=True).returncode == 0:
|
||||
return
|
||||
QProcess.startDetached("gnome-session-quit", ["--logout", "--no-prompt"])
|
||||
|
||||
def shutdown(self):
|
||||
if self._proc and self._proc.state() != QProcess.NotRunning:
|
||||
self._proc.kill()
|
||||
@@ -362,8 +372,13 @@ def main():
|
||||
fullscreen = "--fullscreen" in sys.argv or (in_gamescope and "--windowed" not in 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.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")
|
||||
if os.path.isfile(icon):
|
||||
app.setWindowIcon(QIcon(icon))
|
||||
@@ -373,6 +388,7 @@ def main():
|
||||
engine = QQmlApplicationEngine()
|
||||
engine.setInitialProperties({"backend": backend, "gamepad": pad,
|
||||
"fullscreen": fullscreen,
|
||||
"customFrame": custom_frame,
|
||||
# Started from Steam: its keys are a Steam
|
||||
# Controller's buttons (desktop layout).
|
||||
"fromSteam": bool(os.environ.get("SteamGameId")),
|
||||
|
||||
Reference in new issue
Block a user