Daemon to detect keypresses or mice movements, Logs idle events and maintains an away-from-keyboard state.
Inputs are registered from anywhere known to the kernel's evdev
subsystem (see kernel Documentation/input/input.rst): VTs outside X,
psuedo-TTYs in X or Wayland, or any other kernel-known inputs. All
mouse movements are known, including from gpm.
idled uses a heuristic match of udev symlink names in /sys/ to determine the keyboard and mouse devices to monitor. It handles added/removed devices by rescanning from start on inotify(7).
Right now, this is used only to tell if we're at the keyboard or not, and generate afk/back output messages and/or logfile. There is a state machine with crude heuristics to determine when the console operator is idle, back, etc (see Configure)
Idle state could inform other things. The plan is to implement configurable scripts / shell commands that will trigger on state changes.
For the moment the output is used with a companion shell loop, like so:
sudo ./idled |& tee fifo & while true do read timestamp length status if [[ $timestamp ]] then if [[ $status == soon ]] then xflashscreen; length=; fi rp echo $length $status else sleep 1; fi; done < fifo
(where rp is an interface to my window manager, and xflashscreen
is a sequence of xcalib -invert calls). The loop causes my screen
to flash and output the message soon when we're close to idle and
afk when we're really idle. There's also an output log of events
that looks something like this:
20250717062352 0:00:11 unidle 20250717062516 0:00:00 soon 20250717062526 0:01:34 afk 20250717062840 0:03:13 unidle 20250717063405 0:00:00 soon 20250717063415 0:05:34 afk 20250717063503 0:00:48 unidle 20250717063744 0:00:00 soon 20250717063850 0:00:00 soon 20250717063900 0:03:56 afk 20250717063901 0:00:01 unidle
This helps a lot to reconstruct things when I forget to enter time tracking events! Lots of integration possibilities with the window manager and time tracking tools.
More in the future! TODO
The config file ~/.idlerc configures idled:
unidle_keyboard_count: 3 unidle_keyboard_secs: 10 unidle_mouse_count: 60 unidle_mouse_secs: 7 idle_secs: 30 restart_secs: 3 rescan_secs: 2
In the future we will add variables to configure execve() on idle
events.
TODO detail those variables!
When devices are added and removed, idled currently rescans devices, resetting the global idle state machine. We could keep a per-device state in the future and use them to infer the global idle state.
The overhead is very low. It uses an edge triggered kernel event. TODO confirm. Specifically, it uses epoll(7) and inotify(7) mechanisms via ctypes-linked C code (via pypi inotify library at the moment).
Requires: inotify-0.2.10+ | https://pypi.org/project/inotify/
We use inotify lib from pypi, but what we use from it is small and we should just port this into idled. We have to go through unnecessary abstractions and have limitation on sleep time per loop when using th library. Better to do ourselves and remove the dependency TODO
example debug run:
$ DEBUG=1 sudo idled debug: enabled > /home/scott/bin/idled(519)main() -> process_rcfile() (Pdb) r debug: making async task process_events-8154414866282 debug: making async task watch_devices-8154414866292 20241030200522 2s device enumeration sleep... debug: paths ['/dev/input/by-path/pci-0000:00:14.0-usb-0:4.2:1.1-event-kbd', '/dev/input/by-path/platform-i8042-serio-0-event-kbd', '/dev/input/by-path/pci-0000:00:14.0-usb-0:4.4:1.0-event-mouse', '/dev/input/by-path/pci-0000:00:15.1-platform-i2c_designware.1-event-mouse'] debug: making async task queue_events-8154414866272 debug: making async task queue_events-8154414866272 debug: making async task queue_events-8154414866272 debug: making async task queue_events-8154414866272 20241030200524 monitoring 4 inputs debug: fd6: type:1, code:30, value:1 debug: fd6: type:0, code:0, value:0 debug: fd6: saw 3, processed 1, key 1, mouse 0 debug: fd6: type:1, code:30, value:0 debug: fd6: type:0, code:0, value:0 debug: fd6: saw 3, processed 1, key 0, mouse 0 debug: fd6: type:1, code:48, value:1 debug: fd6: type:0, code:0, value:0 debug: fd6: saw 3, processed 1, key 1, mouse 0 debug: fd6: type:1, code:48, value:0 debug: fd6: type:0, code:0, value:0 debug: fd6: saw 3, processed 1, key 0, mouse 0 debug: fd8: type:2, code:0, value:1 debug: fd8: type:2, code:1, value:-2 debug: fd8: type:0, code:0, value:0 debug: fd8: saw 3, processed 2, key 0, mouse 2 debug: fd8: type:2, code:1, value:-1 debug: fd8: type:0, code:0, value:0 debug: fd8: saw 2, processed 1, key 0, mouse 1 debug: fd8: type:2, code:1, value:-1 debug: fd8: type:0, code:0, value:0 debug: fd8: saw 2, processed 1, key 0, mouse 1