Skip to content

Instantly share code, notes, and snippets.

@mniip
Last active July 27, 2019 09:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mniip/ffbd6cdeb2739c6f39bfefdbe9e6a89c to your computer and use it in GitHub Desktop.
Save mniip/ffbd6cdeb2739c6f39bfefdbe9e6a89c to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
FILE *proc_stat_fd;
FILE *proc_meminfo_fd;
FILE *proc_diskstats_fd;
void open_proc_files()
{
proc_stat_fd = fopen("/proc/stat", "r");
if(!proc_stat_fd)
{
perror("/proc/stat");
exit(EXIT_FAILURE);
}
proc_meminfo_fd = fopen("/proc/meminfo", "r");
if(!proc_meminfo_fd)
{
perror("/proc/meminfo");
exit(EXIT_FAILURE);
}
proc_diskstats_fd = fopen("/proc/diskstats", "r");
if(!proc_diskstats_fd)
{
perror("/proc/diskstats");
exit(EXIT_FAILURE);
}
}
char const *hid_filename = NULL;
int hid_fd = -1;
void open_hid_file()
{
if(hid_fd == -1)
{
hid_fd = open(hid_filename, O_RDWR);
if(hid_fd == -1)
{
perror("open");
usleep(1000000);
}
}
}
void close_hid_file()
{
if(hid_fd != -1)
close(hid_fd);
hid_fd = -1;
}
typedef struct {
uint8_t r, g, b;
} color;
typedef struct {
uint8_t key;
color color;
} key_status;
typedef struct {
uint8_t num;
uint8_t request[4];
} report_hdr;
void set_key_statuses(size_t len, key_status *sts)
{
open_hid_file();
struct { report_hdr hdr; key_status sts[0x100]; } report;
report.hdr.num = 0x00;
report.hdr.request[0] = 0x0C;
report.hdr.request[1] = 0x00;
report.hdr.request[2] = 0x6E;
report.hdr.request[3] = 0x00;
memcpy(report.sts, sts, sizeof(*sts) * len);
if(hid_fd != -1)
{
size_t l = sizeof report.hdr + sizeof(*sts) * len;
errno = 0;
if(ioctl(hid_fd, HIDIOCSFEATURE(l), &report) != l)
{
perror("ioctl(HIDIOCSFEATURE)");
close_hid_file();
usleep(1000000);
}
}
}
typedef struct {
int enabled;
unsigned long user, nice, system, idle, iowait, irq, softirq;
} core_stats;
core_stats *get_core_stats(int *num_cores)
{
core_stats *stats = NULL;
size_t num_stats = 0;
char *line = NULL;
size_t line_sz = 0;
rewind(proc_stat_fd);
getline(&line, &line_sz, proc_stat_fd);
while(1)
{
if(getline(&line, &line_sz, proc_stat_fd) <= 0)
break;
core_stats st;
int num_core;
if(8 > sscanf(line, "cpu%d%lu%lu%lu%lu%lu%lu%lu", &num_core,
&st.user, &st.nice, &st.system, &st.idle, &st.iowait, &st.irq, &st.softirq))
break;
st.enabled = 1;
if(num_core >= num_stats)
{
stats = realloc(stats, sizeof *stats * (num_core + 1));
memset(stats + num_stats, 0, sizeof *stats * (num_core + 1 - num_stats));
num_stats = num_core + 1;
}
memcpy(&stats[num_core], &st, sizeof st);
}
free(line);
*num_cores = num_stats;
return stats;
}
typedef struct {
unsigned long total, free, available, buffers, cached, swaptotal, swapfree;
} mem_stats;
mem_stats get_mem_stats()
{
mem_stats st;
memset(&st, 0, sizeof st);
char *line = NULL;
size_t line_sz = 0;
rewind(proc_meminfo_fd);
while(1)
{
if(getline(&line, &line_sz, proc_meminfo_fd) <= 0)
break;
sscanf(line, "MemTotal:%lu", &st.total) ||
sscanf(line, "MemFree:%lu", &st.free) ||
sscanf(line, "MemAvailable:%lu", &st.available) ||
sscanf(line, "Buffers:%lu", &st.buffers) ||
sscanf(line, "Cached:%lu", &st.cached) ||
sscanf(line, "SwapTotal:%lu", &st.swaptotal) ||
sscanf(line, "SwapFree:%lu", &st.swapfree);
}
free(line);
return st;
}
typedef struct {
char const *dev_name;
int enabled;
unsigned long read_ms, wrote_ms;
} disk_stats;
void get_disk_stats(disk_stats *sts, size_t len)
{
char *line = NULL;
size_t line_sz = 0;
rewind(proc_diskstats_fd);
for(int i = 0; i < len; i++)
sts[i].enabled = 0;
while(1)
{
if(getline(&line, &line_sz, proc_diskstats_fd) <= 0)
break;
unsigned long read_ms, wrote_ms;
char *name = NULL;
if(3 > sscanf(line, "%*d%*d%ms%*lu%*lu%*lu%lu%*lu%*lu%*lu%lu",
&name, &read_ms, &wrote_ms))
{
free(name);
break;
}
for(int i = 0; i < len; i++)
if(!strcmp(name, sts[i].dev_name))
{
sts[i].enabled = 1;
sts[i].read_ms = read_ms;
sts[i].wrote_ms = wrote_ms;
}
free(name);
}
free(line);
}
int num_cores = 0;
double *ema_loads;
core_stats *last_cores;
void update_ema_loads()
{
int new_cores = 0;
core_stats *stats = get_core_stats(&new_cores);
if(new_cores > num_cores)
{
ema_loads = realloc(ema_loads, sizeof *ema_loads * new_cores);
memset(ema_loads + num_cores, 0, sizeof *ema_loads * (new_cores - num_cores));
last_cores = realloc(last_cores, sizeof *last_cores * new_cores);
memcpy(last_cores + num_cores, stats + num_cores, sizeof *stats * (new_cores - num_cores));
num_cores = new_cores;
}
for(int i = 0; i < new_cores; i++)
{
if(stats[i].enabled)
{
unsigned long duser = stats[i].user - last_cores[i].user;
unsigned long dnice = stats[i].nice - last_cores[i].nice;
unsigned long dsystem = stats[i].system - last_cores[i].system;
unsigned long didle = stats[i].idle - last_cores[i].idle;
unsigned long diowait = stats[i].iowait - last_cores[i].iowait;
unsigned long dirq = stats[i].irq - last_cores[i].irq;
unsigned long dsoftirq = stats[i].softirq - last_cores[i].softirq;
double load = (duser + dnice + dsystem + dirq + dsoftirq) / (double)(duser + dnice + dsystem + didle + diowait + dirq + dsoftirq);
if(!(load == load)) load = 0.0;
ema_loads[i] = load * 0.5 + ema_loads[i] * 0.5;
}
else
ema_loads[i] = 0;
}
for(int i = new_cores; i < num_cores; i++)
last_cores[i].enabled = 0;
memcpy(last_cores, stats, sizeof *stats * new_cores);
free(stats);
}
double mem_used, mem_buffers, mem_cached, mem_swapped;
void update_mem()
{
mem_stats st = get_mem_stats();
mem_used = (st.total - st.free - st.cached - st.buffers) / (double)st.total;
mem_buffers = st.buffers / (double)st.total;
mem_cached = st.cached / (double)st.total;
mem_swapped = (st.swaptotal - st.swapfree) / (double)st.swaptotal;
}
int num_disks = 0;
double *ema_reads;
double *ema_writes;
disk_stats *last_disks;
void update_ema_io(char const **disks)
{
size_t len = 0;
while(disks[len])
len++;
disk_stats *stats = calloc(sizeof *stats, len);
for(int i = 0; i < len; i++)
stats[i].dev_name = disks[i];
get_disk_stats(stats, len);
if(len != num_disks)
{
ema_reads = realloc(ema_reads, sizeof *ema_reads * len);
ema_writes = realloc(ema_writes, sizeof *ema_writes * len);
last_disks = realloc(last_disks, sizeof *last_disks * len);
if(len > num_disks)
{
memset(ema_reads + num_disks, 0, sizeof *ema_reads * (len - num_disks));
memset(ema_writes + num_disks, 0, sizeof *ema_writes * (len - num_disks));
memset(last_disks + num_disks, 0, sizeof *last_disks * (len - num_disks));
}
num_disks = len;
}
for(int i = 0; i < num_disks; i++)
{
if(stats[i].enabled)
{
unsigned long dread = stats[i].read_ms - last_disks[i].read_ms;
unsigned long dwrote = stats[i].wrote_ms - last_disks[i].wrote_ms;
double reads = dread / 1000.0;
if(reads > 1.0) reads = 1.0;
double writes = dwrote / 1000.0;
if(writes > 1.0) writes = 1.0;
ema_reads[i] = reads * 0.5 + ema_reads[i] * 0.5;
ema_writes[i] = writes * 0.5 + ema_writes[i] * 0.5;
}
else
{
ema_reads[i] = 0;
ema_writes[i] = 0;
}
}
memcpy(last_disks, stats, sizeof *stats * num_disks);
free(stats);
}
color mkcolor(int r, int g, int b)
{
color clr;
clr.r = r < 0 ? 0 : r > 0xFF ? 0xFF : r;
clr.g = g < 0 ? 0 : g > 0xFF ? 0xFF : g;
clr.b = b < 0 ? 0 : b > 0xFF ? 0xFF : b;
return clr;
}
color load_to_color(double l)
{
if(l <= 0.5)
return mkcolor(l * 511, 255, 0);
else
return mkcolor(244, (1 - l) * 511, 0);
}
color io_to_color(double r, double w)
{
return mkcolor(w * 255, 0, r * 255);
}
void mix_color(color *tgt, color src, double alpha)
{
if(alpha < 0) alpha = 0;
else if(alpha > 1) alpha = 1;
tgt->r += src.r * alpha;
tgt->g += src.g * alpha;
tgt->b += src.b * alpha;
}
void color_line(key_status *sts, size_t len, double from, double to, color clr)
{
from *= len;
to *= len;
for(size_t i = 0; i < len; i++)
{
if(i >= from && i + 1 <= to)
sts[i].color = clr;
else if(i < from && i + 1 >= from && i + 1 <= to)
mix_color(&sts[i].color, clr, i + 1 - from);
else if(i >= from && i <= to && i + 1 > to)
mix_color(&sts[i].color, clr, to - i);
else if(i < from && i + 1 >= from && i <= to && i + 1 > to)
mix_color(&sts[i].color, clr, to - from);
}
}
/*
Esc(0x29) F1(0x3A) F2(0x3B) F3(0x3C) F4(0x3D) F5(0x3E) F6(0x3F) F7(0x40) F8(0x41) F9(0x42) F10(0x43) F11(0x44) F12(0x45) PrtScr(0x46) ScrLk(0x47) Pause(0x48) Ins(0x49) Del(0x4C) PgUp(0x4B) PgDn(0x4E)
`(0x35) 1(0x1E) 2(0x1F) 3(0x20) 4(0x21) 5(0x22) 6(0x23) 7(0x24) 8(0x25) 9(0x26) 0(0x27) -(0x2D) =(0x2E) BkSp(0x2A) NumLk(0x53) Num/(0x54) Num*(0x55) Num-(0x56)
Tab(0x2B) Q(0x14) W(0x1A) E(0x08) R(0x15) T(0x17) Y(0x1C) U(0x18) I(0x0C) O(0x12) P(0x13) [(0x2F) ](0x30) \(0x31) Num7(0x5F) Num8(0x60) Num9(0x61) Num+(0x57)
Caps(0x39) A(0x05) S(0x16) D(0x07) F(0x09) G(0x0A) H(0x0B) J(0x0D) K(0x0E) L(0x0F) ;(0x33) '(0x34) Enter(0x28) Num4(0x5C) Num5(0x5D) Num6(0x5E)
LShift(0xE1) Z(0x1D) X(0x1B) C(0x06) V(0x19) B(0x05) N(0x11) M(0x10) ,(0x36) .(0x37) /(0x38) RShift(0xE5) Up(0x52) Num1(0x59) Num2(0x5A) Num3(0x5B) NumEnter(0x58)
LCtrl(0xE0) Fn(0xF0) LAlt(0xE2) Space(0x2C) <(0x64) RAlt(0xE6) Win(0xE3) RCtrl(0xE4) Left(0x50) Down(0x51) Right(0x4F) Num0(0x62) Num.(0x63)
*/
int cpu_keys[] = {0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4C};
int num_cpu_keys = sizeof cpu_keys / sizeof *cpu_keys;
int memory_keys[] = {0x35, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2D, 0x2E, 0x2A, 0x53, 0x54, 0x55, 0x56};
int num_memory_keys = sizeof memory_keys / sizeof *memory_keys;
int swap_keys[] = {0x2B, 0x14, 0x1A, 0x08, 0x15, 0x17, 0x1C, 0x18, 0x0C, 0x12, 0x13, 0x2F, 0x30, 0x31, 0x5F, 0x60, 0x61, 0x57};
int num_swap_keys = sizeof swap_keys / sizeof *swap_keys;
int disk_keys[] = {0x4B, 0x4E};
int num_disk_keys = sizeof disk_keys / sizeof *disk_keys;
char const *disks[] = {"nvme0n1", "sda", NULL};
int main(int argc, char *argv[])
{
hid_filename = argc > 1 ? argv[1] : "/dev/hidraw0";
open_proc_files();
while(1)
{
update_ema_loads();
update_mem();
update_ema_io(disks);
key_status sts[num_cpu_keys + num_memory_keys + num_swap_keys + num_disk_keys];
key_status *cpu_sts = sts;
key_status *mem_sts = sts + num_cpu_keys;
key_status *swap_sts = sts + num_cpu_keys + num_memory_keys;
key_status *disk_sts = sts + num_cpu_keys + num_memory_keys + num_swap_keys;
for(int i = 0; i < num_cpu_keys; i++)
{
cpu_sts[i].key = cpu_keys[i];
cpu_sts[i].color = (i < num_cores && last_cores[i].enabled)
? load_to_color(ema_loads[i])
: mkcolor(0, 0, 0);
}
for(int i = 0; i < num_memory_keys; i++)
{
mem_sts[i].key = memory_keys[i];
mem_sts[i].color = mkcolor(0, 0, 0);
}
color_line(mem_sts, num_memory_keys, 0.0, mem_used, mkcolor(0, 255, 0));
color_line(mem_sts, num_memory_keys, mem_used, mem_used + mem_buffers, mkcolor(0, 0, 255));
color_line(mem_sts, num_memory_keys, mem_used + mem_buffers, mem_used + mem_buffers + mem_cached, mkcolor(255, 127, 0));
for(int i = 0; i < num_swap_keys; i++)
{
swap_sts[i].key = swap_keys[i];
swap_sts[i].color = mkcolor(0, 0, 0);
}
color_line(swap_sts, num_swap_keys, 0.0, mem_swapped, mkcolor(255, 0, 0));
for(int i = 0; i < num_disk_keys; i++)
{
disk_sts[i].key = disk_keys[i];
disk_sts[i].color = (i < num_disks && last_disks[i].enabled)
? io_to_color(ema_reads[i], ema_writes[i])
: mkcolor(0, 0, 0);
}
set_key_statuses(num_cpu_keys + num_memory_keys + num_swap_keys + num_disk_keys, sts);
usleep(100000);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment