Page 1 of 1

Linux idle time detection broken

Posted: Tue Apr 21, 2020 2:46 am
by MrBurritoTaco
The idle detection algorithm seems to be broken in Linux, even though X Server provides this information in it's API see this

Code: Select all

https://github.com/gpolitis/xidle/blob/master/xidle.c or 
https://github.com/g0hl1n/xprintidle/blob/master/xprintidle.c
xidle is show below

Code: Select all

#include <stdio.h>
#include <X11/extensions/scrnsaver.h>

main() {
  XScreenSaverInfo *info = XScreenSaverAllocInfo();
  Display *display = XOpenDisplay(0);

  XScreenSaverQueryInfo(display, DefaultRootWindow(display), info);
  printf("%u ms\n", info->idle);
}
This code upon testing produces segfaults when the info or display pointers are NULL and is therefore unsafe for use, it however does show what is needed at a minimum to get idle time in milliseconds.
xprintidle is a safer approach as it contains error checking and a workarounds for some systems. (See workaroundCreepyXServer in xprintidle link).

This will only work on systems with an xserver running, which will still leave headless systems with broken idle time detection. You could use the 'w' command and awk something useful out of it, or possibly grab the source here

Code: Select all

https://gitlab.com/procps-ng/procps/-/blob/master/w.c
as a baseline to derive what's needed.

Re: Linux idle time detection broken

Posted: Tue Apr 21, 2020 5:22 am
by MrBurritoTaco
For those looking for a workaround, here's a simple python script I made. Ensure that While I'm Working is checked and that you have 'xprintidle' installed, on ubuntu sudo apt install xprintidle should do.

Code: Select all

#!/usr/bin/env python
from sys import argv
from time import sleep
from subprocess import Popen, PIPE

interval = 10 #Number of checks a minute
idle_minutes = 3 if len(argv) == 1 else float(argv[1]) #default to 3 min
paused = False #Assume running, will send a paused signal right away

while True:

  idle_time = int(Popen(["env", "DISPLAY=:0.0", "xprintidle"], stdout=PIPE).stdout.read())
  if idle_time > idle_minutes * 60000 and paused:
    Popen(["FAHClient", "--send-unpause"], stdout=PIPE)
    print "resume"
    paused = False
  if idle_time < idle_minutes * 60000 and not paused:
    Popen(["FAHClient", "--send-pause"], stdout=PIPE)
    print "pause"
    paused = True

  sleep(60 / interval)