In my previous post i wrote about toggling the keyboard backlight after a certain idle time using swayidle. One major flaw of this attempt is that swayidle is not inhibited while watching movies or videos via web browsers. Firefox and Chromium are sending dbus messages that request screensaver inhibit. But for some reason the swaywm-developers decided to not listen to these :/ One has to invoke “/bin/swayidle-inhibit” manually to prevent swayidle from doing it’s work. But this will also prevent the keyboard backlight toggling from working. So I came up with a new solution. A programm written in c that listens to input events:
As soon as an event on the requested input devices is recognized a timestamp is set. This is then being compared to an idle time in order to decide whether to turn the backlight on or off.
The idle time and the input devices are handed over as command line arguments:

./toggle_backlight <idle time> <input devices>
./toggle_backlight 20 /dev/input/event4 /dev/input/event18

In this case the idle time would be 20 seconds and events from devices event4 and event18 are taken into account.
To get an idea which event devices are of interest, one could have a look into /dev/input/by-path. Keyboards do have a ‘kbd’ in it’s name.

lrwxrwxrwx 1 root root   9 Dec 28 03:18 platform-i8042-serio-0-event-kbd -> ../event4
lrwxrwxrwx 1 root root  10 Dec 28 03:18 platform-i8042-serio-1-event-mouse -> ../event18

The backlight itself is set via ‘brightnessctl’. A command line tool to control LEDs, which is in this case called from within the c-code.
One has to specify the device whose brightness shell be altered. This is actually hard coded and should definitely be reworked, soon. A general remark: This program is acutally a ‘quick and dirty’ solution. There surely are some things to improve.

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <poll.h>
#include <time.h>

void printDevName(char *dev) {
   int fd = -1;
   char name[256] = "Unknown";
   if ((fd = open(dev, O_RDONLY)) < 0) {
      perror("evdev open");
      exit(1);
   }
   if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
      perror("evdev ioctl");
   }
   printf("Name: %s\n", name);
   close(fd);
}

int getKbBrightness(void) {
   FILE *fp;
   char path[256];
   int brightness = 0;

   /* Open the command for reading. */
   fp = popen("/bin/brightnessctl -d tpacpi::kbd_backlight get 2>&1", "r");
   if (fp == NULL) {
      printf("Failed to run command\n");
      return -1;
   }
   /* Read the output a line at a time - output it. */
   while (fgets(path, sizeof(path), fp) != NULL) {
      printf("%s", path);
      brightness = atoi(path);
   }
   /* close */
   pclose(fp);
   return brightness;
}

int setKbBrightness(int brightness) {
   FILE *fp;
   char path[256];
   char command[256];
   sprintf(command, "/bin/brightnessctl -d tpacpi::kbd_backlight set %d 2>&1", brightness);
   /* Open the command for writing. */
   fp = popen(command, "w");
   if (fp == NULL) {
      printf("Failed to run command\n");
      return -1;
   }
   /* Read the output a line at a time - output it. */
   while (fgets(path, sizeof(path), fp) != NULL) {
      printf("%s", path);
   }
   /* close */
   pclose(fp);
   return 0;
}

// Argument order is path, idle time, <input devices>
int main(int argc, char *argv[]) {

   printf("Listening for events on the following devices:\n");
   for (int i = 2; i < argc; i++) {
      printDevName(argv[i]);
   }

   // Array of file descriptors to the event devices
   struct pollfd fds[argc-1];
   // Open event devices for reading
   for (int i = 2; i < argc; i++) {
      fds[i-2].fd = open(argv[i], O_RDONLY);
      fds[i-2].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
      if (fds[i-2].fd < 0) {
         fprintf(stderr, "Failed opening event device\n");
         exit(1);
      }
   }

   // Array of structs containing information about events
   struct input_event event[argc-1];

   // Timestamp of last activity
   int t0 = (int) time(NULL);

   // Timeout after which keyboard backlight is turned off
   int timeout = atoi(argv[1]);
   int asleep = 0;
   int prev_brightness = 0;

   while (1) {
      for (int i = 0; i < argc - 1; i++) {
         // Check if there is some data ready to be read
         if (poll(&fds[i], 1, 1)) {
            if (fds[i].events != 0) {
               if (read(fds[i].fd, &event[i], sizeof(struct input_event)) < 0) {
                  fprintf(stderr, "failed to read input event from input device %s\n", argv[i]);
                  exit(1);
               }
               if (event[i].type != 0) {
                  /** printf("Key event!\n"); */
                  t0 = (int) time(NULL);
                  /** printf("Timestamp: %d\n", t0); */
                  if (asleep) {
                     asleep = 0;
                     printf("Time to wake up!\n");
                     setKbBrightness(prev_brightness);
                  }
               }
            }
         }
      }
      // No Event for 'timeout' seconds occoured
      if ((int) time(NULL) - t0 > timeout && !asleep) {
         printf("Time to sleep!\n");
         asleep = 1;
         prev_brightness = getKbBrightness();
         setKbBrightness(0);
      }
   }
}


Comments