diff --git a/bin/omarchy-menu b/bin/omarchy-menu index 7c69d8d3..66a356c1 100755 --- a/bin/omarchy-menu +++ b/bin/omarchy-menu @@ -299,10 +299,12 @@ show_install_ai_menu() { } show_install_gaming_menu() { - case $(menu "Install" " Steam\n RetroArch [AUR]\n󰍳 Minecraft\n Lutris") in + case $(menu "Install" "Omarchy Gaming Helper\n Steam\n RetroArch [AUR]\n󰍳 Minecraft\n Heroic Launcher\n Lutris") in + *Omarchy Gaming Helper*) present_terminal omarchy-setup-gaming ;; *Steam*) present_terminal omarchy-install-steam ;; *RetroArch*) aur_install_and_launch "RetroArch" "retroarch retroarch-assets libretro libretro-fbneo" "com.libretro.RetroArch.desktop" ;; *Minecraft*) aur_install_and_launch "Minecraft [AUR]" "minecraft-launcher" "minecraft-launcher" ;; + *Heroic Launcher*) aur_install_and_launch "Heroic Launcher" "heroic-games-launcher-bin" "heroic" ;; *Lutris*) aur_install_and_launch "Lutris" "lutris" "lutris" ;; *) show_install_menu ;; esac diff --git a/bin/omarchy-setup-gaming b/bin/omarchy-setup-gaming new file mode 100644 index 00000000..52d5009b --- /dev/null +++ b/bin/omarchy-setup-gaming @@ -0,0 +1,25 @@ +#!/bin/bash + +mkdir -p ~/.config/systemd/user/ + +cat > ~/.config/systemd/user/omarchy-gaming-helper.service << EOF +[Unit] +Description=Omarchy Gaming Helper +After=graphical-session.target + +[Service] +Type=simple +ExecStart=$OMARCHY_PATH/default/hypr/scripts/omarchy-gaming-helper.sh +Restart=always +RestartSec=5 + +[Install] +WantedBy=default.target +EOF + +systemctl --user daemon-reload + +systemctl --user enable omarchy-gaming-helper.service +systemctl --user start omarchy-gaming-helper.service + +echo "Omarchy Gaming Helper service has been installed and started." diff --git a/default/hypr/scripts/omarchy-gaming-helper.sh b/default/hypr/scripts/omarchy-gaming-helper.sh new file mode 100755 index 00000000..31b6dee2 --- /dev/null +++ b/default/hypr/scripts/omarchy-gaming-helper.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +# Gaming Helper for Hyprland +# Runs as a service in the background to perform tasks that can't be accomplished with only +# hyprland rules +# Usage: omarchy-gaming-helper.sh [--debug] + +DEBUG_MODE=0 +[[ "$1" == "--debug" ]] && DEBUG_MODE=1 + +# Track if we have active games +GAMES_ACTIVE=0 + +log_debug() { + [[ $DEBUG_MODE -eq 1 ]] && gum log --level debug "$@" +} + +log_info() { + gum log --level info "$@" +} + +# Monitor Management +apply_gaming_scale() { + log_info "Applying integer scaling for gaming" + + # Get all monitors and apply integer scaling + hyprctl monitors -j | jq -r '.[] | "\(.name) \(.width) \(.height) \(.refreshRate) \(.x) \(.y) \(.scale)"' | \ + while read -r name width height refresh x y scale; do + # Round scale to nearest integer + integer_scale=$(awk "BEGIN {printf \"%.0f\", $scale}") + + log_info "Setting $name from scale $scale to $integer_scale" + hyprctl keyword monitor "$name,${width}x${height}@${refresh},${x}x${y},$integer_scale" + done +} + +restore_monitor_settings() { + log_info "Restoring monitor settings" + hyprctl reload +} + +# Game Detection +is_steam_game() { + local class="$1" + [[ "$class" == steam_app_* ]] +} + +count_active_games() { + hyprctl clients -j | jq -r '[.[] | select(.class | startswith("steam_app_"))] | length' +} + +# Event Handlers +handle_window_open() { + local event="$1" + + # Parse: openwindow>>address,workspace,class,title + IFS=',' read -r address workspace class title <<< "${event#openwindow>>}" + + if is_steam_game "$class"; then + log_info "Steam game opened: $title ($class)" + + # Apply scaling on first game + if [[ $GAMES_ACTIVE -eq 0 ]]; then + apply_gaming_scale + fi + GAMES_ACTIVE=$((GAMES_ACTIVE + 1)) + + # Apply fullscreen + hyprctl dispatch fullscreenstate 2 2 "address:$address" + fi +} + +handle_title_change() { + local event="$1" + + # Parse: windowtitlev2>>address,title + IFS=',' read -r address title <<< "${event#windowtitlev2>>}" + address="${address#0x}" + + # Get window class and tags + local window_info=$(hyprctl clients -j | jq -r --arg addr "0x$address" \ + '.[] | select(.address == $addr) | "\(.class) \(.tags)"') + + if [[ -z "$window_info" ]]; then + return + fi + + read -r class tags <<< "$window_info" + + # Handle gamescope windows (Battle.net, etc) + if [[ "$class" == "gamescope" ]]; then + if [[ "$tags" == *"game"* ]] && [[ "$tags" != *"game-launcher"* ]]; then + log_info "Gamescope game detected: $title" + hyprctl dispatch fullscreenstate 2 2 "address:0x$address" + elif [[ "$tags" == *"game-launcher"* ]]; then + log_info "Gamescope launcher detected: $title" + hyprctl dispatch fullscreenstate 0 0 "address:0x$address" + fi + fi +} + +handle_window_close() { + local event="$1" + local address="${event#closewindow>>}" + + # Count remaining games + local remaining_games=$(count_active_games) + log_debug "Window closed, remaining games: $remaining_games" + + # Restore settings when last game closes + if [[ "$remaining_games" -eq 0 ]] && [[ $GAMES_ACTIVE -gt 0 ]]; then + log_info "No games remain, restoring settings" + restore_monitor_settings + GAMES_ACTIVE=0 + else + GAMES_ACTIVE="$remaining_games" + fi +} + +# Main Event Loop +main() { + log_info "Gaming helper started (debug=$DEBUG_MODE)" + + socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | \ + while read -r event; do + log_debug "Event: ${event:0:80}..." + + case "$event" in + openwindow*) + handle_window_open "$event" + ;; + windowtitlev2*) + handle_title_change "$event" + ;; + closewindow*) + handle_window_close "$event" + ;; + esac + done +} + +# Start +main