I would like to execute a command such as
notify-send 'a'
if my Linux machine has been idle for 5 minutes.
By idle, I mean the same thing a screen saver that gets activated would use to define "idle".
I use a program called xprintidle
to find out the X idle time, which I'm strongly guessing uses the same data source as screensavers. xprintidle
doesn't really seem to have an upstream anymore, but the Debian package is alive and well.
It is a very simple application: it returns the amount of milliseconds since last X interaction:
$ sleep 1 && xprintidle
940
$ sleep 5 && xprintidle
4916
$ sleep 10 && xprintidle
9932
(note: due to the underlying system, it will consistently give a value in ms slightly lower than the "actual" idle time).
You can use this to create a script that runs a certain sequence after five minutes of idle time via e.g.:
#!/bin/sh
# Wanted trigger timeout in milliseconds.
IDLE_TIME=$((5*60*1000))
# Sequence to execute when timeout triggers.
trigger_cmd() {
echo "Triggered action $(date)"
}
sleep_time=$IDLE_TIME
triggered=false
# ceil() instead of floor()
while sleep $(((sleep_time+999)/1000)); do
idle=$(xprintidle)
if [ $idle -ge $IDLE_TIME ]; then
if ! $triggered; then
trigger_cmd
triggered=true
sleep_time=$IDLE_TIME
fi
else
triggered=false
# Give 100 ms buffer to avoid frantic loops shortly before triggers.
sleep_time=$((IDLE_TIME-idle+100))
fi
done
The 100 ms offset is because of the earlier noted quirk that xprintidle
will always return a time slightly lower than the "actual" idle time when executed like this. It will work without this offset, and will then be more accurate to a tenth of a second, but it will trigger the xprintidle
check frantically during the last milliseconds before an interval end. Not a performance hog in any way, but I would find that inelegant.
I have used a similar approach in a Perl script (an irssi plugin) for quite some time, but the above was just written and has not really been tested except for a few trial runs during writing.
Try it by running it in a terminal within X. I recommend setting the timeout to e.g. 5000 ms for testing, and adding set -x
directly below #!/bin/sh
to get informative output to see how it works.
xprintidle
gives too high values probably when the system suspends. I try to find why. Thanks for the script anyway :-)
Commented
Jun 2, 2020 at 9:12
I use xssstate
for such purposes. It's available in suckless-tools
package in Debian or Ubuntu, or upstream.
Then you can use the following shell script:
#!/bin/sh
if [ $# -lt 2 ];
then
printf "usage: %s minutes command\n" "$(basename $0)" 2>&1
exit 1
fi
timeout=$(($1*60*1000))
shift
cmd="$@"
triggered=false
while true
do
tosleep=$(((timeout - $(xssstate -i)) / 1000))
if [ $tosleep -le 0 ];
then
$triggered || $cmd
triggered=true
else
triggered=false
sleep $tosleep
fi
done
If you're using GNOME Shell, you can query the user session inactivity from its display manager Mutter using gdbus
, no need for any third-party utility.
For example:
# polling interval in seconds
polling_interval=10
# timeout in milliseconds
timeout=300000
while true; do
inactivity=`gdbus call --session \
--dest org.gnome.Shell \
--object-path /org/gnome/Mutter/IdleMonitor/Core \
--method org.gnome.Mutter.IdleMonitor.GetIdletime \
| grep -oP '^(\(uint64\s*)\K\d+'`
if [[ $timeout -lt $inactivity ]] ; then
notify-send 'a'
fi
sleep $polling_interval
done
Here's a C application that I found which you can compile.
$ more xidle.c
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/scrnsaver.h>
/* Report amount of X server idle time. */
/* Build with- */
/* cc xidle.c -o xidle -lX11 -lXext -lXss */
int main(int argc, char *argv[])
{
Display *display;
int event_base, error_base;
XScreenSaverInfo info;
float seconds;
display = XOpenDisplay("");
if (XScreenSaverQueryExtension(display, &event_base, &error_base)) {
XScreenSaverQueryInfo(display, DefaultRootWindow(display), &info);
seconds = (float)info.idle/1000.0f;
printf("%f\n",seconds);
return(0);
}
else {
fprintf(stderr,"Error: XScreenSaver Extension not present\n");
return(1);
}
}
It needs a couple libraries to build. On my Fedora 19 system I needed the following libraries:
$ rpm -qf /lib64/libX11.so.6 /lib64/libXext.so.6 /lib64/libXss.so.1
libX11-1.6.0-1.fc19.x86_64
libXext-1.3.2-1.fc19.x86_64
libXScrnSaver-1.2.2-5.fc19.x86_64
Once these were installed I compiled the above like so:
$ gcc xidle.c -o xidle -lX11 -lXext -lXss
You can see that it's able to report the number of seconds that X is detecting as idle time by running it like so:
$ while [ 1 ]; do ./xidle ; sleep 2;done
0.005000
1.948000
3.954000
5.959000
7.965000
0.073000 <--- moved the mouse here which resets it
0.035000
Using this executable you could put together a script that can do something like this, monitoring the idle time reported by xidle
.
$ while [ 1 ]; do idle=$(./xidle);
[ $( echo "$idle > 5" | bc ) -eq 0 ] && echo "still < 5" || echo "now > 5";
sleep 2;
done
still < 5
still < 5
still < 5
now > 5
now > 5
still < 5
still < 5
still < 5
The above shows still < 5
until 5 seconds of idle time has elapsed, at which point it starts saying now > 5
, which means that 5+ seconds has passed.
NOTE: You could incorporate your notify-send 'a'
into the above example.
bsd ports (packages collection) has a program that can do this:
http://man.openbsd.org/OpenBSD-current/man1/xidle.1
it's available e.g. here:
http://distcache.freebsd.org/local-distfiles/novel/xidle-26052015.tar.bz2
build like:
# apt-get install libxss-dev # for include/X11/extensions/scrnsaver.h
# gcc -o /usr/local/bin/xidle xidle.c -lX11 -lXss
note that the -program needs to contain the full path to the binary, as it's passed to execv().
$ xidle -timeout 120 -program "/usr/bin/xlock -mode pyro"
xautolock
but there are no examplesxautolock -time 5 -locker "notify-send 'a'"
. You can put this:xautolock -time 5 -locker "notify-send 'a'" &
in your.xinitrc
file or similar.