1
0
Fork 1
mirror of https://github.com/Zerodya/hyprfreeze.git synced 2024-11-10 01:18:49 +01:00

v0.1.0 release

This commit is contained in:
Nicola 2023-10-13 12:40:12 +02:00 committed by GitHub
parent 48fdb1c89d
commit 4d3f9d51cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 229 additions and 24 deletions

View file

@ -1,16 +1,66 @@
# hyprfreeze
Simple bash script to "freeze" a game process in Hyprland, allowing you to resume it later.
Useful to:
- Pause games during unpausable cutscenes
- Save system resources (CPU and GPU are free, the process is saved in RAM)
Hyprfreeze is an utility to suspend a game process (and other programs) in Hyprland.
(Also works for any other program; freezes the current active window)
### Usage
Make the script executable and add a bind in your Hyprland config:
Useful to:
- Pause single-player games that can't be paused (like Dark Souls)
- Pause during cutscenes to read the subtitles or when something urgent comes up
- Save system resources (excluding RAM) if you need them for another computer task, or if the game's pause menu uses too many
## Installation
### Arch Linux
Coming soon<sup><sup>TM</sup></sup> in AUR.
### Dependencies
- hyprland (to get the pid of the active window with hyprctl)
- jq (to parse json)
- hyprprop (to get the pid of a window by selecting it with your mouse)
### Manual
Clone this repo and symlink the `hyprfreeze` script to a directory in your `PATH`:
```bash
$ git clone https://github.com/Zerodya/hyprfreeze.git Hyprfreeze
$ ln -s $(pwd)/Hyprfreeze/hyprfreeze $HOME/.local/bin
$ chmod +x Hyprfreeze/hyprfreeze
```
bind = $mainMod, PAUSE, exec, ~/scripts/hyprfreeze
## Usage
Add a bind in your Hyprland config to pause the current active window:
```
### Known issues
- Mouse input doesn't work inside Xorg/XWayland windows while a Wine/Proton game is paused
- Workaround: Run the game in gamescope
- Suspending the computer while a game is paused and then resuming it later may cause the game to stutter heavily
# ~/.config/hypr/hyprland.conf
...
# Toggle freeze on active window
bind = $mainMod, PAUSE, exec, hyprfreeze -a
```
### Available flags
```
-h, --help show help message
-a, --active pause/resume active window
-p, --pid pause/resume by process id
-n, --name pause/resume by process name/command
-r, --prop pause/resume by clicking on window
--info show information about the process
--dry-run doesn't actually pause/resume a process, useful with --info
```
### Examples:
```
# Pause game by process name
hyprfreeze -n eldenring.exe
```
```
# Get info about a process by clicking on its window, without suspending it
hyprfreeze -r --info --dry-run
```
## Disclaimer
There is always the risk, although slim, that an application may crash.
This is intrinsically related to modifying running processes and is not something that Hyprfreeze can prevent.
Please make sure to **save your data** before using hyprfreeze.
## Known issues
- Pausing Wine/Proton games will cause mouse input to not work inside XWayland windows [#1](https://github.com/Zerodya/hyprfreeze/issues/2)
- Workaround 1: Run the game in `gamescope`
- Workaround 2: Pause the game from a terminal `hyprfreeze -n eldenring.exe`
- Pausing Linux native games (e.g. Minecraft) may cause sound to stop in other apps [#2](https://github.com/Zerodya/hyprfreeze/issues/2)

View file

@ -1,16 +1,171 @@
#!/usr/bin/env bash
PID=$(hyprctl activewindow | grep -i pid | awk -F "pid: " '{ print $2 }' | awk -F "," '{ print $1 }')
PIDS=$(pstree $PID -npl | grep -oP '(?<=\()[0-9]+(?=\))')
function printHelp() {
cat <<EOF
Usage: hyprfreeze (-a | -p [pid] | -n [name] | -r) [--info --dry-run]
# Pause process if running
if [[ "$(ps -o state= $PID)" == S ]]; then
kill -STOP $PIDS
exit 0
fi
Hyprfreeze is an utility to suspend a game process (and other programs) in Hyprland.
# Resume process if stopped
if [[ "$(ps -o state= $PID)" == T ]]; then
kill -CONT $PIDS
exit 0
fi
Options:
-h, --help show help message
-a, --active pause/resume active window
-p, --pid pause/resume by process id
-n, --name pause/resume by process name/command
-r, --prop pause/resume by clicking on window
--info show information about the process
--dry-run doesn't actually pause/resume a process, useful with --info
EOF
}
function hyprfreeze() {
# 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[^\)]+')
# Pause or resume processes
if [[ "$(ps -o state= $PID)" == T ]]; then
kill -CONT $PIDS 2>/dev/null &&
echo "Resumed $(ps -p $PID -o comm= 2>/dev/null) ($PID)" || exit 1
else
kill -STOP $PIDS 2>/dev/null &&
echo "Stopped $(ps -p $PID -o comm= 2>/dev/null) ($PID)" || exit 1
fi
}
function freezeActive() {
PID=$(hyprctl activewindow -j | jq '.pid')
# Prevent pausing itself
script_pid=$$
if echo "$(pstree -p $PID | grep -oP '\(\K[^\)]+')" | grep -q "$script_pid"; then
echo "You are trying to suspend the hyprfreeze process."
echo "This option is meant to be used via keybind to pause the active window, running it in terminal would suspend hyprfreeze itself."
exit 1
fi
hyprfreeze
}
function freezePid() {
# Check if process pid exists
if ! ps -p $1 &>/dev/null; then
echo "Process ID $1 not found"
exit 1
fi
PID=$1
hyprfreeze
}
function freezeName() {
# 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}')
hyprfreeze
}
function freezeProp() {
PID=$(hyprprop | jq '.pid')
hyprfreeze
}
function info() {
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)"
}
function args() {
# Check if no options were passed
if [ -z "$1" ]; then
printHelp
exit 1
fi
# Track valid flags
local valid_flag_count=0
# Parse options
local options="hap:n:r"
local long_options="help,active,pid:,name:,prop,info,dry-run"
local 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)
((valid_flag_count++))
FLAG_ACTIVE=true
;;
-p|--pid)
((valid_flag_count++))
shift;
FLAG_PID="$1"
;;
-n|--name)
((valid_flag_count++))
shift;
NAME_FLAG="$1"
;;
-r|--prop)
((valid_flag_count++))
FLAG_PROP=true
;;
--info)
INFO=1
;;
--dry-run)
DRYRUN=1
;;
--)
shift; # Skip -- argument
COMMAND=${@:2}
break;;
*)
exit 1
;;
esac
shift
done
# Check if more than one valid flag is provided, or if none was provided
if [ $valid_flag_count -ne 1 ]; then
printHelp
exit 1
fi
}
function main() {
# Handle the chosen valid flag
if [ "$FLAG_ACTIVE" = true ]; then freezeActive;
elif [ -n "$FLAG_PID" ]; then freezePid "$FLAG_PID";
elif [ -n "$NAME_FLAG" ]; then freezeName "$NAME_FLAG";
elif [ "$FLAG_PROP" = true ]; then freezeProp;
fi
# Run info function after PID is obtained
if [ $INFO -eq 1 ]; then info; fi
}
INFO=0
DRYRUN=0
args "$@"
main