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:
parent
48fdb1c89d
commit
4d3f9d51cd
2 changed files with 229 additions and 24 deletions
74
README.md
74
README.md
|
@ -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)
|
179
hyprfreeze
179
hyprfreeze
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue