#include #include #include #include #include #include #include #include #include #include #include #include #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; }