1829 lines
69 KiB
Bash
Executable file
1829 lines
69 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# ARCGames - Gaming Mode Installer for Intel Arc dGPUs
|
|
# Version: 1.5.0
|
|
#
|
|
# Description:
|
|
# Sets up a SteamOS-like gaming experience on Arch Linux with Hyprland,
|
|
# specifically optimized for Intel Arc discrete GPUs (Alchemist, Battlemage).
|
|
#
|
|
# Features:
|
|
# - Steam and gaming dependencies installation
|
|
# - Mesa-git or stable Mesa driver selection
|
|
# - Gamescope session switching (Hyprland <-> Gaming Mode)
|
|
# - Performance tuning (GPU, audio, memory)
|
|
# - External Steam library auto-mounting
|
|
#
|
|
# Usage:
|
|
# ./ARCGames_install.sh [--help|--version]
|
|
#
|
|
# Keybinds (after installation):
|
|
# Super+Shift+S - Switch to Gaming Mode (from Hyprland)
|
|
# Super+Shift+R - Return to Desktop (from Gaming Mode)
|
|
#
|
|
###############################################################################
|
|
|
|
set -uo pipefail
|
|
|
|
ARCGAMES_VERSION="1.5.0"
|
|
|
|
# Track mesa driver state for safe cleanup on interrupt
|
|
_MESA_REMOVAL_IN_PROGRESS=0
|
|
cleanup_on_exit() {
|
|
if [[ "$_MESA_REMOVAL_IN_PROGRESS" -eq 1 ]]; then
|
|
echo "" >&2
|
|
echo "================================================================" >&2
|
|
echo " WARNING: Installation interrupted during mesa driver swap!" >&2
|
|
echo " Your system may have no graphics driver installed." >&2
|
|
echo " Recovery from TTY:" >&2
|
|
echo " sudo pacman -S mesa lib32-mesa vulkan-intel lib32-vulkan-intel" >&2
|
|
echo "================================================================" >&2
|
|
echo "" >&2
|
|
fi
|
|
}
|
|
trap cleanup_on_exit EXIT
|
|
|
|
###############################################################################
|
|
# CONFIGURATION
|
|
###############################################################################
|
|
|
|
CONFIG_FILE="/etc/gaming-mode.conf"
|
|
# Note: REAL_HOME not yet defined here, check both locations
|
|
[[ -f "${HOME}/.gaming-mode.conf" ]] && CONFIG_FILE="${HOME}/.gaming-mode.conf"
|
|
[[ -n "${SUDO_USER:-}" ]] && {
|
|
_sudo_home=$(getent passwd "$SUDO_USER" | cut -d: -f6)
|
|
[[ -n "$_sudo_home" && -f "$_sudo_home/.gaming-mode.conf" ]] && CONFIG_FILE="$_sudo_home/.gaming-mode.conf"
|
|
}
|
|
|
|
# Parse config file safely (no arbitrary code execution)
|
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
while IFS='=' read -r _key _value; do
|
|
_key="${_key#"${_key%%[![:space:]]*}"}" # trim leading whitespace
|
|
_key="${_key%"${_key##*[![:space:]]}"}" # trim trailing whitespace
|
|
_value="${_value#"${_value%%[![:space:]]*}"}"
|
|
_value="${_value%"${_value##*[![:space:]]}"}"
|
|
case "$_key" in
|
|
PERFORMANCE_MODE) PERFORMANCE_MODE="$_value" ;;
|
|
USE_MESA_GIT) USE_MESA_GIT="$_value" ;;
|
|
esac
|
|
done < "$CONFIG_FILE" 2>/dev/null || true
|
|
fi
|
|
|
|
: "${PERFORMANCE_MODE:=enabled}"
|
|
: "${USE_MESA_GIT:=1}" # 1 = mesa-git from AUR (recommended), 0 = stable mesa
|
|
|
|
# Global state
|
|
NEEDS_RELOGIN=0
|
|
INTEL_ARC_VK_DEVICE=""
|
|
INTEL_ARC_DRM_CARD=""
|
|
|
|
# Resolve actual user (handles sudo case)
|
|
REAL_USER="${SUDO_USER:-$USER}"
|
|
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
|
|
|
|
###############################################################################
|
|
# UTILITY FUNCTIONS
|
|
###############################################################################
|
|
|
|
info() { echo "[*] $*"; }
|
|
warn() { echo "[!] $*" >&2; }
|
|
err() { echo "[!] $*" >&2; }
|
|
|
|
die() {
|
|
local msg="$1"
|
|
local code="${2:-1}"
|
|
echo "FATAL: $msg" >&2
|
|
logger -t arcgames "Installation failed: $msg"
|
|
exit "$code"
|
|
}
|
|
|
|
check_package() {
|
|
pacman -Qi "$1" &>/dev/null
|
|
}
|
|
|
|
check_aur_helper_functional() {
|
|
local helper="$1"
|
|
"$helper" --version &>/dev/null
|
|
}
|
|
|
|
# Validate REAL_HOME was resolved (must be after die() is defined)
|
|
[[ -z "$REAL_HOME" ]] && die "Could not resolve home directory for user: $REAL_USER"
|
|
|
|
# Run command as the original user (handles case where script is run with sudo)
|
|
run_as_user() {
|
|
if [[ -n "${SUDO_USER:-}" ]] && [[ "$EUID" -eq 0 ]]; then
|
|
sudo -u "$SUDO_USER" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# ENVIRONMENT VALIDATION
|
|
###############################################################################
|
|
|
|
validate_environment() {
|
|
command -v pacman >/dev/null || die "pacman required"
|
|
command -v hyprctl >/dev/null || die "hyprctl required"
|
|
[[ -d "$REAL_HOME/.config/hypr" ]] || die "Hyprland config directory not found (~/.config/hypr)"
|
|
|
|
# Ensure lspci is available for GPU detection
|
|
if ! command -v lspci >/dev/null 2>&1; then
|
|
info "Installing pciutils for GPU detection..."
|
|
sudo pacman -S --needed --noconfirm pciutils || die "Failed to install pciutils"
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# GPU DETECTION
|
|
###############################################################################
|
|
|
|
# Check if a DRM card is an Intel iGPU (integrated) vs dGPU (discrete Arc)
|
|
is_intel_igpu() {
|
|
local card_path="$1"
|
|
local device_path="$card_path/device"
|
|
local pci_slot=""
|
|
|
|
[[ -L "$device_path" ]] && pci_slot=$(basename "$(readlink -f "$device_path")")
|
|
[[ -z "$pci_slot" ]] && return 1
|
|
|
|
local device_info
|
|
device_info=$(lspci -s "$pci_slot" 2>/dev/null)
|
|
|
|
# Arc dGPU patterns (NOT iGPUs)
|
|
if echo "$device_info" | grep -iqE 'arc|alchemist|battlemage|celestial'; then
|
|
return 1
|
|
fi
|
|
|
|
# iGPU patterns
|
|
if echo "$device_info" | grep -iqE 'uhd|iris|hd graphics|integrated'; then
|
|
return 0
|
|
fi
|
|
|
|
# xe driver = Arc dGPU, i915 can be either
|
|
local driver_link="$card_path/device/driver"
|
|
if [[ -L "$driver_link" ]]; then
|
|
local driver
|
|
driver=$(basename "$(readlink "$driver_link")")
|
|
[[ "$driver" == "xe" ]] && return 1
|
|
fi
|
|
|
|
# Fallback: PCI bus 00 is typically iGPU
|
|
[[ "$pci_slot" =~ ^0000:00: ]] && return 0
|
|
|
|
return 1 # Assume dGPU
|
|
}
|
|
|
|
# Get the Vulkan device ID (vendor:device) for a PCI slot
|
|
get_vk_device_id() {
|
|
local pci_slot="$1"
|
|
local vendor device
|
|
|
|
vendor=$(cat "/sys/bus/pci/devices/$pci_slot/vendor" 2>/dev/null | sed 's/0x//')
|
|
device=$(cat "/sys/bus/pci/devices/$pci_slot/device" 2>/dev/null | sed 's/0x//')
|
|
|
|
if [[ -n "$vendor" && -n "$device" ]]; then
|
|
echo "${vendor}:${device}"
|
|
fi
|
|
}
|
|
|
|
# Find Intel Arc dGPU with connected display
|
|
find_intel_arc_display_gpu() {
|
|
local found_arc=false
|
|
local arc_card=""
|
|
local arc_pci=""
|
|
local arc_has_display=false
|
|
|
|
for card_path in /sys/class/drm/card[0-9]*; do
|
|
local card_name
|
|
card_name=$(basename "$card_path")
|
|
[[ "$card_name" == render* ]] && continue
|
|
|
|
# Check for Intel GPU driver
|
|
local driver_link="$card_path/device/driver"
|
|
[[ -L "$driver_link" ]] || continue
|
|
|
|
local driver
|
|
driver=$(basename "$(readlink "$driver_link")")
|
|
[[ "$driver" == "i915" || "$driver" == "xe" ]] || continue
|
|
|
|
# Skip iGPUs - we only want discrete Arc
|
|
if is_intel_igpu "$card_path"; then
|
|
info "Skipping Intel iGPU: $card_name"
|
|
continue
|
|
fi
|
|
|
|
# This is an Intel Arc dGPU
|
|
found_arc=true
|
|
local pci_slot
|
|
pci_slot=$(basename "$(readlink -f "$card_path/device")")
|
|
|
|
# Check for connected display
|
|
for connector in "$card_path"/"$card_name"-*/status; do
|
|
if [[ -f "$connector" ]] && grep -q "^connected$" "$connector" 2>/dev/null; then
|
|
arc_card="$card_name"
|
|
arc_pci="$pci_slot"
|
|
arc_has_display=true
|
|
info "Intel Arc dGPU with display: $card_name (PCI: $pci_slot)"
|
|
break 2
|
|
fi
|
|
done
|
|
|
|
# Remember Arc GPU even if no display connected
|
|
if [[ -z "$arc_card" ]]; then
|
|
arc_card="$card_name"
|
|
arc_pci="$pci_slot"
|
|
fi
|
|
done
|
|
|
|
$found_arc || return 1
|
|
|
|
# Set global variables
|
|
INTEL_ARC_DRM_CARD="$arc_card"
|
|
INTEL_ARC_VK_DEVICE=$(get_vk_device_id "$arc_pci")
|
|
|
|
if $arc_has_display; then
|
|
info "Monitor connected to Intel Arc: $INTEL_ARC_DRM_CARD"
|
|
else
|
|
warn "No monitor detected on Intel Arc, but will use: $INTEL_ARC_DRM_CARD"
|
|
fi
|
|
|
|
[[ -n "$INTEL_ARC_VK_DEVICE" ]] && info "Vulkan device ID: $INTEL_ARC_VK_DEVICE"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Verify Intel Arc GPU is present and detect display GPU
|
|
check_intel_arc() {
|
|
local gpu_info
|
|
gpu_info=$(lspci 2>/dev/null | grep -iE 'vga|3d|display' || echo "")
|
|
|
|
# Verify Intel GPU presence
|
|
if ! echo "$gpu_info" | grep -iq intel; then
|
|
die "No Intel GPU detected. This script is for Intel Arc dGPUs only."
|
|
fi
|
|
|
|
# Check for Arc-specific patterns
|
|
if ! echo "$gpu_info" | grep -iqE 'arc|alchemist|battlemage|celestial'; then
|
|
local has_xe=false
|
|
for card in /sys/class/drm/card[0-9]*/device/driver; do
|
|
if [[ -L "$card" ]]; then
|
|
local driver
|
|
driver=$(basename "$(readlink "$card")")
|
|
if [[ "$driver" == "xe" ]]; then
|
|
has_xe=true
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
$has_xe || warn "No Intel Arc pattern found in lspci. Checking for discrete Intel GPU..."
|
|
fi
|
|
|
|
# Find Arc dGPU with display
|
|
if ! find_intel_arc_display_gpu; then
|
|
die "No Intel Arc discrete GPU found. This script is for Intel Arc dGPUs only."
|
|
fi
|
|
|
|
info "Intel Arc dGPU detected and selected: $INTEL_ARC_DRM_CARD"
|
|
return 0
|
|
}
|
|
|
|
###############################################################################
|
|
# MULTILIB REPOSITORY
|
|
###############################################################################
|
|
|
|
enable_multilib_repo() {
|
|
info "Enabling multilib repository..."
|
|
sudo cp /etc/pacman.conf "/etc/pacman.conf.backup.$(date +%Y%m%d%H%M%S)" || die "Failed to backup pacman.conf"
|
|
# Uncomment only the [multilib] header and its Include line
|
|
sudo sed -i '/^#\[multilib\]$/{s/^#//;n;s/^#//}' /etc/pacman.conf || die "Failed to enable multilib"
|
|
|
|
if grep -q "^\[multilib\]" /etc/pacman.conf 2>/dev/null; then
|
|
info "Multilib repository enabled successfully"
|
|
sudo pacman -Syu --noconfirm || die "Failed to update system"
|
|
else
|
|
die "Failed to enable multilib repository"
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# MESA MANAGEMENT
|
|
###############################################################################
|
|
|
|
rollback_to_stable_mesa() {
|
|
info "Rolling back from mesa-git to stable mesa..."
|
|
|
|
# Identify installed mesa-git packages
|
|
local -a git_pkgs_to_remove=()
|
|
check_package "lib32-mesa-git" && git_pkgs_to_remove+=("lib32-mesa-git")
|
|
check_package "mesa-git" && git_pkgs_to_remove+=("mesa-git")
|
|
|
|
# Remove mesa-git packages (lib32 first due to dependency)
|
|
if ((${#git_pkgs_to_remove[@]})); then
|
|
info "Removing mesa-git packages: ${git_pkgs_to_remove[*]}"
|
|
|
|
if check_package "lib32-mesa-git"; then
|
|
sudo pacman -Rdd --noconfirm lib32-mesa-git 2>/dev/null || warn "Failed to remove lib32-mesa-git"
|
|
fi
|
|
|
|
if check_package "mesa-git"; then
|
|
sudo pacman -Rdd --noconfirm mesa-git 2>/dev/null || die "Failed to remove mesa-git"
|
|
fi
|
|
fi
|
|
|
|
# Install stable mesa packages
|
|
info "Installing stable mesa packages..."
|
|
local -a stable_pkgs=("mesa" "vulkan-intel" "vulkan-mesa-layers")
|
|
|
|
if grep -q "^\[multilib\]" /etc/pacman.conf 2>/dev/null; then
|
|
stable_pkgs+=("lib32-mesa" "lib32-vulkan-intel" "lib32-vulkan-mesa-layers")
|
|
fi
|
|
|
|
sudo pacman -S --needed --noconfirm "${stable_pkgs[@]}" || \
|
|
die "Failed to install stable mesa packages. System may be in broken state!
|
|
Try manually: sudo pacman -S ${stable_pkgs[*]}"
|
|
|
|
# Verify installation
|
|
if check_package "mesa"; then
|
|
info "Rollback complete - stable mesa installed"
|
|
else
|
|
die "Rollback verification failed - mesa not installed"
|
|
fi
|
|
}
|
|
|
|
install_mesa_git() {
|
|
local multilib_enabled="$1"
|
|
|
|
info "Installing mesa-git from AUR (recommended for Intel Arc)..."
|
|
|
|
# Install build tools
|
|
info "Ensuring build tools are installed..."
|
|
sudo pacman -S --needed --noconfirm base-devel git || die "Failed to install build tools"
|
|
|
|
# Find AUR helper
|
|
local aur_helper=""
|
|
if command -v yay >/dev/null 2>&1 && check_aur_helper_functional yay; then
|
|
aur_helper="yay"
|
|
elif command -v paru >/dev/null 2>&1 && check_aur_helper_functional paru; then
|
|
aur_helper="paru"
|
|
fi
|
|
|
|
[[ -z "$aur_helper" ]] && die "No AUR helper found (yay or paru required for mesa-git). Install one first:
|
|
git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si"
|
|
|
|
info "Using AUR helper: $aur_helper"
|
|
|
|
# Check for and remove conflicting packages
|
|
local -a potential_conflicts=(
|
|
"lib32-vulkan-mesa-implicit-layers" "lib32-vulkan-mesa-layers" "lib32-vulkan-intel" "lib32-mesa"
|
|
"vulkan-mesa-implicit-layers" "vulkan-mesa-layers" "vulkan-intel" "mesa"
|
|
)
|
|
local -a found_conflicts=()
|
|
for pkg in "${potential_conflicts[@]}"; do
|
|
pacman -Qi "$pkg" &>/dev/null && found_conflicts+=("$pkg")
|
|
done
|
|
|
|
if ((${#found_conflicts[@]})); then
|
|
info "Removing conflicting packages: ${found_conflicts[*]}"
|
|
_MESA_REMOVAL_IN_PROGRESS=1
|
|
# Use -Rdd to remove without dependency checks (mesa-git will satisfy deps)
|
|
sudo pacman -Rdd --noconfirm "${found_conflicts[@]}" || \
|
|
die "Failed to remove conflicting packages: ${found_conflicts[*]}. Cannot install mesa-git with conflicting packages still present."
|
|
# Verify removal
|
|
sleep 1
|
|
for pkg in "${found_conflicts[@]}"; do
|
|
if pacman -Qi "$pkg" &>/dev/null; then
|
|
warn "Retrying removal of $pkg..."
|
|
sudo pacman -Rdd --noconfirm "$pkg" || die "Failed to remove $pkg - cannot continue with mesa-git install"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Clear AUR helper cache for mesa-git
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/yay/mesa-git" 2>/dev/null || true
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/paru/clone/mesa-git" 2>/dev/null || true
|
|
|
|
# Install mesa-git (lib32-mesa-git depends on it)
|
|
info "Building and installing mesa-git (this may take a while)..."
|
|
if ! run_as_user "$aur_helper" -S --noconfirm --removemake --cleanafter --overwrite '/usr/lib/*' \
|
|
--answeredit None --answerclean None --answerdiff None mesa-git; then
|
|
# Check for working mesa driver
|
|
if ! pacman -Qi mesa-git &>/dev/null && ! pacman -Qi mesa &>/dev/null; then
|
|
die "Failed to install mesa-git and no mesa driver is installed!
|
|
Run: sudo pacman -S mesa lib32-mesa vulkan-intel lib32-vulkan-intel"
|
|
fi
|
|
die "Failed to install mesa-git"
|
|
fi
|
|
|
|
# Verify installation
|
|
pacman -Qi mesa-git &>/dev/null || die "mesa-git installation verification failed"
|
|
info "mesa-git installed successfully"
|
|
|
|
# Install lib32-mesa-git if multilib enabled (REQUIRED for 32-bit games/Steam)
|
|
# NOTE: lib32-mesa-git MUST be used with mesa-git - stable lib32-vulkan-intel is incompatible!
|
|
if [[ "$multilib_enabled" == "true" ]]; then
|
|
info "Building and installing lib32-mesa-git (required for Steam)..."
|
|
info "This may take 10-30 minutes to compile..."
|
|
|
|
# Remove conflicting lib32 packages BEFORE attempting install
|
|
# This prevents the interactive "Remove lib32-mesa? [y/N]" prompt
|
|
# which --noconfirm defaults to N (abort)
|
|
local -a _lib32_conflicts=()
|
|
for _pkg in lib32-vulkan-mesa-implicit-layers lib32-vulkan-mesa-layers \
|
|
lib32-vulkan-intel lib32-mesa; do
|
|
pacman -Qi "$_pkg" &>/dev/null && _lib32_conflicts+=("$_pkg")
|
|
done
|
|
if ((${#_lib32_conflicts[@]})); then
|
|
info "Removing conflicting lib32 packages: ${_lib32_conflicts[*]}"
|
|
sudo pacman -Rdd --noconfirm "${_lib32_conflicts[@]}" || \
|
|
die "Failed to remove conflicting lib32 packages"
|
|
sleep 1
|
|
fi
|
|
|
|
local max_attempts=2
|
|
local attempt=1
|
|
local lib32_success=false
|
|
|
|
while [[ $attempt -le $max_attempts ]] && [[ "$lib32_success" == "false" ]]; do
|
|
# On retries, re-check for conflicts (may have been reinstalled by failed build)
|
|
if [[ $attempt -gt 1 ]]; then
|
|
local -a _retry_conflicts=()
|
|
for _pkg in lib32-vulkan-mesa-implicit-layers lib32-vulkan-mesa-layers \
|
|
lib32-vulkan-intel lib32-mesa; do
|
|
pacman -Qi "$_pkg" &>/dev/null && _retry_conflicts+=("$_pkg")
|
|
done
|
|
if ((${#_retry_conflicts[@]})); then
|
|
info "Removing conflicting lib32 packages: ${_retry_conflicts[*]}"
|
|
sudo pacman -Rdd --noconfirm "${_retry_conflicts[@]}" 2>/dev/null || true
|
|
sleep 1
|
|
fi
|
|
fi
|
|
|
|
# Clear AUR helper cache for lib32-mesa-git to force fresh install
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/yay/lib32-mesa-git" 2>/dev/null || true
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/paru/clone/lib32-mesa-git" 2>/dev/null || true
|
|
|
|
if run_as_user "$aur_helper" -S --noconfirm --removemake --cleanafter --overwrite '/usr/lib32/*' \
|
|
--answeredit None --answerclean None --answerdiff None lib32-mesa-git; then
|
|
lib32_success=true
|
|
info "lib32-mesa-git installed successfully"
|
|
else
|
|
warn "lib32-mesa-git build attempt $attempt failed"
|
|
((attempt++))
|
|
[[ $attempt -le $max_attempts ]] && info "Retrying..."
|
|
fi
|
|
done
|
|
|
|
if [[ "$lib32_success" == "false" ]]; then
|
|
die "Failed to install lib32-mesa-git after $max_attempts attempts.
|
|
This is REQUIRED when using mesa-git (stable lib32-vulkan-intel is incompatible).
|
|
|
|
To fix manually:
|
|
1. $aur_helper -S lib32-mesa-git
|
|
|
|
Or rollback to stable mesa:
|
|
1. sudo pacman -Rdd mesa-git
|
|
2. sudo pacman -S mesa lib32-mesa vulkan-intel lib32-vulkan-intel"
|
|
fi
|
|
fi
|
|
|
|
_MESA_REMOVAL_IN_PROGRESS=0
|
|
info "mesa-git installation complete"
|
|
}
|
|
|
|
###############################################################################
|
|
# STEAM DEPENDENCIES
|
|
###############################################################################
|
|
|
|
check_steam_dependencies() {
|
|
info "Checking Steam dependencies for Intel Arc..."
|
|
|
|
#---------------------------------------------------------------------------
|
|
# System Update
|
|
#---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " SYSTEM UPDATE RECOMMENDED"
|
|
echo "================================================================"
|
|
echo ""
|
|
read -p "Upgrade system now? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
info "Upgrading system..."
|
|
sudo pacman -Syu --noconfirm || die "Failed to upgrade system"
|
|
fi
|
|
echo ""
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Mesa Driver Selection
|
|
#---------------------------------------------------------------------------
|
|
local current_mesa="none"
|
|
local has_mesa_git=false
|
|
|
|
if check_package "mesa-git"; then
|
|
current_mesa="mesa-git"
|
|
has_mesa_git=true
|
|
elif check_package "mesa"; then
|
|
current_mesa="stable"
|
|
fi
|
|
|
|
echo "================================================================"
|
|
echo " MESA DRIVER SELECTION"
|
|
echo "================================================================"
|
|
echo ""
|
|
[[ "$current_mesa" != "none" ]] && echo " Currently installed: $current_mesa" && echo ""
|
|
echo " Choose your Mesa driver:"
|
|
echo ""
|
|
echo " [1] mesa-git (Recommended for Intel Arc)"
|
|
echo " - Latest drivers from Mesa development branch"
|
|
echo " - Best performance and newest fixes for Arc GPUs"
|
|
echo " - Built from AUR (takes longer to install/update)"
|
|
echo ""
|
|
echo " [2] Stable mesa"
|
|
echo " - Official Arch Linux packages"
|
|
echo " - Faster to install, standard updates"
|
|
echo " - May lack latest Intel Arc optimizations"
|
|
echo ""
|
|
read -p "Select driver [1/2] (default: 1): " -n 1 -r mesa_choice
|
|
echo
|
|
|
|
if [[ "$mesa_choice" == "2" ]]; then
|
|
USE_MESA_GIT=0
|
|
info "Using stable mesa packages"
|
|
|
|
# Handle rollback if switching from mesa-git
|
|
if $has_mesa_git; then
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " ROLLBACK: mesa-git -> stable mesa"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " This will:"
|
|
echo " - Remove mesa-git and lib32-mesa-git"
|
|
echo " - Install stable mesa, lib32-mesa, vulkan-intel, lib32-vulkan-intel"
|
|
echo ""
|
|
read -p "Proceed with rollback? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
rollback_to_stable_mesa
|
|
else
|
|
warn "Rollback cancelled - keeping mesa-git"
|
|
USE_MESA_GIT=1
|
|
fi
|
|
fi
|
|
else
|
|
USE_MESA_GIT=1
|
|
info "Using mesa-git from AUR"
|
|
fi
|
|
echo ""
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Multilib Repository Check
|
|
#---------------------------------------------------------------------------
|
|
local -a missing_deps=()
|
|
local -a optional_deps=()
|
|
local multilib_enabled=false
|
|
|
|
if grep -q "^\[multilib\]" /etc/pacman.conf 2>/dev/null; then
|
|
multilib_enabled=true
|
|
info "Multilib repository: enabled"
|
|
else
|
|
err "Multilib repository: NOT enabled (required for Steam)"
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " MULTILIB REPOSITORY REQUIRED"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " Steam requires 32-bit libraries from the multilib repository."
|
|
echo ""
|
|
read -p "Enable multilib repository now? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
enable_multilib_repo
|
|
multilib_enabled=true
|
|
else
|
|
die "Multilib repository is required for Steam"
|
|
fi
|
|
fi
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Define Package Lists
|
|
#---------------------------------------------------------------------------
|
|
local -a core_deps=(
|
|
"steam"
|
|
"lib32-vulkan-icd-loader"
|
|
"vulkan-icd-loader"
|
|
"mesa-utils"
|
|
"lib32-glibc"
|
|
"lib32-gcc-libs"
|
|
"lib32-libx11"
|
|
"lib32-libxss"
|
|
"lib32-alsa-plugins"
|
|
"lib32-libpulse"
|
|
"lib32-openal"
|
|
"lib32-nss"
|
|
"lib32-libcups"
|
|
"lib32-sdl2-compat"
|
|
"lib32-freetype2"
|
|
"lib32-fontconfig"
|
|
"lib32-libnm"
|
|
"networkmanager"
|
|
"gamemode"
|
|
"lib32-gamemode"
|
|
"ttf-liberation"
|
|
"xdg-user-dirs"
|
|
"kbd"
|
|
)
|
|
|
|
# Add stable mesa if not using mesa-git
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 0 ]]; then
|
|
core_deps+=("mesa" "lib32-mesa")
|
|
fi
|
|
|
|
local -a gpu_deps=(
|
|
"intel-media-driver"
|
|
"vulkan-tools"
|
|
)
|
|
|
|
# Add stable vulkan-intel if not using mesa-git
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 0 ]]; then
|
|
gpu_deps+=("vulkan-intel" "lib32-vulkan-intel" "vulkan-mesa-layers")
|
|
fi
|
|
|
|
local -a recommended_deps=(
|
|
"gamescope"
|
|
"mangohud"
|
|
"lib32-mangohud"
|
|
"proton-ge-custom-bin"
|
|
"proton-cachyos-slr"
|
|
"udisks2"
|
|
)
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Check Dependencies
|
|
#---------------------------------------------------------------------------
|
|
info "Checking core Steam dependencies..."
|
|
for dep in "${core_deps[@]}"; do
|
|
check_package "$dep" || missing_deps+=("$dep")
|
|
done
|
|
|
|
info "Checking Intel GPU dependencies..."
|
|
for dep in "${gpu_deps[@]}"; do
|
|
check_package "$dep" || missing_deps+=("$dep")
|
|
done
|
|
|
|
# Check mesa-git packages
|
|
local mesa_git_needed=false
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 1 ]]; then
|
|
info "Checking mesa-git (AUR)..."
|
|
if ! check_package "mesa-git"; then
|
|
mesa_git_needed=true
|
|
info "mesa-git: not installed (will install from AUR)"
|
|
else
|
|
local current_mesa_ver
|
|
current_mesa_ver=$(pacman -Q mesa-git 2>/dev/null | awk '{print $2}')
|
|
info "mesa-git: already installed ($current_mesa_ver)"
|
|
echo ""
|
|
read -p "Rebuild mesa-git from latest source? [y/N]: " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
mesa_git_needed=true
|
|
info "mesa-git will be rebuilt from latest source"
|
|
fi
|
|
fi
|
|
|
|
if $multilib_enabled && ! check_package "lib32-mesa-git"; then
|
|
mesa_git_needed=true
|
|
info "lib32-mesa-git: not installed (will install from AUR)"
|
|
elif $multilib_enabled; then
|
|
info "lib32-mesa-git: already installed"
|
|
fi
|
|
fi
|
|
|
|
info "Checking recommended dependencies..."
|
|
for dep in "${recommended_deps[@]}"; do
|
|
check_package "$dep" || optional_deps+=("$dep")
|
|
done
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Display Results
|
|
#---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " STEAM DEPENDENCY CHECK RESULTS"
|
|
echo "================================================================"
|
|
echo ""
|
|
|
|
# Clean missing deps array
|
|
local -a clean_missing=()
|
|
for item in "${missing_deps[@]}"; do
|
|
[[ -n "$item" && "$item" != "multilib-repository" ]] && clean_missing+=("$item")
|
|
done
|
|
missing_deps=("${clean_missing[@]+"${clean_missing[@]}"}")
|
|
|
|
# Show mesa driver status
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 1 ]]; then
|
|
echo " MESA DRIVER: mesa-git (Intel Arc optimized)"
|
|
if $mesa_git_needed; then
|
|
echo " - Will be built and installed from AUR"
|
|
else
|
|
echo " - Already installed"
|
|
fi
|
|
else
|
|
echo " MESA DRIVER: Stable mesa (official packages)"
|
|
fi
|
|
echo ""
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Install mesa-git (FIRST, before other packages)
|
|
#---------------------------------------------------------------------------
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 1 ]] && $mesa_git_needed; then
|
|
echo "================================================================"
|
|
echo " MESA-GIT INSTALLATION (AUR)"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " Building mesa-git from AUR..."
|
|
echo " This may take 10-30 minutes depending on your system."
|
|
echo ""
|
|
install_mesa_git "$multilib_enabled"
|
|
echo ""
|
|
fi
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Verify mesa-git provides required vulkan drivers before continuing
|
|
#---------------------------------------------------------------------------
|
|
if [[ "${USE_MESA_GIT:-1}" -eq 1 ]]; then
|
|
if ! pacman -Qi mesa-git &>/dev/null; then
|
|
die "mesa-git is not installed. Cannot continue."
|
|
fi
|
|
if [[ "$multilib_enabled" == "true" ]] && ! pacman -Qi lib32-mesa-git &>/dev/null; then
|
|
die "lib32-mesa-git is not installed but is required for Steam.
|
|
Run: yay -S lib32-mesa-git"
|
|
fi
|
|
info "Verified: mesa-git and lib32-mesa-git are installed"
|
|
fi
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Install Missing Packages
|
|
#---------------------------------------------------------------------------
|
|
if ((${#missing_deps[@]})); then
|
|
echo " MISSING REQUIRED PACKAGES (${#missing_deps[@]}):"
|
|
for dep in "${missing_deps[@]}"; do
|
|
echo " - $dep"
|
|
done
|
|
echo ""
|
|
|
|
read -p "Install missing required packages? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
info "Installing missing dependencies..."
|
|
sudo pacman -S --needed --noconfirm "${missing_deps[@]}" || die "Failed to install dependencies"
|
|
info "Required dependencies installed successfully"
|
|
else
|
|
die "Missing required Steam dependencies"
|
|
fi
|
|
else
|
|
info "All required pacman dependencies are installed!"
|
|
fi
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Install Optional Packages
|
|
#---------------------------------------------------------------------------
|
|
echo ""
|
|
if ((${#optional_deps[@]})); then
|
|
echo " RECOMMENDED PACKAGES (${#optional_deps[@]}):"
|
|
for dep in "${optional_deps[@]}"; do
|
|
echo " - $dep"
|
|
done
|
|
echo ""
|
|
|
|
read -p "Install recommended packages? [y/N]: " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
info "Installing recommended packages..."
|
|
|
|
local -a pacman_optional=()
|
|
local -a aur_optional=()
|
|
for dep in "${optional_deps[@]}"; do
|
|
if pacman -Si "$dep" &>/dev/null; then
|
|
pacman_optional+=("$dep")
|
|
else
|
|
aur_optional+=("$dep")
|
|
fi
|
|
done
|
|
|
|
if ((${#pacman_optional[@]})); then
|
|
sudo pacman -S --needed --noconfirm "${pacman_optional[@]}" || info "Some optional packages failed"
|
|
fi
|
|
|
|
if ((${#aur_optional[@]})); then
|
|
local aur_helper=""
|
|
command -v yay >/dev/null 2>&1 && check_aur_helper_functional yay && aur_helper="yay"
|
|
[[ -z "$aur_helper" ]] && command -v paru >/dev/null 2>&1 && check_aur_helper_functional paru && aur_helper="paru"
|
|
|
|
if [[ -n "$aur_helper" ]]; then
|
|
read -p "Install AUR packages with $aur_helper? [y/N]: " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
run_as_user "$aur_helper" -S --needed --noconfirm "${aur_optional[@]}" || info "Some AUR packages failed"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
check_steam_config
|
|
}
|
|
|
|
###############################################################################
|
|
# STEAM CONFIGURATION
|
|
###############################################################################
|
|
|
|
check_steam_config() {
|
|
info "Checking Steam configuration..."
|
|
|
|
# Check the real user's groups (not root's when run with sudo)
|
|
local user_groups
|
|
user_groups=$(id -Gn "$REAL_USER" 2>/dev/null || groups "$REAL_USER" 2>/dev/null || echo "")
|
|
|
|
local missing_groups=()
|
|
echo "$user_groups" | grep -qw 'video' || missing_groups+=("video")
|
|
echo "$user_groups" | grep -qw 'input' || missing_groups+=("input")
|
|
echo "$user_groups" | grep -qw 'wheel' || missing_groups+=("wheel")
|
|
|
|
if ((${#missing_groups[@]})); then
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " USER GROUP PERMISSIONS"
|
|
echo "================================================================"
|
|
echo ""
|
|
read -p "Add $REAL_USER to ${missing_groups[*]} group(s)? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
local groups_to_add
|
|
groups_to_add=$(IFS=,; echo "${missing_groups[*]}")
|
|
if sudo usermod -aG "$groups_to_add" "$REAL_USER"; then
|
|
info "Successfully added $REAL_USER to group(s): $groups_to_add"
|
|
NEEDS_RELOGIN=1
|
|
fi
|
|
fi
|
|
else
|
|
info "User $REAL_USER is in video, input, and wheel groups - permissions OK"
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# PERFORMANCE CONFIGURATION
|
|
###############################################################################
|
|
|
|
setup_performance_permissions() {
|
|
local udev_rules_file="/etc/udev/rules.d/99-gaming-performance.rules"
|
|
local sudoers_file="/etc/sudoers.d/gaming-mode-sysctl"
|
|
|
|
if [[ -f "$udev_rules_file" ]] && [[ -f "$sudoers_file" ]]; then
|
|
info "Performance permissions already configured"
|
|
return 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " PERFORMANCE PERMISSIONS SETUP"
|
|
echo "================================================================"
|
|
echo ""
|
|
read -p "Set up passwordless performance controls? [Y/n]: " -n 1 -r
|
|
echo
|
|
|
|
[[ $REPLY =~ ^[Nn]$ ]] && { info "Skipping permissions setup"; return 0; }
|
|
|
|
# Udev rules for GPU frequency control
|
|
if [[ ! -f "$udev_rules_file" ]]; then
|
|
info "Creating udev rules for Intel Arc performance control..."
|
|
sudo tee "$udev_rules_file" > /dev/null <<'UDEV_RULES'
|
|
# Gaming Mode Performance Control Rules - Intel Arc
|
|
# Group-writable (video group) instead of world-writable for security
|
|
KERNEL=="cpu[0-9]*", SUBSYSTEM=="cpu", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/devices/system/cpu/%k/cpufreq/scaling_governor && chmod 664 /sys/devices/system/cpu/%k/cpufreq/scaling_governor'"
|
|
# Intel Xe driver (Arc GPUs)
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="xe", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_boost_freq_mhz && chmod 664 /sys/class/drm/%k/gt_boost_freq_mhz'"
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="xe", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_min_freq_mhz && chmod 664 /sys/class/drm/%k/gt_min_freq_mhz'"
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="xe", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_max_freq_mhz && chmod 664 /sys/class/drm/%k/gt_max_freq_mhz'"
|
|
# Fallback for i915 driver
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="i915", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_boost_freq_mhz && chmod 664 /sys/class/drm/%k/gt_boost_freq_mhz'"
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="i915", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_min_freq_mhz && chmod 664 /sys/class/drm/%k/gt_min_freq_mhz'"
|
|
KERNEL=="card[0-9]", SUBSYSTEM=="drm", DRIVERS=="i915", ACTION=="add", RUN+="/bin/sh -c 'chgrp video /sys/class/drm/%k/gt_max_freq_mhz && chmod 664 /sys/class/drm/%k/gt_max_freq_mhz'"
|
|
UDEV_RULES
|
|
sudo udevadm control --reload-rules || true
|
|
sudo udevadm trigger --subsystem-match=cpu --subsystem-match=drm || true
|
|
fi
|
|
|
|
# Sudoers rules for sysctl
|
|
if [[ ! -f "$sudoers_file" ]]; then
|
|
info "Creating sudoers rule for Performance Mode..."
|
|
sudo tee "$sudoers_file" > /dev/null << 'SUDOERS_PERF'
|
|
# Gaming Mode - Allow passwordless sysctl for performance tuning
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w kernel.sched_autogroup_enabled=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w kernel.sched_migration_cost_ns=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w kernel.sched_min_granularity_ns=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w kernel.sched_latency_ns=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w vm.swappiness=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w vm.dirty_ratio=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w vm.dirty_background_ratio=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w vm.dirty_writeback_centisecs=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w vm.dirty_expire_centisecs=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w fs.inotify.max_user_watches=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w fs.inotify.max_user_instances=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w fs.file-max=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w net.core.rmem_max=*
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w net.core.wmem_max=*
|
|
SUDOERS_PERF
|
|
sudo chmod 0440 "$sudoers_file"
|
|
fi
|
|
|
|
# Memory lock limits
|
|
local memlock_file="/etc/security/limits.d/99-gaming-memlock.conf"
|
|
if [[ ! -f "$memlock_file" ]]; then
|
|
info "Creating memlock limits..."
|
|
# Set memlock to ~25% of total RAM (in KB)
|
|
local total_ram_kb
|
|
total_ram_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
|
|
local memlock_kb=$(( total_ram_kb / 4 ))
|
|
# Clamp: minimum 2GB, maximum 16GB
|
|
(( memlock_kb < 2097152 )) && memlock_kb=2097152
|
|
(( memlock_kb > 16777216 )) && memlock_kb=16777216
|
|
info "Setting memlock to $(( memlock_kb / 1024 ))MB (based on $(( total_ram_kb / 1024 ))MB total RAM)"
|
|
sudo tee "$memlock_file" > /dev/null << MEMLOCKCONF
|
|
# Gaming memlock limits (auto-calculated: ~25% of total RAM)
|
|
* soft memlock ${memlock_kb}
|
|
* hard memlock ${memlock_kb}
|
|
MEMLOCKCONF
|
|
fi
|
|
|
|
# PipeWire low-latency config
|
|
local pipewire_conf_dir="/etc/pipewire/pipewire.conf.d"
|
|
local pipewire_conf="$pipewire_conf_dir/10-gaming-latency.conf"
|
|
if [[ ! -f "$pipewire_conf" ]]; then
|
|
info "Creating PipeWire low-latency configuration..."
|
|
sudo mkdir -p "$pipewire_conf_dir"
|
|
sudo tee "$pipewire_conf" > /dev/null << 'PIPEWIRECONF'
|
|
# Low-latency PipeWire tuning
|
|
context.properties = {
|
|
default.clock.min-quantum = 256
|
|
}
|
|
PIPEWIRECONF
|
|
fi
|
|
|
|
info "Performance permissions configured"
|
|
}
|
|
|
|
setup_shader_cache() {
|
|
local env_file="/etc/environment.d/99-shader-cache.conf"
|
|
|
|
if [[ -f "$env_file" ]]; then
|
|
info "Shader cache configuration already exists"
|
|
return 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " SHADER CACHE OPTIMIZATION"
|
|
echo "================================================================"
|
|
echo ""
|
|
read -p "Configure shader cache optimization? [Y/n]: " -n 1 -r
|
|
echo
|
|
|
|
[[ $REPLY =~ ^[Nn]$ ]] && return 0
|
|
|
|
info "Creating shader cache configuration..."
|
|
sudo mkdir -p /etc/environment.d
|
|
sudo tee "$env_file" > /dev/null << 'SHADERCACHE'
|
|
# Shader cache tuning for Intel Arc
|
|
MESA_SHADER_CACHE_MAX_SIZE=12G
|
|
MESA_SHADER_CACHE_DISABLE_CLEANUP=1
|
|
__GL_SHADER_DISK_CACHE=1
|
|
__GL_SHADER_DISK_CACHE_SIZE=12884901888
|
|
__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1
|
|
DXVK_STATE_CACHE=1
|
|
SHADERCACHE
|
|
sudo chmod 644 "$env_file"
|
|
info "Shader cache configured for Intel Arc"
|
|
}
|
|
|
|
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=()
|
|
|
|
for pkg in "${required_packages[@]}"; do
|
|
check_package "$pkg" || packages_to_install+=("$pkg")
|
|
done
|
|
|
|
if ((${#packages_to_install[@]})); then
|
|
info "The following packages are required: ${packages_to_install[*]}"
|
|
read -p "Install missing packages? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
sudo pacman -S --needed --noconfirm "${packages_to_install[@]}" || die "package install failed"
|
|
else
|
|
die "Required packages missing"
|
|
fi
|
|
else
|
|
info "All required packages present."
|
|
fi
|
|
|
|
setup_performance_permissions
|
|
setup_shader_cache
|
|
|
|
# Grant cap_sys_nice to gamescope
|
|
if [[ "${PERFORMANCE_MODE,,}" == "enabled" ]] && command -v gamescope >/dev/null 2>&1; then
|
|
if ! getcap "$(command -v gamescope)" 2>/dev/null | grep -q 'cap_sys_nice'; then
|
|
echo ""
|
|
read -p "Grant cap_sys_nice to gamescope? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
sudo setcap 'cap_sys_nice=eip' "$(command -v gamescope)" || warn "Failed to set capability"
|
|
info "Capability granted to gamescope"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# SESSION SWITCHING
|
|
###############################################################################
|
|
|
|
setup_session_switching() {
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " SESSION SWITCHING SETUP (Hyprland <-> Gamescope)"
|
|
echo " Intel Arc dGPU Configuration"
|
|
echo "================================================================"
|
|
echo ""
|
|
read -p "Set up session switching? [Y/n]: " -n 1 -r
|
|
echo
|
|
[[ $REPLY =~ ^[Nn]$ ]] && return 0
|
|
|
|
# Use global REAL_USER and REAL_HOME for consistency
|
|
local current_user="$REAL_USER"
|
|
local user_home="$REAL_HOME"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Detect Monitor Resolution
|
|
#---------------------------------------------------------------------------
|
|
local monitor_width=1920
|
|
local monitor_height=1080
|
|
local monitor_refresh=60
|
|
local monitor_output=""
|
|
|
|
if command -v hyprctl >/dev/null 2>&1; then
|
|
local monitor_json
|
|
monitor_json=$(hyprctl monitors -j 2>/dev/null)
|
|
if [[ -n "$monitor_json" ]]; then
|
|
if command -v jq >/dev/null 2>&1; then
|
|
monitor_width=$(echo "$monitor_json" | jq -r '.[0].width // 1920') || monitor_width=1920
|
|
monitor_height=$(echo "$monitor_json" | jq -r '.[0].height // 1080') || monitor_height=1080
|
|
monitor_refresh=$(echo "$monitor_json" | jq -r '.[0].refreshRate // 60 | floor') || monitor_refresh=60
|
|
monitor_output=$(echo "$monitor_json" | jq -r '.[0].name // ""') || monitor_output=""
|
|
else
|
|
# Fallback: parse JSON without jq
|
|
monitor_width=$(echo "$monitor_json" | grep -o '"width":[[:space:]]*[0-9]*' | head -1 | grep -o '[0-9]*$') || monitor_width=1920
|
|
monitor_height=$(echo "$monitor_json" | grep -o '"height":[[:space:]]*[0-9]*' | head -1 | grep -o '[0-9]*$') || monitor_height=1080
|
|
# refreshRate can be decimal (e.g., 143.998), extract integer part
|
|
monitor_refresh=$(echo "$monitor_json" | grep -o '"refreshRate":[[:space:]]*[0-9.]*' | head -1 | grep -o '[0-9.]*$' | cut -d. -f1) || monitor_refresh=60
|
|
monitor_output=$(echo "$monitor_json" | grep -o '"name":[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/') || monitor_output=""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
info "Detected display: ${monitor_width}x${monitor_height}@${monitor_refresh}Hz${monitor_output:+ on $monitor_output}"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Install ChimeraOS Packages
|
|
#---------------------------------------------------------------------------
|
|
info "Checking for ChimeraOS gamescope-session packages..."
|
|
|
|
local -a aur_packages=()
|
|
local -a packages_to_remove=()
|
|
local -a steam_compat_scripts=(
|
|
"/usr/bin/steamos-session-select"
|
|
"/usr/bin/steamos-update"
|
|
"/usr/bin/jupiter-biosupdate"
|
|
"/usr/bin/steamos-select-branch"
|
|
)
|
|
|
|
# Check gamescope-session base package
|
|
if ! check_package "gamescope-session-git" && ! check_package "gamescope-session"; then
|
|
aur_packages+=("gamescope-session-git")
|
|
fi
|
|
|
|
# Check gamescope-session-steam package
|
|
if ! check_package "gamescope-session-steam-git"; then
|
|
if check_package "gamescope-session-steam"; then
|
|
warn "gamescope-session-steam (non-git) is installed but may be missing Steam compatibility scripts"
|
|
local scripts_missing=false
|
|
for script in "${steam_compat_scripts[@]}"; do
|
|
[[ ! -f "$script" ]] && { scripts_missing=true; break; }
|
|
done
|
|
$scripts_missing && packages_to_remove+=("gamescope-session-steam")
|
|
fi
|
|
aur_packages+=("gamescope-session-steam-git")
|
|
else
|
|
local scripts_missing=false
|
|
for script in "${steam_compat_scripts[@]}"; do
|
|
if [[ ! -f "$script" ]]; then
|
|
warn "gamescope-session-steam-git is installed but $script is missing!"
|
|
scripts_missing=true
|
|
break
|
|
fi
|
|
done
|
|
if $scripts_missing; then
|
|
warn "Reinstalling gamescope-session-steam-git to fix missing scripts..."
|
|
packages_to_remove+=("gamescope-session-steam-git")
|
|
aur_packages+=("gamescope-session-steam-git")
|
|
fi
|
|
fi
|
|
|
|
# Find AUR helper
|
|
local aur_helper=""
|
|
command -v yay >/dev/null 2>&1 && check_aur_helper_functional yay && aur_helper="yay"
|
|
[[ -z "$aur_helper" ]] && command -v paru >/dev/null 2>&1 && check_aur_helper_functional paru && aur_helper="paru"
|
|
|
|
# Remove problematic packages
|
|
if ((${#packages_to_remove[@]})) && [[ -n "$aur_helper" ]]; then
|
|
info "Removing incomplete packages: ${packages_to_remove[*]}"
|
|
sudo pacman -Rns --noconfirm "${packages_to_remove[@]}" 2>/dev/null || true
|
|
fi
|
|
|
|
# Install missing packages
|
|
if ((${#aur_packages[@]})); then
|
|
if [[ -n "$aur_helper" ]]; then
|
|
read -p "Install ChimeraOS session packages with $aur_helper? [Y/n]: " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
info "Installing ChimeraOS gamescope-session packages..."
|
|
run_as_user "$aur_helper" -S --noconfirm --overwrite '/usr/share/gamescope-session*' --overwrite '/usr/bin/steamos-*' --answeredit None --answerclean None --answerdiff None "${aur_packages[@]}" || \
|
|
err "Failed to install gamescope-session packages"
|
|
fi
|
|
else
|
|
warn "No AUR helper found (yay/paru). Please install manually: ${aur_packages[*]}"
|
|
fi
|
|
else
|
|
info "ChimeraOS gamescope-session packages already installed (correct -git versions)"
|
|
fi
|
|
|
|
#---------------------------------------------------------------------------
|
|
# NetworkManager Integration
|
|
#---------------------------------------------------------------------------
|
|
info "Setting up NetworkManager integration..."
|
|
|
|
if systemctl is-active --quiet iwd; then
|
|
sudo mkdir -p /etc/NetworkManager/conf.d
|
|
sudo tee /etc/NetworkManager/conf.d/10-iwd-backend.conf > /dev/null << 'NM_IWD_CONF'
|
|
[device]
|
|
wifi.backend=iwd
|
|
wifi.scan-rand-mac-address=no
|
|
[main]
|
|
plugins=ifupdown,keyfile
|
|
[ifupdown]
|
|
managed=false
|
|
[connection]
|
|
connection.autoconnect-slaves=0
|
|
NM_IWD_CONF
|
|
fi
|
|
|
|
# NM start script
|
|
local nm_start_script="/usr/local/bin/gamescope-nm-start"
|
|
sudo tee "$nm_start_script" > /dev/null << 'NM_START'
|
|
#!/bin/bash
|
|
NM_MARKER="/tmp/.gamescope-started-nm"
|
|
if ! systemctl is-active --quiet NetworkManager.service; then
|
|
if systemctl start NetworkManager.service; then
|
|
touch "$NM_MARKER"
|
|
for _ in {1..20}; do
|
|
nmcli general status &>/dev/null && break
|
|
sleep 0.5
|
|
done
|
|
fi
|
|
fi
|
|
NM_START
|
|
sudo chmod +x "$nm_start_script"
|
|
|
|
# NM stop script - also restores iwd and bluetooth after gaming session
|
|
local nm_stop_script="/usr/local/bin/gamescope-nm-stop"
|
|
sudo tee "$nm_stop_script" > /dev/null << 'NM_STOP'
|
|
#!/bin/bash
|
|
NM_MARKER="/tmp/.gamescope-started-nm"
|
|
if [ -f "$NM_MARKER" ]; then
|
|
rm -f "$NM_MARKER"
|
|
systemctl stop NetworkManager.service 2>/dev/null || true
|
|
fi
|
|
|
|
# Restore iwd (WiFi) and bluetooth if they are enabled but got disrupted
|
|
if systemctl is-enabled --quiet iwd.service 2>/dev/null; then
|
|
systemctl restart iwd.service 2>/dev/null || true
|
|
fi
|
|
if systemctl is-enabled --quiet bluetooth.service 2>/dev/null; then
|
|
systemctl restart bluetooth.service 2>/dev/null || true
|
|
fi
|
|
NM_STOP
|
|
sudo chmod +x "$nm_stop_script"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Polkit Rules
|
|
#---------------------------------------------------------------------------
|
|
local polkit_created=false
|
|
|
|
local polkit_rules="/etc/polkit-1/rules.d/50-gamescope-networkmanager.rules"
|
|
if ! sudo test -f "$polkit_rules"; then
|
|
sudo mkdir -p /etc/polkit-1/rules.d
|
|
sudo tee "$polkit_rules" > /dev/null << 'POLKIT_RULES'
|
|
polkit.addRule(function(action, subject) {
|
|
if ((action.id == "org.freedesktop.NetworkManager.enable-disable-network" ||
|
|
action.id == "org.freedesktop.NetworkManager.enable-disable-wifi" ||
|
|
action.id == "org.freedesktop.NetworkManager.network-control" ||
|
|
action.id == "org.freedesktop.NetworkManager.wifi.scan" ||
|
|
action.id == "org.freedesktop.NetworkManager.settings.modify.system" ||
|
|
action.id == "org.freedesktop.NetworkManager.settings.modify.own" ||
|
|
action.id == "org.freedesktop.NetworkManager.settings.modify.hostname") &&
|
|
subject.isInGroup("wheel")) {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
POLKIT_RULES
|
|
sudo chmod 644 "$polkit_rules"
|
|
polkit_created=true
|
|
fi
|
|
|
|
local udisks_polkit="/etc/polkit-1/rules.d/50-udisks-gaming.rules"
|
|
if ! sudo test -f "$udisks_polkit"; then
|
|
sudo tee "$udisks_polkit" > /dev/null << 'UDISKS_POLKIT'
|
|
polkit.addRule(function(action, subject) {
|
|
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
|
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
|
action.id == "org.freedesktop.udisks2.filesystem-unmount-others" ||
|
|
action.id == "org.freedesktop.udisks2.encrypted-unlock" ||
|
|
action.id == "org.freedesktop.udisks2.power-off-drive") &&
|
|
subject.isInGroup("wheel")) {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
UDISKS_POLKIT
|
|
sudo chmod 644 "$udisks_polkit"
|
|
polkit_created=true
|
|
fi
|
|
|
|
$polkit_created && { sudo systemctl restart polkit.service 2>/dev/null || true; info "Polkit rules created"; }
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Gamescope Session Configuration
|
|
#---------------------------------------------------------------------------
|
|
info "Creating gamescope-session-plus configuration for Intel Arc..."
|
|
|
|
local env_dir="${user_home}/.config/environment.d"
|
|
local gamescope_conf="${env_dir}/gamescope-session-plus.conf"
|
|
run_as_user mkdir -p "$env_dir"
|
|
|
|
local output_connector_line=""
|
|
[[ -n "$monitor_output" ]] && output_connector_line="OUTPUT_CONNECTOR=$monitor_output"
|
|
|
|
run_as_user tee "$gamescope_conf" > /dev/null << GAMESCOPE_CONF
|
|
# Gamescope Session Plus Configuration
|
|
# Generated by ARCGames Installer v${ARCGAMES_VERSION}
|
|
# Intel Arc dGPU Configuration
|
|
# NOTE: environment.d format does NOT use 'export' keyword
|
|
|
|
# Display configuration (managed by Steam - no hardcoded resolution)
|
|
${output_connector_line}
|
|
|
|
# Adaptive sync / VRR disabled
|
|
ADAPTIVE_SYNC=0
|
|
|
|
# Intel Arc Workarounds & Optimizations
|
|
# norbc = disable render buffer compression to avoid visual artifacts on Arc
|
|
INTEL_DEBUG=norbc
|
|
DISABLE_LAYER_MESA_ANTI_LAG=1
|
|
VKD3D_CONFIG=dxr11,dxr
|
|
mesa_glthread=true
|
|
ANV_QUEUE_THREAD_DISABLE=1
|
|
|
|
# Storage and drive management
|
|
STEAM_ALLOW_DRIVE_UNMOUNT=1
|
|
|
|
# Misc
|
|
FCITX_NO_WAYLAND_DIAGNOSE=1
|
|
SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0
|
|
GAMESCOPE_CONF
|
|
info "Created $gamescope_conf"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Session Wrapper Script
|
|
#---------------------------------------------------------------------------
|
|
local nm_wrapper="/usr/local/bin/gamescope-session-nm-wrapper"
|
|
sudo tee "$nm_wrapper" > /dev/null << 'NM_WRAPPER'
|
|
#!/bin/bash
|
|
# Gamescope session wrapper (NM + keybind monitor)
|
|
# Intel Arc configuration is handled via environment.d config file
|
|
|
|
log() { logger -t gamescope-wrapper "$*"; echo "$*"; }
|
|
|
|
cleanup() {
|
|
pkill -f steam-library-mount 2>/dev/null || true
|
|
pkill -f gaming-keybind-monitor 2>/dev/null || true
|
|
sudo -n /usr/local/bin/gamescope-nm-stop 2>/dev/null || true
|
|
rm -f /tmp/.gaming-session-active
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# Start NetworkManager
|
|
sudo -n /usr/local/bin/gamescope-nm-start 2>/dev/null || {
|
|
log "Warning: Could not start NetworkManager - Steam network features may not work"
|
|
}
|
|
|
|
# Start Steam library drive auto-mounter
|
|
if [[ -x /usr/local/bin/steam-library-mount ]]; then
|
|
/usr/local/bin/steam-library-mount &
|
|
log "Steam library drive monitor started"
|
|
else
|
|
log "Warning: steam-library-mount not found - external Steam libraries will not auto-mount"
|
|
fi
|
|
|
|
# Mark gaming session active
|
|
echo "gamescope" > /tmp/.gaming-session-active
|
|
|
|
# Pre-flight check for keybind monitor
|
|
keybind_ok=true
|
|
|
|
if ! python3 -c "import evdev" 2>/dev/null; then
|
|
log "WARNING: python-evdev not installed - Super+Shift+R keybind disabled"
|
|
log "Fix: sudo pacman -S python-evdev"
|
|
keybind_ok=false
|
|
fi
|
|
|
|
if ! groups | grep -qw input; then
|
|
log "WARNING: User not in 'input' group - Super+Shift+R keybind disabled"
|
|
log "Fix: sudo usermod -aG input $USER && log out/in"
|
|
keybind_ok=false
|
|
fi
|
|
|
|
if $keybind_ok && ! ls /dev/input/event* >/dev/null 2>&1; then
|
|
log "WARNING: No input devices accessible - Super+Shift+R keybind disabled"
|
|
keybind_ok=false
|
|
fi
|
|
|
|
if $keybind_ok; then
|
|
/usr/local/bin/gaming-keybind-monitor &
|
|
log "Keybind monitor started (Super+Shift+R to exit)"
|
|
else
|
|
log "Keybind monitor NOT started - use Steam > Power > Exit to Desktop instead"
|
|
fi
|
|
|
|
# Steam-specific environment variables
|
|
export QT_IM_MODULE=steam
|
|
export GTK_IM_MODULE=Steam
|
|
export STEAM_DISABLE_AUDIO_DEVICE_SWITCHING=1
|
|
export STEAM_ENABLE_VOLUME_HANDLER=1
|
|
|
|
log "Starting gamescope-session-plus (Intel Arc config via environment.d)"
|
|
|
|
/usr/share/gamescope-session-plus/gamescope-session-plus steam
|
|
rc=$?
|
|
|
|
exit "$rc"
|
|
NM_WRAPPER
|
|
sudo chmod +x "$nm_wrapper"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# SDDM Session Entry
|
|
#---------------------------------------------------------------------------
|
|
local session_desktop="/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"
|
|
sudo tee "$session_desktop" > /dev/null << 'SESSION_DESKTOP'
|
|
[Desktop Entry]
|
|
Name=Gaming Mode (Intel Arc)
|
|
Comment=Steam Big Picture with gamescope-session
|
|
Exec=/usr/local/bin/gamescope-session-nm-wrapper
|
|
Type=Application
|
|
DesktopNames=gamescope
|
|
SESSION_DESKTOP
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Session Switch Scripts
|
|
#---------------------------------------------------------------------------
|
|
local os_session_select="/usr/lib/os-session-select"
|
|
sudo tee "$os_session_select" > /dev/null << 'OS_SESSION_SELECT'
|
|
#!/bin/bash
|
|
rm -f /tmp/.gaming-session-active
|
|
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || true
|
|
timeout 5 steam -shutdown 2>/dev/null || true
|
|
sleep 1
|
|
nohup sudo -n systemctl restart sddm &>/dev/null &
|
|
disown
|
|
exit 0
|
|
OS_SESSION_SELECT
|
|
sudo chmod +x "$os_session_select"
|
|
|
|
local switch_script="/usr/local/bin/switch-to-gaming"
|
|
sudo tee "$switch_script" > /dev/null << 'SWITCH_SCRIPT'
|
|
#!/bin/bash
|
|
sudo -n /usr/local/bin/gaming-session-switch gaming 2>/dev/null || {
|
|
notify-send -u critical -t 3000 "Gaming Mode" "Failed to update session config" 2>/dev/null || true
|
|
}
|
|
notify-send -u normal -t 2000 "Gaming Mode" "Switching to Gaming Mode..." 2>/dev/null || true
|
|
pkill gamescope 2>/dev/null || true
|
|
pkill -f gamescope-session 2>/dev/null || true
|
|
sleep 2
|
|
pkill -9 gamescope 2>/dev/null || true
|
|
pkill -9 -f gamescope-session 2>/dev/null || true
|
|
sudo -n chvt 2 2>/dev/null || true
|
|
sleep 0.3
|
|
sudo -n systemctl restart sddm
|
|
SWITCH_SCRIPT
|
|
sudo chmod +x "$switch_script"
|
|
|
|
local switch_desktop_script="/usr/local/bin/switch-to-desktop"
|
|
sudo tee "$switch_desktop_script" > /dev/null << 'SWITCH_DESKTOP'
|
|
#!/bin/bash
|
|
[[ ! -f /tmp/.gaming-session-active ]] && exit 0
|
|
rm -f /tmp/.gaming-session-active
|
|
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || true
|
|
timeout 5 steam -shutdown 2>/dev/null || true
|
|
sleep 1
|
|
pkill gamescope 2>/dev/null || true
|
|
pkill -f gamescope-session 2>/dev/null || true
|
|
sleep 2
|
|
pkill -9 gamescope 2>/dev/null || true
|
|
pkill -9 -f gamescope-session 2>/dev/null || true
|
|
sudo -n chvt 2 2>/dev/null || true
|
|
sleep 0.3
|
|
nohup sudo -n systemctl restart sddm &>/dev/null &
|
|
disown
|
|
exit 0
|
|
SWITCH_DESKTOP
|
|
sudo chmod +x "$switch_desktop_script"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Keybind Monitor (Python)
|
|
#---------------------------------------------------------------------------
|
|
local keybind_monitor="/usr/local/bin/gaming-keybind-monitor"
|
|
sudo tee "$keybind_monitor" > /dev/null << 'KEYBIND_MONITOR'
|
|
#!/usr/bin/env python3
|
|
"""Gaming Mode Keybind Monitor - Super+Shift+R to exit"""
|
|
import sys, subprocess, time
|
|
try:
|
|
import evdev
|
|
from evdev import ecodes
|
|
except ImportError:
|
|
sys.exit(1)
|
|
|
|
def find_keyboards():
|
|
keyboards = []
|
|
for path in evdev.list_devices():
|
|
try:
|
|
device = evdev.InputDevice(path)
|
|
caps = device.capabilities()
|
|
if ecodes.EV_KEY in caps:
|
|
keys = caps[ecodes.EV_KEY]
|
|
if ecodes.KEY_A in keys and ecodes.KEY_R in keys:
|
|
keyboards.append(device)
|
|
except Exception:
|
|
continue
|
|
return keyboards
|
|
|
|
def monitor_keyboards(keyboards):
|
|
meta_pressed = shift_pressed = False
|
|
from selectors import DefaultSelector, EVENT_READ
|
|
selector = DefaultSelector()
|
|
for kbd in keyboards:
|
|
selector.register(kbd, EVENT_READ)
|
|
try:
|
|
while True:
|
|
for key, mask in selector.select():
|
|
device = key.fileobj
|
|
try:
|
|
for event in device.read():
|
|
if event.type != ecodes.EV_KEY:
|
|
continue
|
|
if event.code in (ecodes.KEY_LEFTMETA, ecodes.KEY_RIGHTMETA):
|
|
meta_pressed = event.value > 0
|
|
elif event.code in (ecodes.KEY_LEFTSHIFT, ecodes.KEY_RIGHTSHIFT):
|
|
shift_pressed = event.value > 0
|
|
elif event.code == ecodes.KEY_R and event.value == 1:
|
|
if meta_pressed and shift_pressed:
|
|
subprocess.run(['/usr/local/bin/switch-to-desktop'])
|
|
return
|
|
except Exception:
|
|
continue
|
|
except KeyboardInterrupt:
|
|
pass
|
|
finally:
|
|
selector.close()
|
|
|
|
def main():
|
|
time.sleep(2)
|
|
keyboards = find_keyboards()
|
|
if keyboards:
|
|
monitor_keyboards(keyboards)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
KEYBIND_MONITOR
|
|
sudo chmod +x "$keybind_monitor"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Steam Library Auto-Mounter
|
|
#---------------------------------------------------------------------------
|
|
local steam_mount_script="/usr/local/bin/steam-library-mount"
|
|
sudo tee "$steam_mount_script" > /dev/null << 'STEAM_MOUNT'
|
|
#!/bin/bash
|
|
# Steam Library Drive Auto-Mounter
|
|
|
|
check_steam_library() {
|
|
local mount_point="$1"
|
|
[[ -d "$mount_point/steamapps" ]] || [[ -d "$mount_point/SteamLibrary/steamapps" ]] || \
|
|
[[ -f "$mount_point/libraryfolder.vdf" ]] || [[ -f "$mount_point/steamapps/libraryfolder.vdf" ]]
|
|
}
|
|
|
|
handle_device() {
|
|
local device="$1"
|
|
findmnt -n "$device" &>/dev/null && return
|
|
[[ "$device" =~ [0-9]$ ]] || return
|
|
local fstype
|
|
fstype="$(lsblk -n -o FSTYPE --nodeps "$device" 2>/dev/null)"
|
|
case "$fstype" in
|
|
ext4|ext3|ext2|btrfs|xfs|ntfs|vfat|exfat|f2fs) ;;
|
|
*) return ;;
|
|
esac
|
|
command -v udisksctl &>/dev/null || return
|
|
udisksctl mount -b "$device" --no-user-interaction 2>/dev/null || return
|
|
local mount_point
|
|
mount_point="$(findmnt -n -o TARGET "$device" 2>/dev/null)"
|
|
[[ -z "$mount_point" ]] && return
|
|
check_steam_library "$mount_point" || udisksctl unmount -b "$device" --no-user-interaction 2>/dev/null
|
|
}
|
|
|
|
shopt -s nullglob
|
|
for dev in /dev/sd*[0-9]* /dev/nvme*p[0-9]*; do
|
|
[[ -b "$dev" ]] && handle_device "$dev"
|
|
done
|
|
shopt -u nullglob
|
|
|
|
action="" dev_path=""
|
|
udevadm monitor --kernel --property --subsystem-match=block 2>/dev/null | while read -r line; do
|
|
case "$line" in
|
|
ACTION=*) action="${line#ACTION=}" ;;
|
|
DEVNAME=*) dev_path="${line#DEVNAME=}" ;;
|
|
"")
|
|
if [[ "$action" == "add" && -n "$dev_path" && "$dev_path" =~ [0-9]$ && -b "$dev_path" ]]; then
|
|
sleep 1
|
|
handle_device "$dev_path"
|
|
fi
|
|
action="" dev_path=""
|
|
;;
|
|
esac
|
|
done
|
|
STEAM_MOUNT
|
|
sudo chmod +x "$steam_mount_script"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# SDDM Configuration
|
|
#---------------------------------------------------------------------------
|
|
sudo mkdir -p /etc/sddm.conf.d
|
|
|
|
local sddm_gaming_conf="/etc/sddm.conf.d/zz-gaming-session.conf"
|
|
local autologin_user="$current_user"
|
|
[[ -f /etc/sddm.conf.d/autologin.conf ]] && \
|
|
autologin_user=$(sed -n 's/^User=//p' /etc/sddm.conf.d/autologin.conf 2>/dev/null | head -1)
|
|
[[ -z "$autologin_user" ]] && autologin_user="$current_user"
|
|
|
|
sudo tee "$sddm_gaming_conf" > /dev/null << SDDM_GAMING
|
|
[Autologin]
|
|
User=${autologin_user}
|
|
Session=hyprland-uwsm
|
|
Relogin=true
|
|
SDDM_GAMING
|
|
|
|
local session_helper="/usr/local/bin/gaming-session-switch"
|
|
sudo tee "$session_helper" > /dev/null << 'SESSION_HELPER'
|
|
#!/bin/bash
|
|
CONF="/etc/sddm.conf.d/zz-gaming-session.conf"
|
|
[[ ! -f "$CONF" ]] && exit 1
|
|
case "$1" in
|
|
gaming) sed -i 's/^Session=.*/Session=gamescope-session-steam-nm/' "$CONF" ;;
|
|
desktop) sed -i 's/^Session=.*/Session=hyprland-uwsm/' "$CONF" ;;
|
|
*) exit 1 ;;
|
|
esac
|
|
SESSION_HELPER
|
|
sudo chmod +x "$session_helper"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Sudoers Rules
|
|
#---------------------------------------------------------------------------
|
|
local sudoers_session="/etc/sudoers.d/gaming-session-switch"
|
|
sudo tee "$sudoers_session" > /dev/null << 'SUDOERS_SWITCH'
|
|
%video ALL=(ALL) NOPASSWD: /usr/local/bin/gaming-session-switch
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart sddm
|
|
%video ALL=(ALL) NOPASSWD: /usr/bin/chvt
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/bin/systemctl start NetworkManager.service
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop NetworkManager.service
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart iwd.service
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart bluetooth.service
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/local/bin/gamescope-nm-start
|
|
%wheel ALL=(ALL) NOPASSWD: /usr/local/bin/gamescope-nm-stop
|
|
SUDOERS_SWITCH
|
|
sudo chmod 0440 "$sudoers_session"
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Hyprland Keybind
|
|
#---------------------------------------------------------------------------
|
|
local hypr_bindings_conf="${user_home}/.config/hypr/bindings.conf"
|
|
local hypr_main_conf="${user_home}/.config/hypr/hyprland.conf"
|
|
local keybind_target=""
|
|
|
|
# Determine where to add the keybind
|
|
if [[ -f "$hypr_bindings_conf" ]]; then
|
|
keybind_target="$hypr_bindings_conf"
|
|
elif [[ -f "$hypr_main_conf" ]]; then
|
|
keybind_target="$hypr_main_conf"
|
|
fi
|
|
|
|
if [[ -n "$keybind_target" ]] && ! grep -q "switch-to-gaming" "$keybind_target" 2>/dev/null; then
|
|
run_as_user tee -a "$keybind_target" > /dev/null << 'HYPR_GAMING'
|
|
|
|
# Gaming Mode - Switch to Gamescope session (Intel Arc)
|
|
bindd = SUPER SHIFT, S, Gaming Mode, exec, /usr/local/bin/switch-to-gaming
|
|
HYPR_GAMING
|
|
info "Added Gaming Mode keybind to $(basename "$keybind_target")"
|
|
elif [[ -z "$keybind_target" ]]; then
|
|
warn "No Hyprland config found - please add keybind manually:"
|
|
warn " bindd = SUPER SHIFT, S, Gaming Mode, exec, /usr/local/bin/switch-to-gaming"
|
|
fi
|
|
|
|
# Reload Hyprland
|
|
command -v hyprctl >/dev/null 2>&1 && hyprctl monitors >/dev/null 2>&1 && hyprctl reload >/dev/null 2>&1
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Done
|
|
#---------------------------------------------------------------------------
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " SESSION SWITCHING CONFIGURED (Intel Arc)"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " Usage:"
|
|
echo " - Press Super+Shift+S in Hyprland to switch to Gaming Mode"
|
|
echo " - Press Super+Shift+R in Gaming Mode to return to Hyprland"
|
|
echo ""
|
|
}
|
|
|
|
###############################################################################
|
|
# MAIN ENTRY POINT
|
|
###############################################################################
|
|
|
|
execute_setup() {
|
|
sudo -k
|
|
sudo -v || die "sudo authentication required"
|
|
|
|
validate_environment
|
|
check_intel_arc
|
|
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " ARCGames INSTALLER v${ARCGAMES_VERSION}"
|
|
echo " Intel Arc dGPU Gaming Mode Setup"
|
|
echo "================================================================"
|
|
echo ""
|
|
|
|
check_steam_dependencies
|
|
setup_requirements
|
|
setup_session_switching
|
|
|
|
if [[ "$NEEDS_RELOGIN" -eq 1 ]]; then
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " IMPORTANT: LOG OUT REQUIRED"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " User groups have been updated. Please log out and log back in."
|
|
echo ""
|
|
else
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " SETUP COMPLETE"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " To switch to Gaming Mode: Press Super+Shift+S"
|
|
echo " To return to Desktop: Press Super+Shift+R"
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
show_help() {
|
|
cat << EOF
|
|
ARCGames Installer v${ARCGAMES_VERSION}
|
|
|
|
Gaming Mode installer for Intel Arc discrete GPUs.
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Options:
|
|
--help, -h Show this help message
|
|
--version Show version number
|
|
--rebuild-mesa Rebuild mesa-git from latest upstream source
|
|
|
|
EOF
|
|
}
|
|
|
|
###############################################################################
|
|
# MESA-GIT REBUILD
|
|
###############################################################################
|
|
|
|
rebuild_mesa_git() {
|
|
info "Mesa-git rebuild requested"
|
|
|
|
# Check if mesa-git is actually installed
|
|
if ! check_package "mesa-git"; then
|
|
die "mesa-git is not installed. Run the full installer first, or use: yay -S mesa-git"
|
|
fi
|
|
|
|
# Find AUR helper
|
|
local aur_helper=""
|
|
command -v yay >/dev/null 2>&1 && check_aur_helper_functional yay && aur_helper="yay"
|
|
[[ -z "$aur_helper" ]] && command -v paru >/dev/null 2>&1 && check_aur_helper_functional paru && aur_helper="paru"
|
|
[[ -z "$aur_helper" ]] && die "No AUR helper found (yay or paru required)"
|
|
|
|
info "Using AUR helper: $aur_helper"
|
|
|
|
# Show current mesa-git version
|
|
local current_ver
|
|
current_ver=$(pacman -Qi mesa-git 2>/dev/null | grep "^Version" | awk '{print $3}')
|
|
info "Current mesa-git version: $current_ver"
|
|
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " MESA-GIT REBUILD"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " This will rebuild mesa-git from the latest upstream source."
|
|
echo " Build time: typically 10-30 minutes per package."
|
|
echo ""
|
|
|
|
local -a packages=("mesa-git")
|
|
if check_package "lib32-mesa-git"; then
|
|
packages+=("lib32-mesa-git")
|
|
echo " Packages to rebuild: mesa-git, lib32-mesa-git"
|
|
else
|
|
echo " Package to rebuild: mesa-git"
|
|
fi
|
|
echo ""
|
|
|
|
read -p "Proceed with rebuild? [Y/n]: " -n 1 -r
|
|
echo
|
|
[[ $REPLY =~ ^[Nn]$ ]] && { info "Rebuild cancelled"; return 0; }
|
|
|
|
# Clear AUR helper cache to force fresh build
|
|
for pkg in "${packages[@]}"; do
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/yay/${pkg}" 2>/dev/null || true
|
|
run_as_user rm -rf "${REAL_HOME}/.cache/paru/clone/${pkg}" 2>/dev/null || true
|
|
done
|
|
|
|
# Rebuild mesa-git
|
|
info "Rebuilding mesa-git from latest source..."
|
|
if ! run_as_user "$aur_helper" -S --noconfirm --rebuild --removemake --cleanafter \
|
|
--overwrite '/usr/lib/*' --answeredit None --answerclean None --answerdiff None mesa-git; then
|
|
die "Failed to rebuild mesa-git"
|
|
fi
|
|
info "mesa-git rebuilt successfully"
|
|
|
|
# Rebuild lib32-mesa-git if installed
|
|
if check_package "lib32-mesa-git"; then
|
|
info "Rebuilding lib32-mesa-git from latest source..."
|
|
if ! run_as_user "$aur_helper" -S --noconfirm --rebuild --removemake --cleanafter \
|
|
--overwrite '/usr/lib32/*' --answeredit None --answerclean None --answerdiff None lib32-mesa-git; then
|
|
die "Failed to rebuild lib32-mesa-git"
|
|
fi
|
|
info "lib32-mesa-git rebuilt successfully"
|
|
fi
|
|
|
|
# Show new version
|
|
local new_ver
|
|
new_ver=$(pacman -Qi mesa-git 2>/dev/null | grep "^Version" | awk '{print $3}')
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " MESA-GIT REBUILD COMPLETE"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " Previous version: $current_ver"
|
|
echo " New version: $new_ver"
|
|
echo ""
|
|
}
|
|
|
|
###############################################################################
|
|
# COMMAND LINE HANDLING
|
|
###############################################################################
|
|
|
|
case "${1:-}" in
|
|
--help|-h) show_help; exit 0 ;;
|
|
--version) echo "ARCGames Installer v${ARCGAMES_VERSION}"; exit 0 ;;
|
|
--rebuild-mesa) rebuild_mesa_git ;;
|
|
"") execute_setup ;;
|
|
*) echo "Unknown option: $1"; exit 1 ;;
|
|
esac
|