Skip to content

Instantly share code, notes, and snippets.

@hyc
Last active May 20, 2023 22:53
Show Gist options
  • Save hyc/0d3d4cc4d554c79fa3e33c79c3271faf to your computer and use it in GitHub Desktop.
Save hyc/0d3d4cc4d554c79fa3e33c79c3271faf to your computer and use it in GitHub Desktop.
Toggle keyboard backlight using OpenRGB on Lenovo Legion 7
/* Author: Howard Chu <[email protected]> 2023-05-20
*
* monitor keyboard activity and toggle keyboard backlight
* for Lenovo Legion 7 gen6.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#define DEFPORT 6742
#define NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS 1051 /* OpenRGB NetworkProtocol.h */
#define ZONECOLORS 143
static char dummybuf[8192];
unsigned short port = DEFPORT;
struct sockaddr_in sa;
int kbdfd;
int oldkbmode;
/** @brief How many milliseconds before turning off kbd light */
#ifndef IDLE_MSEC
#define IDLE_MSEC 7000
#endif
typedef struct header {
char magic[4];
unsigned int dev_idx;
unsigned int id;
unsigned int size;
} header;
typedef struct msgdata {
unsigned int size2;
unsigned int zone;
unsigned short num;
unsigned short color[ZONECOLORS*2];
} msgdata;
typedef struct msgpkt {
header hdr;
msgdata dat;
} msgpkt;
static void die(int x)
{
ioctl(kbdfd, KDSKBMODE, oldkbmode);
exit(1);
}
int main(int argc, char *argv[])
{
struct pollfd pfd;
int rc, blfd, scfd;
int brt, timeout, prev = -1;
int maxbrt;
char bm[2] = "0\n";
msgpkt msg = {{{'O','R','G','B'},0,NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS, sizeof(msgdata)},
{sizeof(msgdata),0,ZONECOLORS}};
/* Get max brightness */
scfd = open("/sys/class/backlight/amdgpu_bl1/max_brightness", O_RDONLY);
read(scfd, dummybuf, sizeof(dummybuf));
maxbrt = atoi(dummybuf);
close(scfd);
scfd = open("/sys/class/backlight/amdgpu_bl1/brightness", O_RDONLY);
/* Connect to OpenRGB SDK */
blfd = socket(PF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(blfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("connect");
exit(1);
}
/* pfd.fd = open("/dev/input/by-path/platform-i8042-serio-0-event-kbd", O_RDONLY); */
pfd.fd = open("/dev/tty0", O_RDONLY);
kbdfd = pfd.fd;
pfd.events = POLLIN;
ioctl(kbdfd, KDGKBMODE, &oldkbmode);
signal(SIGINT, die);
ioctl(kbdfd, KDSKBMODE, K_RAW);
timeout = IDLE_MSEC;
while (1) {
rc = poll(&pfd, 1, timeout);
/* Kbd colors range from 0 to 255.
* Screen brightness ranges from 0 to 255.
* Consider making the keyboard brightness
* depend on the screen brightness.
*
* Assume 255 means working in a bright room.
* In that case, leave the kbd light off.
* Otherwise, use screen brightness as kbd brightness.
*/
if (rc) {
/* got keyboard input, flush it all and
* wait for the next event. Also check
* the screen brightness and set the kbd
* backlight accordingly.
*/
read(pfd.fd, dummybuf, sizeof(dummybuf));
timeout = IDLE_MSEC;
read(scfd, dummybuf, sizeof(dummybuf));
lseek(scfd, 0, SEEK_SET);
brt = atoi(dummybuf);
if (brt == maxbrt) {
brt = 0;
}
} else {
/* soft fade out */
brt >>= 1;
timeout = brt ? 100 : -1;
/* once we've gotten a timeout, turn off
* kbd backlight and wait forever for
* the next keypress
*/
}
if (brt == prev)
continue;
{int i;
unsigned short *c = msg.dat.color;
for (i=0; i<ZONECOLORS; i++) {
*c++ = brt<<8;
*c++ = brt;
}
}
write(blfd, &msg, sizeof(msg));
prev = brt;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment