forked from mkoura/browser-suspender
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbrowser-suspender.sh
executable file
·146 lines (123 loc) · 3.8 KB
/
browser-suspender.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env bash
#
# browser-suspender: Periodically check whether Firefox-based browser is out of
# focus and STOP it in that case after a time delay; if in focus but stopped,
# send SIGCONT.
#
# (c) Petr Baudis <[email protected]> 2014
# (c) Martin Kourim <[email protected]> 2016
# MIT licence if this is even copyrightable
read_timeout=4 # [s]
stop_delay=10 # [s]
# Max number of cached WIN_CLASS entries
max_cached_entries=1000
# Cached WIN_CLASS counter
wincount=0
battery_mode=false
case "$1" in
-b | *battery) battery_mode=true ;;
-h | *help) echo "Usage: ${0##*/} [-b|-battery]" >&2; exit 0 ;;
esac
hash xprop 2>/dev/null || { echo "Please install xprop" >&2; exit 1; }
# Let xprop run in spy mode and output to named pipe
xprop_pipe="$(mktemp -u /tmp/browser-suspender.XXXXXXX)"
mkfifo "$xprop_pipe" || exit 1
exec 10<>"$xprop_pipe" # assign pipe to file descriptor
xprop -spy -root _NET_ACTIVE_WINDOW > "$xprop_pipe" &
xprop_pid="$!"
ARR="procs pstate last_in_focus wclass"
for i in $ARR; do declare -A "$i"; done
resume() {
for pid in "${!pstate[@]}"; do
if [ "${pstate[$pid]}" = stopped ]; then
if kill -CONT "$pid"; then
echo "$(date) Resuming browser @ $pid"
pstate[$pid]=running
fi
fi
done
}
cleanup() {
resume
kill "$xprop_pid"
rm -f "$xprop_pipe"
}
trap cleanup EXIT
trap exit HUP INT TERM
on_battery() {
for bat_file in /sys/class/power_supply/BAT*/status; do
read battery < "$bat_file"
[ "$battery" = Discharging ] && return 0
done
return 1
}
while true; do
# Any changes in root window? Return immediately if so,
# otherwise wait for "$read_timeout" seconds.
read -t "$read_timeout" xprop_out <&10
# Resume all and clear all collected data if we are not running on battery
if [ "$battery_mode" != true ] && ! on_battery; then
resume
if [ "$wincount" -gt 0 ]; then
for i in $ARR; do unset -v "$i"; declare -A "$i"; done
wincount=0
fi
continue
fi
now="$(date +%s)"
# Get active window id
[ -n "$xprop_out" ] && window="${xprop_out#*# }"
[ -z "$window" -o "$window" = '0x0' ] && continue
if [ -z "${wclass[$window]}" ]; then
# Clear cache
[ "$wincount" -gt "$max_cached_entries" ] && { unset -v wclass; declare -A wclass; wincount=0; }
# What kind of window is it?
wclass[$window]="$(xprop -id "$window" WM_CLASS)"
((wincount++))
fi
case "${wclass[$window]}" in
# 'Navigator' is not enough - it will not match e.g. save file dialog
*Navigator*|*Firefox*|*Iceweasel*|*PaleMoon*|*chromium*)
# Browser! We know it is running. Make sure we
# have its pid and update the last seen date.
# If we stopped it, resume again.
pid="${procs[$window]}"
if [ -n "$pid" ] && [ "${pstate[$pid]}" = stopped ]; then
if kill -CONT "$pid"; then
echo "$(date) Resuming browser @ $pid"
pstate[$pid]=running
fi
fi
if [ -z "$pid" ]; then
wpid="$(xprop -id "$window" _NET_WM_PID)"
pid="${wpid#*= }"
procs[$window]="$pid"
pstate[$pid]=running
fi
last_in_focus[$pid]="$now"
;;
esac
# Stop browsers
for pid in "${!pstate[@]}"; do
focus_t="${last_in_focus[$pid]}"
if [[ -z "$focus_t" || "${pstate[$pid]}" = stopped || "${pstate[$pid]}" = unknown ]]; then
continue
fi
# Out of focus for long enough?
(( (now - focus_t) < stop_delay )) && continue
# Suspend the process
if kill -STOP "$pid" 2>/dev/null; then
echo "$(date) Stopping browser @ $pid"
pstate[$pid]=stopped
else
if [ -e /proc/"$pid" ]; then
# We don't have permissions to send signal
pstate[$pid]=unknown
else
# The process no longer exists, clean up
unset -v pstate[$pid]
unset -v last_in_focus["$pid"]
fi
fi
done
done