From 9e8fcdcfa1ee576f3b940e0bd70b821b0b3ff483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20La=C3=ADn?= Date: Sat, 17 Feb 2024 01:51:53 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(hypr):=20add=20hyprfreeze,=20a?= =?UTF-8?q?n=20script=20to=20suspend=20the=20active=20window=20process?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useful when gaming --- .config/hypr/configs/binds.conf | 5 +- .config/hypr/scripts/hyprfreeze | 223 ++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100755 .config/hypr/scripts/hyprfreeze diff --git a/.config/hypr/configs/binds.conf b/.config/hypr/configs/binds.conf index cfa70dd8..bb95cbcc 100644 --- a/.config/hypr/configs/binds.conf +++ b/.config/hypr/configs/binds.conf @@ -144,7 +144,7 @@ binde = , H, resizeactive, -40 0 binde = , K, resizeactive, 0 -40 binde = , J, resizeactive, 0 40 -bind = , escape, submap, reset +bind = , ESCAPE, submap, reset submap = reset @@ -179,3 +179,6 @@ bind = SUPER, F12, exec, ~/.config/hypr/scripts/performance # Open Wiki bind = SUPER, F1, exec, xdg-open https://wiki.hyprland.org/ bind = SUPERSHIFT, F1, exec, xdg-open https://github.com/Matt-FTW/dotfiles/wiki + +# Freeze active window +bind = SUPERSHIFT, ESCAPE, exec, ~/.config/hypr/scripts/hyprfreeze diff --git a/.config/hypr/scripts/hyprfreeze b/.config/hypr/scripts/hyprfreeze new file mode 100755 index 00000000..39e3d102 --- /dev/null +++ b/.config/hypr/scripts/hyprfreeze @@ -0,0 +1,223 @@ +#!/usr/bin/env bash + +function printHelp() { + cat < | -n | -r) [options] + +Utility to suspend a game process (and other programs) in Hyprland. + +Options: + -h, --help show help message + + -a, --active toggle suspend by active window + -p, --pid toggle suspend by process id + -n, --name toggle suspend by process name/command + -r, --prop toggle suspend by clicking on window (hyprprop must be installed) + + -s, --silent don't send notification + -t, --notif-timeout notification timeout in milliseconds (default 5000) + --info show information about the process + --dry-run doesn't actually suspend/resume a process, useful with --info + --debug enable debug mode +EOF +} + +function debugPrint() { + if [ "$DEBUG" -eq 1 ]; then + echo "[DEBUG] $1" + fi +} + +function toggleFreeze() { + # Skip this function if --dry-run flag was provided + if [[ $DRYRUN == "1" ]]; then return 0; fi + + # Get pids of process tree + PIDS=$(pstree -p "$PID" | grep -oP '\(\K[^\)]+') + + debugPrint "PIDs: $PIDS" + + # Prevent suspending itself + local pid_of_script=$$ + if echo "$PIDS" | grep -q "$pid_of_script"; then + echo "You are trying to suspend the hyprfreeze process." + exit 1 + fi + + # Suspend or resume processes + if [[ "$(ps -o state= "$PID")" == T ]]; then + debugPrint "Resuming processes..." + kill -CONT $PIDS 2>/dev/null && echo "Resumed $(ps -p "$PID" -o comm= 2>/dev/null) (PID $PID)" || exit 1 + else + debugPrint "Suspending processes..." + kill -STOP $PIDS 2>/dev/null && echo "Suspended $(ps -p "$PID" -o comm= 2>/dev/null) (PID $PID)" || exit 1 + fi +} + +function getPidByActive() { + debugPrint "Getting PID by active window..." + PID=$(hyprctl activewindow -j | jq '.pid') + debugPrint "PID by active window: $PID" +} + +function getPidByPid() { + debugPrint "Getting PID by PID: $1" + # Check if process pid exists + if ! ps -p "$1" &>/dev/null; then + echo "Process ID $1 not found" + exit 1 + fi + + PID=$1 +} + +function getPidByName() { + debugPrint "Getting PID by name: $1" + # Check if process name exists + if ! pidof -x "$1" >/dev/null; then + echo "Process name $1 not found" + exit 1 + fi + + # Get last process if there are multiple + PID=$(pidof "$1" | awk '{print $NF}') + debugPrint "PID by name: $PID" +} + +function getPidByProp() { + debugPrint "Getting PID by prop..." + if ! command -v hyprprop; then + echo "You need to install 'hyprprop' to use this feature. (https://github.com/vilari-mickopf/hyprprop)" + exit 1 + fi + + PID=$(hyprprop | jq '.pid') + debugPrint "PID by prop: $PID" +} + +function printInfo() { + debugPrint "Printing process info...\n" + echo -e "$(tput bold)Process tree:$(tput sgr0)" + ps -p "$PID" 2>/dev/null && pstree -p "$PID" + + echo -e "\n$(tput bold)Process threads:$(tput sgr0)" + ps -eLo pid,tid,comm | grep "$PID" 2>/dev/null + + echo -e "\n$(tput bold)Process ID$(tput sgr0) = $PID \ + \n$(tput bold)Process name$(tput sgr0) = $(ps -p "$PID" -o comm= 2>/dev/null) \ + \n$(tput bold)Process state$(tput sgr0) = $(ps -o state= -p "$PID" 2>/dev/null)\n" +} + +function sendNotification() { + debugPrint "Sending notification..." + local title + title=$( [[ "$(ps -p "$PID" -o state=)" == T ]] && + echo "Suspended $(ps -p "$PID" -o comm= 2>/dev/null)" || + echo "Resumed $(ps -p "$PID" -o comm= 2>/dev/null)") + + local message="PID $PID" + + notify-send "${title}" "${message}" -t "$NOTIF_TIMEOUT" -a Hyprfreeze +} + +function args() { + # Track required flags + local required_flag_count=0 + + # Parse options + local options="hap:n:rst:" + local long_options="help,active,pid:,name:,prop,silent,notif-timeout:,info,dry-run,debug" + local parsed_args + parsed_args=$(getopt -o "$options" --long "$long_options" -n "$(basename "$0")" -- "$@") + + eval set -- "$parsed_args" + while true; do + case $1 in + -h | --help) + printHelp + exit 0 + ;; + -a | --active) + ((required_flag_count++)) + FLAG_ACTIVE=true + ;; + -p | --pid) + ((required_flag_count++)) + shift + FLAG_PID="$1" + ;; + -n | --name) + ((required_flag_count++)) + shift + NAME_FLAG="$1" + ;; + -r | --prop) + ((required_flag_count++)) + FLAG_PROP=true + ;; + -s | --silent) + SILENT=1 + ;; + -t | --notif-timeout) + shift + NOTIF_TIMEOUT="$1" + ;; + --info) + INFO=1 + ;; + --dry-run) + DRYRUN=1 + ;; + --debug) + DEBUG=1 + ;; + --) + shift # Skip -- argument + break + ;; + *) + exit 1 + ;; + esac + shift + done + + # Check if more than one required flag is provided, or if none was provided + if [ $required_flag_count -ne 1 ]; then + printHelp + exit 1 + fi +} + +function main() { + debugPrint "Starting main function..." + # Get pid by a required flag + if [ "$FLAG_ACTIVE" = true ]; then + getPidByActive + elif [ -n "$FLAG_PID" ]; then + getPidByPid "$FLAG_PID" + elif [ -n "$NAME_FLAG" ]; then + getPidByName "$NAME_FLAG" + elif [ "$FLAG_PROP" = true ]; then + getPidByProp + fi + + # Suspend or resume process + toggleFreeze + + # Run these functions after PID is obtained + if [ $INFO -eq 1 ]; then printInfo; fi + if [ $SILENT -ne 1 ]; then sendNotification; fi + + debugPrint "End of main function." +} + +SILENT=0 +NOTIF_TIMEOUT=5000 +INFO=0 +DRYRUN=0 +DEBUG=0 + +args "$@" + +main