367 lines
8.7 KiB
C
367 lines
8.7 KiB
C
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
#include <pthread.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdint.h>
|
||
|
#include <signal.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#define MODULE_DATA_SIZE 64
|
||
|
#define MODULE_BLOCK_SIZE 1024
|
||
|
|
||
|
#define BLOCK_CLOCK "clock"
|
||
|
#define BLOCK_VOLUME "volume"
|
||
|
#define BLOCK_MEMORY "memory"
|
||
|
|
||
|
#define MODID_CLOCK 1
|
||
|
#define MODID_VOLUME 2
|
||
|
#define MODID_MEMORY 3
|
||
|
|
||
|
#define PARAM_CLOCK_LONG 1
|
||
|
|
||
|
typedef struct module_ {
|
||
|
timer_t timer;
|
||
|
int interval;
|
||
|
char bufdata[MODULE_DATA_SIZE];
|
||
|
char bufblock[MODULE_BLOCK_SIZE];
|
||
|
char colorbg[16];
|
||
|
char colorfg[16];
|
||
|
uint64_t id;
|
||
|
uint64_t params;
|
||
|
} module_t;
|
||
|
|
||
|
typedef struct appdata_
|
||
|
{
|
||
|
module_t** modules;
|
||
|
size_t mod_count;
|
||
|
pthread_mutex_t mutex;
|
||
|
} appdata_t;
|
||
|
|
||
|
static appdata_t* _data;
|
||
|
|
||
|
static module_t* add_module(appdata_t* data);
|
||
|
static void init_modules(appdata_t* data);
|
||
|
static void module_start(module_t* module);
|
||
|
static void on_timer(union sigval timer_data);
|
||
|
static void module_stop(module_t* module);
|
||
|
static void cleanup(void);
|
||
|
static void fatal_exit(const char* msg);
|
||
|
static int update_module(module_t* m);
|
||
|
static void send_header(void);
|
||
|
static void send_data(void);
|
||
|
static void* thread_func_read(void* arg);
|
||
|
static int update_memory(module_t* mod);
|
||
|
static int update_clock(module_t* m);
|
||
|
static int update_volume(module_t* m);
|
||
|
static int_fast8_t get_button_number(const char* buff);
|
||
|
static module_t* find_first_module_type(appdata_t* data, uint64_t t);
|
||
|
static int command_to_buffer(const char* cmd, char* outbuff, size_t buffsize);
|
||
|
|
||
|
static void cleanup(void) {
|
||
|
for (size_t i = 0; i < _data->mod_count; ++i)
|
||
|
module_stop(_data->modules[i]);
|
||
|
}
|
||
|
|
||
|
static void fatal_exit(const char* msg) {
|
||
|
if (!msg)
|
||
|
perror(msg);
|
||
|
cleanup();
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
static int_fast8_t get_button_number(const char* buff) {
|
||
|
char* pbut = strstr(buff, "\"button\":");
|
||
|
if (NULL == pbut)
|
||
|
return -1;
|
||
|
pbut += 8;
|
||
|
while (0 != pbut) {
|
||
|
if (*pbut >= '0' && *pbut <= '9') {
|
||
|
return (int_fast8_t)(*pbut - '0');
|
||
|
}
|
||
|
++pbut;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void on_click_clock(module_t* m, const char* buffer) {
|
||
|
int_fast8_t butn = get_button_number(buffer);
|
||
|
if (butn >= 2)
|
||
|
system("foot -e tty-clock");
|
||
|
else
|
||
|
m->params ^= PARAM_CLOCK_LONG;
|
||
|
send_data();
|
||
|
}
|
||
|
|
||
|
static module_t* find_first_module_type(appdata_t* data, uint64_t t) {
|
||
|
for (size_t i = 0; i < data->mod_count; ++i) {
|
||
|
if (data->modules[i]->id == t)
|
||
|
return data->modules[i];
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int command_to_buffer(const char* cmd, char* outbuff, size_t buffsize) {
|
||
|
FILE* pf = popen(cmd, "r");
|
||
|
if (!pf) {
|
||
|
strcpy(outbuff, "ERR");
|
||
|
return -1;
|
||
|
}
|
||
|
int retval = 1;
|
||
|
if (!feof(pf)) {
|
||
|
if (fgets(outbuff, buffsize, pf) != NULL) {
|
||
|
if (strlen(outbuff) > 0) {
|
||
|
if (*(outbuff+strlen(outbuff)-1) == '\n')
|
||
|
*(outbuff+strlen(outbuff)-1) = 0;
|
||
|
}
|
||
|
else
|
||
|
retval = 0;
|
||
|
}
|
||
|
else
|
||
|
retval = -1;
|
||
|
}
|
||
|
pclose(pf);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
static void* thread_func_read(void* arg) {
|
||
|
char buffer[1024];
|
||
|
appdata_t* data = (appdata_t*)arg;
|
||
|
module_t* m;
|
||
|
while (1) {
|
||
|
if (fgets(buffer, sizeof(buffer), stdin) == NULL)
|
||
|
break;
|
||
|
if (strstr(buffer, BLOCK_CLOCK) != NULL) {
|
||
|
m = find_first_module_type(data, MODID_CLOCK);
|
||
|
if (m)
|
||
|
on_click_clock(m, buffer);
|
||
|
}
|
||
|
else if (strstr(buffer, BLOCK_MEMORY) != NULL) {
|
||
|
system("foot -e htop");
|
||
|
}
|
||
|
else if (strstr(buffer, BLOCK_VOLUME) != NULL) {
|
||
|
system("pavucontrol");
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void get_module_block(module_t* module, const char* block_name) {
|
||
|
strcpy(module->bufblock, "{\"name\":\"");
|
||
|
strcat(module->bufblock, block_name);
|
||
|
strcat(module->bufblock, "\",\"full_text\":\"");
|
||
|
strcat(module->bufblock, module->bufdata);
|
||
|
/* strcat(data->clock->bufblock, "\",\"short_text\":\"");
|
||
|
strcat(data->clock->bufblock, data->clock->bufdata);*/
|
||
|
strcat(module->bufblock, "\", \"background\":\"");
|
||
|
strcat(module->bufblock, module->colorbg);
|
||
|
strcat(module->bufblock, "\",\"color\":\"");
|
||
|
strcat(module->bufblock, module->colorfg);
|
||
|
strcat(module->bufblock, "\"}");
|
||
|
}
|
||
|
|
||
|
static int update_clock(module_t* m) {
|
||
|
if (NULL == m)
|
||
|
return 0;
|
||
|
char buff[MODULE_DATA_SIZE];
|
||
|
time_t time_now = time(NULL);
|
||
|
if (m->params & PARAM_CLOCK_LONG)
|
||
|
strftime(buff, sizeof(buff), "%H:%M:%S, %a %d.%m.%Y",
|
||
|
localtime((const time_t*)&time_now));
|
||
|
else
|
||
|
strftime(buff, sizeof(buff), "%H:%M:%S",
|
||
|
localtime((const time_t*)&time_now));
|
||
|
if (strcmp(buff, m->bufdata) == 0)
|
||
|
return 0;
|
||
|
strcpy(m->bufdata, buff);
|
||
|
get_module_block(m, BLOCK_CLOCK);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int update_volume(module_t* m) {
|
||
|
if (NULL == m)
|
||
|
return 0;
|
||
|
char buff[MODULE_DATA_SIZE];
|
||
|
int retv = command_to_buffer("show-volume.sh", buff, sizeof(buff));
|
||
|
if (retv <= 0)
|
||
|
return -1;
|
||
|
if (strcmp(buff, m->bufdata) == 0)
|
||
|
return 0;
|
||
|
strcpy(m->bufdata, buff);
|
||
|
get_module_block(m, BLOCK_VOLUME);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int update_memory(module_t* mod) {
|
||
|
if (NULL == mod)
|
||
|
return 0;
|
||
|
FILE* pfile = fopen("/proc/meminfo", "r");
|
||
|
if (NULL == pfile) {
|
||
|
return 0;
|
||
|
}
|
||
|
char line[256];
|
||
|
unsigned int mem_total;
|
||
|
unsigned int mem_avail;
|
||
|
unsigned int swap_total;
|
||
|
unsigned int swap_free;
|
||
|
unsigned int end = 0;
|
||
|
while (fgets(line, sizeof(line), pfile)) {
|
||
|
if (sscanf(line, "MemTotal: %u kB", &mem_total) == 1) {
|
||
|
end |= 1;
|
||
|
continue;
|
||
|
}
|
||
|
if (sscanf(line, "MemAvailable: %u kB", &mem_avail) == 1) {
|
||
|
end |= 2;
|
||
|
continue;
|
||
|
}
|
||
|
if (sscanf(line, "SwapTotal: %u kB", &swap_total) == 1) {
|
||
|
end |= 4;
|
||
|
continue;
|
||
|
}
|
||
|
if (sscanf(line, "SwapFree: %u kB", &swap_free) == 1) {
|
||
|
end |= 8;
|
||
|
continue;
|
||
|
}
|
||
|
if (0xF == end)
|
||
|
break;
|
||
|
}
|
||
|
fclose(pfile);
|
||
|
char buff[MODULE_DATA_SIZE];
|
||
|
sprintf(buff, "%.2f - %.2f GiB",
|
||
|
(double)(mem_total - mem_avail) / (1024 * 1024),
|
||
|
(double)(swap_total - swap_free) / (1024 * 1024));
|
||
|
if (strcmp(buff, mod->bufdata) == 0)
|
||
|
return 0;
|
||
|
strcpy(mod->bufdata, buff);
|
||
|
get_module_block(mod, BLOCK_MEMORY);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static module_t* add_module(appdata_t* data) {
|
||
|
if (0 == data->mod_count) {
|
||
|
data->modules = (module_t**)malloc(sizeof(module_t*));
|
||
|
} else {
|
||
|
data->modules = (module_t**)realloc(data->modules,
|
||
|
(data->mod_count + 1) * sizeof(module_t*));
|
||
|
}
|
||
|
if (NULL == data->modules)
|
||
|
abort();
|
||
|
data->modules[data->mod_count] = (module_t*)malloc(sizeof(module_t));
|
||
|
if (NULL == data->modules[data->mod_count])
|
||
|
abort();
|
||
|
memset(data->modules[data->mod_count], 0, sizeof(module_t));
|
||
|
++data->mod_count;
|
||
|
return data->modules[data->mod_count - 1];
|
||
|
}
|
||
|
|
||
|
static int update_module(module_t* m) {
|
||
|
if (m->id == MODID_MEMORY)
|
||
|
return update_memory(m);
|
||
|
else if (m->id == MODID_CLOCK) {
|
||
|
return update_clock(m);
|
||
|
}
|
||
|
else if (m->id == MODID_VOLUME) {
|
||
|
return update_volume(m);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void send_header(void) {
|
||
|
puts("{ \"version\": 1, \"click_events\":true }");
|
||
|
puts("[ []");
|
||
|
}
|
||
|
|
||
|
static void send_data(void) {
|
||
|
pthread_mutex_lock(&(_data->mutex));
|
||
|
printf(",[");
|
||
|
|
||
|
for (size_t i = 0; i < _data->mod_count; ++i) {
|
||
|
if (i > 0)
|
||
|
printf(",");
|
||
|
printf(_data->modules[i]->bufblock);
|
||
|
}
|
||
|
printf("]\n");
|
||
|
fflush(stdout);
|
||
|
pthread_mutex_unlock(&(_data->mutex));
|
||
|
}
|
||
|
|
||
|
static void on_timer(union sigval timer_data) {
|
||
|
int need_refresh = update_module((module_t*)timer_data.sival_ptr);
|
||
|
if (need_refresh)
|
||
|
send_data();
|
||
|
}
|
||
|
|
||
|
static void module_start(module_t* module) {
|
||
|
struct sigevent sev;
|
||
|
struct itimerspec its;
|
||
|
memset(&sev, 0, sizeof(sev));
|
||
|
sev.sigev_notify = SIGEV_THREAD;
|
||
|
sev.sigev_notify_function = &on_timer;
|
||
|
sev.sigev_value.sival_ptr = module;
|
||
|
if (-1 == timer_create(CLOCK_REALTIME, &sev, &(module->timer))) {
|
||
|
fatal_exit("timer_create");
|
||
|
}
|
||
|
its.it_value.tv_sec = 0;
|
||
|
its.it_value.tv_nsec = 10;
|
||
|
its.it_interval.tv_sec = module->interval;
|
||
|
its.it_interval.tv_nsec = 0;
|
||
|
if (-1 == timer_settime(module->timer, 0, &its, NULL))
|
||
|
fatal_exit("timer_settime");
|
||
|
}
|
||
|
|
||
|
static void module_stop(module_t* module) {
|
||
|
if (NULL == module)
|
||
|
return;
|
||
|
if (0 != timer_delete(module->timer))
|
||
|
fatal_exit("timer_stop");
|
||
|
}
|
||
|
|
||
|
static void init_modules(appdata_t* data) {
|
||
|
module_t* module;
|
||
|
module = add_module(data);
|
||
|
module->id = MODID_MEMORY;
|
||
|
strcpy(module->colorbg, "#3c3c3cff");
|
||
|
strcpy(module->colorfg, "#d0ffd0ff");
|
||
|
module->interval = 2;
|
||
|
|
||
|
module = add_module(data);
|
||
|
module->id = MODID_VOLUME;
|
||
|
strcpy(module->colorbg, "#3c3c3cff");
|
||
|
strcpy(module->colorfg, "#e0e0ffff");
|
||
|
module->interval = 1;
|
||
|
|
||
|
module = add_module(data);
|
||
|
module->id = MODID_CLOCK;
|
||
|
strcpy(module->colorbg, "#3c3c3cff");
|
||
|
strcpy(module->colorfg, "#f0f0f0ff");
|
||
|
module->interval = 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
int main(int argc, char** argv) {
|
||
|
int iret;
|
||
|
pthread_t thread_read;
|
||
|
appdata_t data;
|
||
|
_data = &data;
|
||
|
memset(&data, 0, sizeof(appdata_t));
|
||
|
data.mod_count = 0;
|
||
|
init_modules(&data);
|
||
|
pthread_mutex_init(&data.mutex, NULL);
|
||
|
iret = pthread_create(&thread_read, NULL, &thread_func_read, (void*)&data);
|
||
|
if (0 != iret)
|
||
|
return EXIT_FAILURE;
|
||
|
send_header();
|
||
|
for (size_t i = 0; i < data.mod_count; ++i) {
|
||
|
module_start(data.modules[i]);
|
||
|
update_module(data.modules[i]);
|
||
|
}
|
||
|
while (1) {
|
||
|
pause();
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|