Skip to content

Instantly share code, notes, and snippets.

@metatoaster
Last active October 12, 2020 12:25
Show Gist options
  • Select an option

  • Save metatoaster/cc24aa628e5a9fc56b93a6dc721ddbb4 to your computer and use it in GitHub Desktop.

Select an option

Save metatoaster/cc24aa628e5a9fc56b93a6dc721ddbb4 to your computer and use it in GitHub Desktop.
Caps lock LED for HDD Activity
#!/usr/bin/env python
"""
Make Caps Lock LED to show HDD activity, for Xorg/X11 on Linux
This Python script uses ctypes on the libX11 shared object, /proc/vmstat
pseudo-file provided by the Linux kernel to turn a keyboard's caps lock
light to act like a HDD activity LED.
The LED to be controlled must have allowExplicit enabled. Easiest way
to achieve this is to write out a xkbcomp configuration file and replace
all instaces of ``!allowExplicit`` with ``allowExplicit`` like so::
$ xkbcomp $DISPLAY /tmp/xkbconf
$ sed -i 's/\!allowExplicit/allowExplicit/' /tmp/xkbconf
$ xkbcomp /tmp/xkbconf $DISPLAY
$ rm /tmp/xkbconf
If the low level API calls to the above are known they can be included
into this script.
"""
import os
import sys
from ctypes import Structure
from ctypes import byref
from ctypes import cdll
from ctypes import c_int
from ctypes import c_uint
from time import sleep
# library
x11 = cdll.LoadLibrary("libX11.so")
XCloseDisplay = x11.XCloseDisplay
# this is actually wrapped later
# XOpenDisplay = x11.XOpenDisplay
# Constants and structures
timeout = 0.04
KBLed = 1 << 4
KBLedMode = 1 << 5
OFF = 0
ON = 1
vmstat_filename = '/proc/vmstat'
class XKeyboardControl(Structure):
_fields_ = [
("key_click_percent", c_int),
("bell_percent", c_int),
("bell_pitch", c_int),
("bell_duration", c_int),
("led", c_int),
("led_mode", c_int),
("key", c_int),
# On, Off, Default */
("auto_repeat_mode", c_int),
]
# functions
def error(*a):
"""
A dumb way to spit out an error message.
"""
for i in a:
sys.stderr.write(i)
sys.stderr.write('\n')
sys.exit(1)
def get_vmstat_pginout(fd):
"""
Extract the page in and page out values. Only grab the strings.
"""
fd.seek(0)
return [s for s in fd.readlines() if s.startswith('pgpg')]
def XOpenDisplay():
"""
Just let the underlying lib do its job, but die in a fire otherwise.
"""
dpy = x11.XOpenDisplay(None)
if not dpy:
error('Failed to connect to X Server ', os.environ.get('DISPLAY'))
return dpy
def set_led(led, mode):
"""
Similar to the set_led function in xset, but without the display
argument because it is opened from within here.
"""
dpy = XOpenDisplay()
values = XKeyboardControl()
values.led = led
values.led_mode = mode
x11.XChangeKeyboardControl(dpy, c_int(KBLed | KBLedMode), byref(values))
XCloseDisplay(dpy)
def startup_check():
"""
Gracefully exit rather than segfault when display is not found.
"""
dpy = XOpenDisplay()
x11.XCloseDisplay(dpy)
if not os.path.exists(vmstat_filename):
error(
'"', vmstat_filename, '" not found. ',
'Linux Kernel must be compiled with VM_EVENT_COUNTERS enabled.',
)
def main(led):
with open(vmstat_filename) as vmstat:
status = get_vmstat_pginout(vmstat)
try:
while True:
sleep(timeout)
current = get_vmstat_pginout(vmstat)
if current == status:
set_led(led, OFF)
else:
set_led(led, ON)
status = current
except KeyboardInterrupt:
print('Quitting...')
if __name__ == '__main__':
led = 1 # Caps Lock
main(led)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment