diff --git a/Super_shift_S_release.sh b/Super_shift_S_release.sh index 42be22c..fed87fd 100755 --- a/Super_shift_S_release.sh +++ b/Super_shift_S_release.sh @@ -1,20 +1,54 @@ #!/bin/bash +# ============================================================================== +# Super Shift S - Omarchy Deck Mode Installer +# +# This script transforms an Omarchy (Arch Linux + Hyprland) desktop into a +# dual-mode system: Desktop Mode (Hyprland) and Gaming Mode (Steam Big Picture +# inside Gamescope — the same compositor the Steam Deck uses). +# +# It handles everything needed for this transformation: +# - Installing all Steam/gaming dependencies and GPU drivers +# - Configuring NVIDIA kernel parameters (nvidia-drm.modeset=1) +# - Setting up session switching between Hyprland and Gamescope via SDDM +# - Creating keybinds (Super+Shift+S to enter, Super+Shift+R to exit) +# - Configuring NetworkManager handoff (iwd <-> NM) for Steam network access +# - Setting up performance tuning (CPU governor, GPU power, kernel sysctl) +# - Auto-mounting external drives with Steam libraries +# - Configuring permissions (polkit, sudoers, udev) so it all works +# without password prompts during gameplay +# +# The script is idempotent — running it again skips steps that are already done. +# ============================================================================== + set -Euo pipefail +# -E: ERR traps are inherited by functions, command substitutions, and subshells +# -u: Treat unset variables as errors (catches typos in variable names) +# -o pipefail: A pipeline fails if ANY command in it fails, not just the last one Super_Shift_S_VERSION="12.27" +# Load configuration from /etc/gaming-mode.conf (system-wide) or +# ~/.gaming-mode.conf (user override). This lets users disable performance +# tuning by setting PERFORMANCE_MODE=disabled without editing the script. CONFIG_FILE="/etc/gaming-mode.conf" [[ -f "$HOME/.gaming-mode.conf" ]] && CONFIG_FILE="$HOME/.gaming-mode.conf" source "$CONFIG_FILE" 2>/dev/null || true : "${PERFORMANCE_MODE:=enabled}" +# Flags that track whether the user needs to reboot or re-login after setup. +# Various steps set these to 1 when they make changes that only take effect +# after a session restart (e.g. adding user groups, changing kernel params). NEEDS_RELOGIN=0 NEEDS_REBOOT=0 +# Logging helpers — consistent prefix makes it easy to spot installer output +# in a busy terminal. err() goes to stderr so it can be captured separately. info(){ echo "[*] $*"; } warn(){ echo "[!] $*"; } err(){ echo "[!] $*" >&2; } +# Fatal error handler — logs to the system journal (journalctl -t gaming-mode) +# so failures can be diagnosed even after the terminal is closed. die() { local msg="$1"; local code="${2:-1}" echo "FATAL: $msg" >&2 @@ -22,6 +56,9 @@ die() { exit "$code" } +# AUR helpers (yay, paru) can break after a major system update if their +# own dependencies change. This checks whether the helper actually runs, +# not just whether the binary exists on disk. check_aur_helper_functional() { local helper="$1" if $helper --version &>/dev/null; then @@ -31,6 +68,10 @@ check_aur_helper_functional() { fi } +# If yay is broken (common after Go or glibc updates), this rebuilds it +# from scratch by cloning the AUR package and running makepkg. This avoids +# a chicken-and-egg problem where you need an AUR helper to install an +# AUR helper. rebuild_yay() { info "Attempting to rebuild yay..." local tmp_dir @@ -49,14 +90,28 @@ rebuild_yay() { fi } +# Sanity check — make sure we're actually running on an Omarchy system. +# pacman = Arch Linux, hyprctl = Hyprland compositor, ~/.config/hypr = config dir. +# If any of these are missing, the script can't do its job. validate_environment() { command -v pacman >/dev/null || die "pacman required" command -v hyprctl >/dev/null || die "hyprctl required" [ -d "$HOME/.config/hypr" ] || die "Hyprland config directory not found (~/.config/hypr)" } +# Quick check if a pacman package is installed. Used throughout the script +# to avoid reinstalling things that are already present. check_package() { pacman -Qi "$1" &>/dev/null; } +# Distinguishes AMD integrated GPUs (iGPUs/APUs) from discrete AMD GPUs (dGPUs). +# This matters because Gaming Mode needs to target the RIGHT GPU — if you have +# both an AMD APU and an NVIDIA dGPU, we want to use the NVIDIA card for gaming, +# not the low-power APU that's driving your laptop display. +# +# It works by reading the PCI device info and matching against known AMD APU +# codenames (Phoenix, Rembrandt, Van Gogh, etc.). If the name matches an APU +# pattern, it returns 0 (true = this IS an iGPU). If it matches a discrete +# GPU pattern (Navi, RX, Vega 56/64), it returns 1 (false = this is a dGPU). is_amd_igpu_card() { local card_path="$1" local device_path="$card_path/device" @@ -73,6 +128,11 @@ is_amd_igpu_card() { return 1 } +# Intel-only GPU detection — Gaming Mode doesn't support Intel GPUs because +# Gamescope (the compositor) doesn't work well with Intel graphics. +# However, many laptops have Intel iGPU + NVIDIA/AMD dGPU — those are fine +# because we use the discrete GPU for gaming. This function only blocks +# systems where Intel is the ONLY GPU available. check_intel_only() { # Returns 0 (true) if system has Intel GPU but NO AMD/NVIDIA GPU # Returns 1 (false) if system has AMD/NVIDIA (Intel iGPU + dGPU is OK) @@ -104,6 +164,17 @@ check_intel_only() { return 1 # Has AMD/NVIDIA (or no Intel), allow it } +# Finds which monitors are physically connected to the discrete GPU. +# Gaming Mode needs to know which display output to use (e.g. HDMI-1, DP-2) +# and what resolution/refresh rate it supports. +# +# It scans /sys/class/drm/ for GPU cards, identifies the discrete GPU by its +# driver (nvidia or amdgpu), then checks each connector (HDMI, DP, etc.) to +# see if a monitor is plugged in. Resolution and refresh rate are read from +# the EDID data that the monitor reports to the system. +# +# Uses bash nameref variables (_monitors, _dgpu_card, _dgpu_type) so the +# caller gets the results without needing subshells or global variables. detect_dgpu_monitors() { local -n _monitors=$1 local -n _dgpu_card=$2 @@ -162,6 +233,15 @@ detect_dgpu_monitors() { done } +# NVIDIA GPUs require a kernel parameter (nvidia-drm.modeset=1) to work +# properly with Wayland compositors like Gamescope. Without it, the GPU +# won't expose DRM (Direct Rendering Manager) devices, and Gamescope +# won't be able to take over the display. +# +# This function checks if the parameter is already set in the running +# kernel's command line (/proc/cmdline). If not, it detects the bootloader +# (Limine, GRUB, or systemd-boot) and offers to add it automatically. +# A reboot is required after adding the parameter. check_nvidia_kernel_params() { local lspci_output lspci_output=$(/usr/bin/lspci 2>/dev/null) @@ -236,6 +316,10 @@ check_nvidia_kernel_params() { esac } +# Adds nvidia-drm.modeset=1 to the Limine bootloader config. +# Limine stores kernel command line parameters on lines starting with "cmdline:". +# This appends the parameter to the end of that line. A backup is created first +# in case something goes wrong. configure_limine_nvidia() { local config_file="$1" @@ -265,6 +349,9 @@ configure_limine_nvidia() { fi } +# Adds nvidia-drm.modeset=1 to GRUB's kernel parameters. +# GRUB stores defaults in /etc/default/grub — after modifying it, grub-mkconfig +# must be run to regenerate the actual boot config at /boot/grub/grub.cfg. configure_grub_nvidia() { local grub_default="/etc/default/grub" @@ -304,6 +391,14 @@ MSG warn "Gaming Mode may not work correctly without nvidia-drm.modeset=1" } +# Sets NVIDIA-specific environment variables needed for Gamescope to work +# properly with NVIDIA GPUs. These tell the system to use the NVIDIA DRM +# backend for GBM (Generic Buffer Management), which is how Wayland +# compositors allocate GPU memory for rendering. +# +# GBM_BACKEND=nvidia-drm — Use NVIDIA's DRM backend for buffer allocation +# __GLX_VENDOR_LIBRARY_NAME=nvidia — Use NVIDIA's GLX implementation +# __VK_LAYER_NV_optimus=NVIDIA_only — On Optimus laptops, force Vulkan to use NVIDIA install_nvidia_deckmode_env() { local lspci_output lspci_output=$(/usr/bin/lspci 2>/dev/null) @@ -332,6 +427,17 @@ EOF NEEDS_RELOGIN=1 } +# Steam on Linux requires a LOT of dependencies — 32-bit libraries (lib32-*), +# Vulkan drivers, audio libraries, fonts, and GPU-specific drivers. This +# function checks for everything Steam needs and offers to install what's missing. +# +# It handles three categories: +# 1. Core deps — required for Steam to run at all (lib32 libs, Vulkan, audio) +# 2. GPU deps — driver packages specific to NVIDIA or AMD +# 3. Recommended — nice-to-haves like MangoHud (FPS overlay), Proton-GE, etc. +# +# The multilib repository must be enabled in pacman.conf for 32-bit packages +# to be available — Steam is a 32-bit application that needs 32-bit libraries. check_steam_dependencies() { info "Checking Steam dependencies for Arch Linux..." @@ -657,6 +763,11 @@ check_steam_dependencies() { check_steam_config } +# Enables the multilib repository in /etc/pacman.conf. Multilib provides +# 32-bit versions of libraries (lib32-*) which Steam requires because it's +# a 32-bit application. By default on Arch, these lines are commented out. +# This function uncomments them and runs a full system upgrade so the new +# packages become available. enable_multilib_repo() { info "Enabling multilib repository..." @@ -679,6 +790,14 @@ enable_multilib_repo() { fi } +# Checks that the user is in the right Linux groups for gaming: +# - video: Access to GPU hardware (required for rendering) +# - input: Access to controllers, gamepads, and keyboard input devices +# - wheel: Sudo/admin group, needed for NetworkManager control in gaming mode +# +# Also checks for some common performance tips like lowering vm.swappiness +# (reduces swap usage during gaming) and increasing the open file limit +# (needed for esync, a Steam/Proton feature that reduces CPU overhead). check_steam_config() { info "Checking Steam configuration..." @@ -753,6 +872,23 @@ check_steam_config() { fi } +# Sets up the permissions needed for Gaming Mode to tune system performance +# WITHOUT requiring a password prompt. This creates three things: +# +# 1. UDEV RULES — Make CPU governor and GPU performance files writable by +# regular users. Normally these sysfs files are root-only, but udev rules +# can relax permissions when the devices are detected at boot. +# +# 2. SUDOERS RULES — Allow members of the "video" group to run specific +# sysctl commands (kernel scheduler tuning, VM settings, network buffers) +# and nvidia-smi commands without entering a password. These are narrowly +# scoped — only the exact commands needed, not blanket sudo access. +# +# 3. MEMLOCK LIMITS — Increase the memory lock limit to 2GB. Games using +# esync/fsync need to lock memory pages to avoid latency spikes. +# +# 4. PIPEWIRE CONFIG — Sets a lower audio quantum (buffer size) for reduced +# audio latency during gaming. Lower = less delay but more CPU usage. setup_performance_permissions() { local udev_rules_file="/etc/udev/rules.d/99-gaming-performance.rules" local sudoers_file="/etc/sudoers.d/gaming-mode-sysctl" @@ -866,6 +1002,18 @@ PIPEWIRECONF return 0 } +# Configures shader cache settings for better gaming performance. +# When a game runs for the first time, the GPU driver compiles shaders +# (small programs that run on the GPU). This causes stuttering because +# compilation takes time. By caching these compiled shaders to disk +# (up to 12GB), the stuttering only happens once — next time the shader +# loads instantly from cache. +# +# MESA_SHADER_CACHE — AMD/Intel open-source driver cache +# __GL_SHADER_DISK_CACHE — NVIDIA proprietary driver cache +# DXVK_STATE_CACHE — Proton/Wine DirectX-to-Vulkan translation cache +# RADV_PERFTEST=gpl — AMD Vulkan: enables graphics pipeline library for +# faster shader compilation setup_shader_cache() { local env_file="/etc/environment.d/99-shader-cache.conf" @@ -916,6 +1064,10 @@ SHADERCACHE fi } +# Silences a harmless but annoying warning from fcitx5 (input method framework). +# fcitx5 complains about Wayland support on every login, even if you don't use +# it for input. This sets FCITX_NO_WAYLAND_DIAGNOSE=1 to suppress the warning +# in both Hyprland config and the user's environment. setup_fcitx_silence() { local env_dir="$HOME/.config/environment.d" local env_file="$env_dir/90-fcitx-wayland.conf" @@ -941,6 +1093,10 @@ EOF fi } +# Configures the Elephant app launcher (used in Omarchy) to launch desktop +# applications through uwsm-app. UWSM (Universal Wayland Session Manager) +# ensures apps are properly associated with the Wayland session, which +# prevents issues with apps losing track of their display server. configure_elephant_launcher() { local cfg="$HOME/.config/elephant/desktopapplications.toml" if [[ ! -f "$cfg" ]]; then @@ -963,6 +1119,8 @@ configure_elephant_launcher() { restart_elephant_walker } +# Restarts the Elephant/Walker launcher service so config changes take +# effect immediately without requiring a logout. restart_elephant_walker() { if ! systemctl --user show-environment >/dev/null 2>&1; then return 0 @@ -975,6 +1133,16 @@ restart_elephant_walker() { systemctl --user restart app-walker@autostart.service >/dev/null 2>&1 || true } +# Installs the core packages that the Gaming Mode scripts themselves need +# (as opposed to Steam's dependencies which are handled separately). +# These include: +# - python-evdev: Reads raw keyboard input for the Super+Shift+R hotkey +# - libcap: Sets Linux capabilities on gamescope (cap_sys_nice for priority) +# - ntfs-3g: Mounts NTFS-formatted game drives (common for Windows dual-boot) +# - xcb-util-cursor: X11 cursor support needed by some Proton games +# +# After installing packages, it also runs the sub-setup functions for +# performance permissions, shader cache, and gamescope capabilities. setup_requirements() { local -a required_packages=("steam" "gamescope" "mangohud" "python" "python-evdev" "libcap" "gamemode" "curl" "pciutils" "ntfs-3g" "xcb-util-cursor") local -a packages_to_install=() @@ -1019,6 +1187,34 @@ setup_requirements() { fi } +# ============================================================================== +# SESSION SWITCHING — The heart of the installer +# +# This is the biggest and most important function. It sets up everything needed +# to seamlessly switch between Desktop Mode (Hyprland) and Gaming Mode +# (Gamescope + Steam Big Picture). +# +# The switching mechanism works through SDDM (the display/login manager): +# 1. User presses Super+Shift+S in Hyprland +# 2. switch-to-gaming script updates SDDM config to point to Gaming session +# 3. SDDM restarts and auto-logs into the Gaming Mode session +# 4. gamescope-session-nm-wrapper starts performance tuning, NetworkManager, +# drive mounting, keybind monitor, then launches Gamescope + Steam +# 5. When done (Super+Shift+R or Steam > Exit to Desktop), the reverse happens +# +# This function creates ALL the scripts and config files needed for this flow: +# - Session wrapper (gamescope-session-nm-wrapper) +# - Switch scripts (switch-to-gaming, switch-to-desktop) +# - Keybind monitor (Python daemon using evdev for Super+Shift+R) +# - NetworkManager start/stop scripts (iwd <-> NM handoff) +# - Steam library auto-mount daemon +# - SDDM session entry and config +# - Polkit and sudoers rules for passwordless operation +# - Hyprland keybind for Super+Shift+S +# +# It also installs ChimeraOS's gamescope-session packages from AUR, which +# provide the base session framework that the Steam Deck uses. +# ============================================================================== setup_session_switching() { echo "" echo "================================================================" @@ -1312,6 +1508,17 @@ setup_session_switching() { info "ChimeraOS gamescope-session packages already installed (correct -git versions)" fi + # NetworkManager Integration + # + # Omarchy uses iwd (Intel Wireless Daemon) for WiFi, but Steam requires + # NetworkManager for its network settings UI. These can't run simultaneously + # without conflicts, so we create a managed handoff: + # - On gaming session start: NM starts, takes over networking from iwd + # - On gaming session exit: NM stops, iwd restarts and reconnects to WiFi + # + # If iwd is active, we configure NM to use iwd as its WiFi backend so they + # cooperate instead of fighting. If systemd-networkd is also running, we + # tell NM to leave ethernet interfaces alone to avoid conflicts. info "Setting up NetworkManager integration..." if systemctl is-active --quiet iwd; then info "Detected iwd is active - configuring NetworkManager to use iwd backend..." @@ -1431,6 +1638,16 @@ NM_STOP sudo chmod +x "$nm_stop_script" info "Created NetworkManager start/stop scripts" + # Steam Library Auto-Mount Daemon + # + # Many gamers have games spread across multiple drives (external SSDs, + # NTFS partitions from a Windows dual-boot, etc.). This daemon: + # 1. Scans all connected drives for Steam library folders (steamapps/) + # 2. Mounts drives that contain Steam libraries via udisks2 + # 3. Unmounts drives that DON'T have Steam libraries (keeps it clean) + # 4. Watches for hot-plugged drives (USB drives plugged in during gaming) + # + # It runs in the background during Gaming Mode and stops when you exit. local steam_mount_script="/usr/local/bin/steam-library-mount" info "Creating Steam library drive mount script..." sudo tee "$steam_mount_script" > /dev/null << 'STEAM_MOUNT' @@ -1538,6 +1755,13 @@ STEAM_MOUNT sudo chmod +x "$steam_mount_script" info "Created $steam_mount_script" + # Polkit Rules + # + # Polkit is Linux's permission system for D-Bus actions. Steam communicates + # with NetworkManager over D-Bus, but by default only root can control NM. + # These rules allow members of the "wheel" group (admin users) to control + # NetworkManager without a password prompt — otherwise Steam would show + # a "permission denied" error when trying to access network settings. local polkit_rules="/etc/polkit-1/rules.d/50-gamescope-networkmanager.rules" if sudo test -f "$polkit_rules"; then @@ -1572,6 +1796,10 @@ POLKIT_RULES fi fi + # Udisks2 Polkit Rules — same concept as above but for drive mounting. + # Allows the steam-library-mount daemon to mount/unmount drives without + # a password prompt. Without this, plugging in a USB drive with games + # would show a password dialog — not ideal in the middle of a gaming session. local udisks_polkit="/etc/polkit-1/rules.d/50-udisks-gaming.rules" if sudo test -f "$udisks_polkit"; then @@ -1601,6 +1829,18 @@ UDISKS_POLKIT fi fi + # Gamescope Session Configuration + # + # This config file tells gamescope-session-plus (from ChimeraOS) how to + # set up the gaming display. It includes: + # - Resolution and refresh rate (auto-detected from your monitor) + # - Which display output to use (e.g. HDMI-1, DP-2) + # - GPU-specific settings (NVIDIA vs AMD have different requirements) + # + # NVIDIA gets: GBM_BACKEND=nvidia-drm, VULKAN_ADAPTER pointing to the GPU + # AMD gets: ADAPTIVE_SYNC=1 (FreeSync), ENABLE_GAMESCOPE_HDR=1 (HDR support) + # + # NVIDIA is capped at 2560x1440 due to Gamescope limitations with NVIDIA GPUs. info "Creating gamescope-session-plus configuration..." local env_dir="${user_home}/.config/environment.d" local gamescope_conf="${env_dir}/gamescope-session-plus.conf" @@ -1653,6 +1893,13 @@ GAMESCOPE_CONF info "Created $gamescope_conf" + # NVIDIA Gamescope Wrapper + # + # NVIDIA GPUs need the --force-composition flag in Gamescope to avoid + # rendering glitches. This wrapper script sits in front of the real + # gamescope binary — when Gaming Mode starts, it calls this wrapper + # instead, which adds the flag and then exec's the real gamescope. + # It checks if the flag is actually supported first (older versions don't have it). info "Creating NVIDIA gamescope wrapper..." local nvidia_wrapper_dir="/usr/local/lib/gamescope-nvidia" local nvidia_wrapper="${nvidia_wrapper_dir}/gamescope" @@ -1670,6 +1917,24 @@ NVIDIA_WRAPPER sudo chmod +x "$nvidia_wrapper" info "Created $nvidia_wrapper" + # Main Session Wrapper — gamescope-session-nm-wrapper + # + # This is the master script that runs when Gaming Mode starts. It's the + # entry point for the entire gaming session and orchestrates everything: + # + # 1. Enables performance mode (CPU governor → performance, GPU max power) + # 2. Adds NVIDIA wrapper to PATH if needed + # 3. Starts NetworkManager for Steam's network access + # 4. Launches steam-library-mount daemon for external drive detection + # 5. Starts the keybind monitor (listens for Super+Shift+R to exit) + # 6. Sets Steam-specific environment variables + # 7. Launches gamescope-session-plus (the actual Gamescope + Steam session) + # + # When the session ends (for any reason), the cleanup trap: + # - Kills the mount daemon and keybind monitor + # - Stops NetworkManager and restores iwd WiFi + # - Restores balanced power mode (CPU powersave, GPU defaults) + # - Removes the session marker file info "Creating NetworkManager session wrapper..." local nm_wrapper="/usr/local/bin/gamescope-session-nm-wrapper" @@ -1824,6 +2089,12 @@ NM_WRAPPER sudo chmod +x "$nm_wrapper" info "Created $nm_wrapper" + # SDDM Session Entry + # + # SDDM (the login/display manager) needs a .desktop file to know about + # Gaming Mode as a session option. This is what tells SDDM "when you + # auto-login to the gaming session, run this script." It's placed in + # /usr/share/wayland-sessions/ alongside the normal Hyprland session. info "Creating SDDM session entry..." local session_desktop="/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop" @@ -1838,6 +2109,12 @@ SESSION_DESKTOP info "Created $session_desktop" + # os-session-select — Steam's "Exit to Desktop" handler + # + # When you click Steam > Power > "Exit to Desktop" inside Gaming Mode, + # Steam calls /usr/lib/os-session-select. On a real Steam Deck this + # switches to Desktop Mode. Our version does the same thing — it updates + # SDDM to boot back into Hyprland and restarts the display manager. info "Creating session-select script..." local os_session_select="/usr/lib/os-session-select" @@ -1857,6 +2134,15 @@ OS_SESSION_SELECT sudo chmod +x "$os_session_select" info "Created $os_session_select" + # switch-to-gaming — Called when Super+Shift+S is pressed in Hyprland + # + # This script handles the transition from Desktop to Gaming Mode: + # 1. Masks suspend targets — prevents the system from sleeping when the + # monitor briefly disconnects during the display manager restart + # 2. Updates SDDM config to auto-login to the gaming session + # 3. Kills any leftover gamescope processes from a previous session + # 4. Switches to VT2 (virtual terminal) to avoid display conflicts + # 5. Restarts SDDM, which auto-logs into Gaming Mode info "Creating switch-to-gaming script..." local switch_script="/usr/local/bin/switch-to-gaming" @@ -1879,6 +2165,15 @@ SWITCH_SCRIPT sudo chmod +x "$switch_script" info "Created $switch_script" + # switch-to-desktop — Called when Super+Shift+R is pressed in Gaming Mode + # + # This script handles the transition back from Gaming to Desktop Mode: + # 1. Unmasks suspend targets (re-enables sleep/hibernate) + # 2. Restores Bluetooth (disabled during gaming to reduce interference) + # 3. Gracefully shuts down Steam (timeout 5s, then force kill) + # 4. Kills gamescope with SIGTERM first, then SIGKILL if it won't die + # 5. Updates SDDM config back to Hyprland session + # 6. Restarts SDDM, which auto-logs into Desktop Mode info "Creating switch-to-desktop script..." local switch_desktop_script="/usr/local/bin/switch-to-desktop" @@ -1926,6 +2221,17 @@ SWITCH_DESKTOP sudo chmod +x "$switch_desktop_script" info "Created $switch_desktop_script" + # Keybind Monitor — Python daemon for Super+Shift+R in Gaming Mode + # + # Inside Gamescope, Hyprland isn't running so its keybinds don't work. + # This Python script uses python-evdev to read raw keyboard input directly + # from /dev/input/event* devices, bypassing the compositor entirely. + # + # It uses Linux's selector (epoll) interface to efficiently monitor multiple + # keyboard devices simultaneously without busy-waiting. When it detects + # Super+Shift+R, it calls switch-to-desktop to return to Hyprland. + # + # The user must be in the "input" group to read /dev/input/ devices. info "Creating gaming mode keybind monitor..." local keybind_monitor="/usr/local/bin/gaming-keybind-monitor" @@ -2018,6 +2324,19 @@ KEYBIND_MONITOR sudo chmod +x "$keybind_monitor" info "Created $keybind_monitor" + # SDDM Session Switching Config + # + # SDDM supports auto-login — it can automatically log in a user to a + # specific session without showing the login screen. This config file + # controls WHICH session SDDM auto-logs into. + # + # The switching mechanism works by editing this file: + # - "Session=hyprland-uwsm" → boots into Desktop Mode + # - "Session=gamescope-session-steam-nm" → boots into Gaming Mode + # + # The gaming-session-switch helper script toggles this value, then SDDM + # is restarted to pick up the change. The "zz-" prefix ensures this + # config loads LAST and overrides any other SDDM autologin settings. info "Creating SDDM session switching config..." local sddm_gaming_conf="/etc/sddm.conf.d/zz-gaming-session.conf" @@ -2066,6 +2385,16 @@ SESSION_HELPER sudo chmod +x "$session_helper" info "Created $session_helper" + # Sudoers Rules for Session Switching + # + # The session switching scripts need to run several commands as root + # (restart SDDM, start/stop NetworkManager, control Bluetooth, etc.). + # These sudoers rules allow members of the "video" and "wheel" groups + # to run ONLY these specific commands without a password. + # + # This is much safer than giving blanket NOPASSWD sudo — each rule + # is scoped to a specific binary path. An attacker can't leverage + # these rules to run arbitrary commands as root. local sudoers_session="/etc/sudoers.d/gaming-session-switch" if [[ -f "$sudoers_session" ]]; then @@ -2218,6 +2547,20 @@ HYPR_GAMING return 0 } +# Comprehensive verification — checks every file, permission, package, group, +# and service that Gaming Mode depends on. This is the --verify mode that +# users can run to diagnose problems without re-running the full installer. +# +# It checks: +# - All installed files exist and have correct permissions +# - Hyprland keybind is configured +# - ChimeraOS AUR packages are installed +# - Steam library mount script and udisks2 are ready +# - python-evdev works and user is in the input group +# - Gamescope session config exists +# - User is in required groups (video, input, wheel) +# - Service states (NM should be inactive, iwd should be active) +# - Sudo permissions work without password verify_installation() { echo "" echo "================================================================" @@ -2471,6 +2814,17 @@ verify_installation() { $all_ok && return 0 || return 1 } +# Main orchestrator — runs the full installation process in order. +# Each step builds on the previous one: +# 1. Authenticate sudo (and clear cached credentials first for a fresh prompt) +# 2. Validate we're on an Omarchy system +# 3. Install Steam dependencies and GPU drivers +# 4. Configure NVIDIA kernel parameters (if applicable) +# 5. Set NVIDIA environment variables (if applicable) +# 6. Install script requirements and performance permissions +# 7. Set up session switching (the big one — all the scripts and configs) +# 8. Prompt for reboot/relogin if needed +# 9. Optionally run verification to confirm everything worked execute_setup() { sudo -k sudo -v || die "sudo authentication required" @@ -2559,6 +2913,11 @@ show_help() { echo "" } +# Command-line argument parsing — determines what mode to run in. +# With no arguments, runs the full installation. Otherwise: +# --help/-h: Show usage information +# --verify/-v: Only check if everything is installed correctly +# --version: Print version number case "${1:-}" in --help|-h) show_help