845 lines
26 KiB
C
845 lines
26 KiB
C
|
#include <ctype.h>
|
||
|
#include <math.h>
|
||
|
#include <pthread.h>
|
||
|
#include <signal.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#ifdef HAS_X11
|
||
|
#include <X11/Xlib.h>
|
||
|
#endif
|
||
|
|
||
|
#define FALSE 0
|
||
|
#define TRUE 1
|
||
|
|
||
|
#define NO_REFRESH 0
|
||
|
#define NO_SIGNAL 0
|
||
|
|
||
|
#define MILISECONDS(t) (t)
|
||
|
#define SECONDS(t) (t * 1000)
|
||
|
#define MINUTES(t) (SECONDS(t) * 60)
|
||
|
#define HOURS(t) (MINUTES(t) * 60)
|
||
|
|
||
|
struct BarModule {
|
||
|
const char *name;
|
||
|
void (*init)(void);
|
||
|
char *(*poll)(void);
|
||
|
const unsigned long refresh_time;
|
||
|
const int signal;
|
||
|
unsigned long last_refresh;
|
||
|
char *last_text;
|
||
|
size_t last_text_length;
|
||
|
int disabled;
|
||
|
};
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
typedef int error_code;
|
||
|
|
||
|
#define ERROR_NONE 0
|
||
|
#define ERROR_UNKNOWN 1
|
||
|
#define ERROR_ARG 2
|
||
|
#define ERROR_MEMORY 3
|
||
|
#define ERROR_MODULE 4
|
||
|
#define ERROR_CONFIG 5
|
||
|
#define ERROR_X11 6
|
||
|
|
||
|
typedef int arg_action;
|
||
|
|
||
|
#define ARG_ACTION_NONE 0
|
||
|
#define ARG_ACTION_CONTINUE 1
|
||
|
#define ARG_ACTION_STOP 2
|
||
|
#define ARG_ACTION_SKIP 3
|
||
|
|
||
|
static const size_t global_prefix_length = sizeof(PREFIX_TEXT) - 1;
|
||
|
static const size_t global_suffix_length = sizeof(SUFFIX_TEXT) - 1;
|
||
|
static const size_t global_seperator_length = sizeof(SEPERATOR_TEXT) - 1;
|
||
|
static const size_t global_module_count =
|
||
|
sizeof(modules) / sizeof(struct BarModule);
|
||
|
static size_t global_disabled_module_count = 0;
|
||
|
static int global_interpret_flags = TRUE;
|
||
|
static error_code global_last_error = ERROR_NONE;
|
||
|
static const char *global_exec_name;
|
||
|
static struct timespec global_refresh_time;
|
||
|
static clockid_t global_cpu_clock_id;
|
||
|
static unsigned long global_last_refresh;
|
||
|
static pthread_t global_main_thread;
|
||
|
static sigset_t global_real_time_mask;
|
||
|
|
||
|
#ifdef HAS_X11
|
||
|
static Display *global_x_display;
|
||
|
static int global_x_screen;
|
||
|
static Window global_x_root;
|
||
|
static const char *flag_x_display_name;
|
||
|
#elif DEFAULT_SET_ROOT_TITLE != FALSE
|
||
|
#error "DEFAULT_SET_ROOT_TITLE can't be TRUE unless X11 support is included"
|
||
|
#endif
|
||
|
|
||
|
static int flag_print_version = FALSE;
|
||
|
static int flag_print_help = FALSE;
|
||
|
static int flag_print_info = FALSE;
|
||
|
static int flag_set_root_title = DEFAULT_SET_ROOT_TITLE;
|
||
|
static int flag_write_to_stdout = !DEFAULT_SET_ROOT_TITLE;
|
||
|
|
||
|
/* internal function declarations */
|
||
|
static void qtb_exit(void);
|
||
|
static void qtb_exit_with_status(error_code status);
|
||
|
static void check_config(void);
|
||
|
static const char *check_arg_value(char arg, const char *value,
|
||
|
arg_action current_action);
|
||
|
static void set_disabled_modules(const char *arg);
|
||
|
static arg_action interpret_flag_character(char arg, const char *next,
|
||
|
arg_action current_action);
|
||
|
static arg_action interpret_flag_arg(const char *arg, const char *next);
|
||
|
static arg_action interpret_normal_arg(const char *arg, const char *next);
|
||
|
static void interpret_args(int argc, const char **argv);
|
||
|
static void print_flag_error(char arg);
|
||
|
static void print_arg_error(const char *arg);
|
||
|
static void print_help_and_exit(void);
|
||
|
static void print_version_and_exit(void);
|
||
|
static void print_info_and_exit(void);
|
||
|
static unsigned long find_gcf(unsigned long *nums, size_t len);
|
||
|
static unsigned long *find_prime_factors(unsigned long n, size_t *out_len);
|
||
|
static void initialize_modules(void);
|
||
|
static void set_refresh_time(void);
|
||
|
static void signal_handler(int signum);
|
||
|
static void setup_signals(void);
|
||
|
static unsigned long find_module_refresh_gcf(void);
|
||
|
static unsigned long timespec_to_miliseconds(struct timespec *ts);
|
||
|
static void miliseconds_to_timespec(unsigned long ms, struct timespec *out_ts);
|
||
|
static void do_initial_refresh(void);
|
||
|
static void refresh_module(struct BarModule *module);
|
||
|
static int check_and_refresh_module(struct BarModule *module,
|
||
|
unsigned long current_time);
|
||
|
static void module_poll_loop(void);
|
||
|
static void rewrite_bar(void);
|
||
|
static void timespec_diff(const struct timespec *start,
|
||
|
const struct timespec *end, struct timespec *out);
|
||
|
static void init_x(void);
|
||
|
static void set_x_root_title(const char *title);
|
||
|
|
||
|
/* module api functions */
|
||
|
void _qtb_internal_log(const char *fmt, ...);
|
||
|
#define qtb_log(...) _qtb_internal_log(__VA_ARGS__)
|
||
|
void qtb_die(void);
|
||
|
void qtb_signal_modules(int sig);
|
||
|
static char *read_file(int fd);
|
||
|
static int handle_execute_parent(pid_t pid, int child_in, int child_out,
|
||
|
int child_err, const char *in,
|
||
|
const char **out, const char **err);
|
||
|
static void handle_execute_child(const char *name, const char **argv, int in,
|
||
|
int out, int err);
|
||
|
void *qtb_malloc(size_t size);
|
||
|
static int is_overflow(size_t n1, size_t n2);
|
||
|
void *qtb_calloc(size_t n, size_t size);
|
||
|
void *qtb_realloc(void *ptr, size_t size);
|
||
|
char *qtb_strdup(const char *str);
|
||
|
void qtb_free(void *ptr);
|
||
|
#ifdef HAS_X11
|
||
|
Display *qtb_get_x_display(void);
|
||
|
#endif
|
||
|
|
||
|
/* function definitions */
|
||
|
int main(int argc, const char **argv) {
|
||
|
global_exec_name = argv[0];
|
||
|
global_main_thread = pthread_self();
|
||
|
check_config();
|
||
|
interpret_args(argc, argv);
|
||
|
if (flag_print_help) {
|
||
|
print_help_and_exit();
|
||
|
} else if (flag_print_version) {
|
||
|
print_version_and_exit();
|
||
|
} else if (flag_print_info) {
|
||
|
print_info_and_exit();
|
||
|
} else if (global_disabled_module_count == global_module_count) {
|
||
|
qtb_log("all modules disabled! exiting...");
|
||
|
qtb_exit_with_status(ERROR_ARG);
|
||
|
} else if (global_last_error != ERROR_NONE) {
|
||
|
qtb_exit();
|
||
|
}
|
||
|
if (clock_getcpuclockid(0, &global_cpu_clock_id) != 0) {
|
||
|
qtb_log("error: could not get cpu clock id");
|
||
|
qtb_exit_with_status(ERROR_UNKNOWN);
|
||
|
}
|
||
|
init_x();
|
||
|
set_refresh_time();
|
||
|
setup_signals();
|
||
|
initialize_modules();
|
||
|
do_initial_refresh();
|
||
|
sigprocmask(SIG_UNBLOCK, &global_real_time_mask, NULL);
|
||
|
module_poll_loop();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void qtb_exit() {
|
||
|
qtb_exit_with_status(global_last_error);
|
||
|
}
|
||
|
|
||
|
static void qtb_exit_with_status(error_code status) {
|
||
|
exit(status);
|
||
|
}
|
||
|
|
||
|
static void check_config() {
|
||
|
if (global_module_count == 0) {
|
||
|
qtb_log("error: no modules defined");
|
||
|
qtb_exit_with_status(ERROR_CONFIG);
|
||
|
}
|
||
|
#ifdef REFRESH_TIME
|
||
|
if (REFRESH_TIME <= 0) {
|
||
|
qtb_log("error: refresh time must be greater than 0, was: '%lu'",
|
||
|
REFRESH_TIME);
|
||
|
qtb_exit_with_status(ERROR_CONFIG);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* if current_action is ARG_ACTION_SKIP, print an error and return NULL,
|
||
|
otherwise, return value */
|
||
|
static const char *check_arg_value(char arg, const char *value,
|
||
|
arg_action current_action) {
|
||
|
if (current_action == ARG_ACTION_SKIP) {
|
||
|
qtb_log("error: value already in use by another option: '%s'", arg);
|
||
|
return NULL;
|
||
|
}
|
||
|
if (!value) {
|
||
|
qtb_log("error: option requires value: '%c'", arg);
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
static void set_disabled_modules(const char *arg) {
|
||
|
if (arg == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
/* reset module disabled state */
|
||
|
global_disabled_module_count = 0;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
modules[i].disabled = FALSE;
|
||
|
}
|
||
|
char *arg_copy = qtb_strdup(arg);
|
||
|
char *end_ptr;
|
||
|
const char *current = strtok_r(arg_copy, ";", &end_ptr);
|
||
|
while (current) {
|
||
|
/* size_t i; */
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (strcmp(modules[i].name, current) == 0) {
|
||
|
modules[i].disabled = TRUE;
|
||
|
++global_disabled_module_count;
|
||
|
goto found_module;
|
||
|
}
|
||
|
}
|
||
|
qtb_log("attempt to disable unknown module: '%s'", current);
|
||
|
found_module:
|
||
|
current = strtok_r(NULL, ";", &end_ptr);
|
||
|
continue;
|
||
|
}
|
||
|
qtb_free(arg_copy);
|
||
|
}
|
||
|
|
||
|
/* if current_action is ARG_ACTION_SKIP, error if we encounter an arg with a
|
||
|
* value */
|
||
|
static arg_action interpret_flag_character(char arg, const char *next,
|
||
|
arg_action current_action) {
|
||
|
switch (arg) {
|
||
|
case 'v':
|
||
|
flag_print_version = TRUE;
|
||
|
return ARG_ACTION_STOP;
|
||
|
case 'h':
|
||
|
flag_print_help = TRUE;
|
||
|
return ARG_ACTION_STOP;
|
||
|
case 'i':
|
||
|
flag_print_info = TRUE;
|
||
|
return ARG_ACTION_STOP;
|
||
|
case 's':
|
||
|
flag_write_to_stdout = TRUE;
|
||
|
flag_set_root_title = FALSE;
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
case 'n':
|
||
|
set_disabled_modules(check_arg_value(arg, next, current_action));
|
||
|
return ARG_ACTION_SKIP;
|
||
|
#ifdef HAS_X11
|
||
|
case 'd':
|
||
|
flag_x_display_name = check_arg_value(arg, next, current_action);
|
||
|
return ARG_ACTION_SKIP;
|
||
|
case 'r':
|
||
|
flag_set_root_title = TRUE;
|
||
|
flag_write_to_stdout = FALSE;
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
#else
|
||
|
case 'd':
|
||
|
case 'r':
|
||
|
qtb_log("error: X11 support not included in build");
|
||
|
global_last_error = ERROR_X11;
|
||
|
return ARG_ACTION_STOP;
|
||
|
#endif
|
||
|
default:
|
||
|
global_last_error = ERROR_ARG;
|
||
|
print_flag_error(arg);
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static arg_action interpret_flag_arg(const char *arg, const char *next) {
|
||
|
if (!global_interpret_flags) {
|
||
|
return ARG_ACTION_NONE;
|
||
|
}
|
||
|
if (strcmp(arg, "--") == 0) {
|
||
|
global_interpret_flags = FALSE;
|
||
|
return ARG_ACTION_NONE;
|
||
|
}
|
||
|
if (arg[0] == '\0') {
|
||
|
print_arg_error(arg);
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
}
|
||
|
if (arg[0] == '-') {
|
||
|
if (arg[1] == '\0') {
|
||
|
print_arg_error(arg);
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
}
|
||
|
arg_action return_action = ARG_ACTION_CONTINUE;
|
||
|
++arg;
|
||
|
while (*arg) {
|
||
|
arg_action current_action =
|
||
|
interpret_flag_character(*arg, next, return_action);
|
||
|
if (current_action == ARG_ACTION_STOP) {
|
||
|
return ARG_ACTION_STOP;
|
||
|
} else if (current_action == ARG_ACTION_CONTINUE && return_action != ARG_ACTION_SKIP) {
|
||
|
return_action = ARG_ACTION_CONTINUE;
|
||
|
} else if (current_action == ARG_ACTION_SKIP) {
|
||
|
return_action = ARG_ACTION_SKIP;
|
||
|
}
|
||
|
++arg;
|
||
|
}
|
||
|
return return_action;
|
||
|
}
|
||
|
return ARG_ACTION_NONE;
|
||
|
}
|
||
|
|
||
|
static arg_action interpret_normal_arg(const char *arg, const char *next) {
|
||
|
print_arg_error(arg);
|
||
|
return ARG_ACTION_CONTINUE;
|
||
|
}
|
||
|
|
||
|
static void interpret_args(int argc, const char **argv) {
|
||
|
int i;
|
||
|
for (i = 1; i < argc; ++i) {
|
||
|
const char *current_arg = argv[i];
|
||
|
const char *current_value = argv[i + 1];
|
||
|
arg_action flag_action = interpret_flag_arg(current_arg, current_value);
|
||
|
switch (flag_action) {
|
||
|
case ARG_ACTION_NONE:
|
||
|
break;
|
||
|
case ARG_ACTION_CONTINUE:
|
||
|
continue;
|
||
|
case ARG_ACTION_STOP:
|
||
|
return;
|
||
|
case ARG_ACTION_SKIP:
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
arg_action normal_action =
|
||
|
interpret_normal_arg(current_arg, current_value);
|
||
|
switch (normal_action) {
|
||
|
case ARG_ACTION_NONE:
|
||
|
break;
|
||
|
case ARG_ACTION_CONTINUE:
|
||
|
continue;
|
||
|
case ARG_ACTION_STOP:
|
||
|
return;
|
||
|
case ARG_ACTION_SKIP:
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void print_flag_error(char arg) {
|
||
|
if (isprint(arg)) {
|
||
|
qtb_log("error: unknown option: '%c'", arg);
|
||
|
} else {
|
||
|
qtb_log("error: unknown option: '0x%X'", arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void print_arg_error(const char *arg) {
|
||
|
qtb_log("error: unknown argument: '%s'", arg);
|
||
|
}
|
||
|
|
||
|
static void print_help_and_exit() {
|
||
|
printf("quick-text-bar - a fast and customizable text based bar\n");
|
||
|
#if HAS_X11
|
||
|
printf("usage: %s [-h] [-v] [-i] [-n MODULES] [-d DISPLAY] [-r] [-s] \n",
|
||
|
global_exec_name);
|
||
|
#else
|
||
|
printf("usage: %s [-h] [-v] [-i] [-n MODULES] [-s] \n", global_exec_name);
|
||
|
#endif
|
||
|
printf(" -h show this message, then exit\n");
|
||
|
printf(" -v print version, then exit\n");
|
||
|
printf(" -i print version and module informaton, then exit\n");
|
||
|
printf(" -n semi-colon seperated list of modules to disable\n");
|
||
|
#ifdef HAS_X11
|
||
|
printf(" -d the X display to use when setting the root window name\n");
|
||
|
if (DEFAULT_SET_ROOT_TITLE) {
|
||
|
printf(" -r set the X root window name when updating the bar "
|
||
|
"(default)\n");
|
||
|
printf(" -s write to stdout when updating the bar\n");
|
||
|
} else {
|
||
|
printf(" -r set the X root window name when updating the bar\n");
|
||
|
printf(" -s write to stdout when updating the bar (default)\n");
|
||
|
}
|
||
|
#else
|
||
|
printf(" -d the X display to use when setting the root window name "
|
||
|
"(disabled)\n");
|
||
|
printf(" -r set the X root window name when updating the bar "
|
||
|
"(disabled)\n");
|
||
|
printf(" -s write to stdout when updating the bar\n");
|
||
|
#endif
|
||
|
qtb_exit();
|
||
|
}
|
||
|
|
||
|
static void print_version_and_exit() {
|
||
|
printf("quick-text-bar v%s\n", QTB_VERSION);
|
||
|
qtb_exit();
|
||
|
}
|
||
|
|
||
|
static void print_info_and_exit(void) {
|
||
|
printf("quick-text-bar v%s\n", QTB_VERSION);
|
||
|
#ifdef HAS_X11
|
||
|
printf("X11 Support: Present\n");
|
||
|
#else
|
||
|
printf("X11 Support: Absent\n");
|
||
|
#endif
|
||
|
printf("Modules: %zu\n", global_module_count);
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
printf(" %s\n", modules[i].name);
|
||
|
}
|
||
|
qtb_exit();
|
||
|
}
|
||
|
|
||
|
static unsigned long find_gcf(unsigned long *nums, size_t len) {
|
||
|
if (len == 0) { /* gcf(0) = 0 */
|
||
|
return 0;
|
||
|
} else if (len == 1) { /* gcf(n) = n */
|
||
|
return nums[0];
|
||
|
}
|
||
|
/* calculate all prime factors */
|
||
|
unsigned long **prime_factors = NULL;
|
||
|
size_t prime_factors_length = 0;
|
||
|
size_t i;
|
||
|
for (i = 0; i < len; ++i) {
|
||
|
unsigned long *i_prime_factors = find_prime_factors(nums[i], NULL);
|
||
|
prime_factors = qtb_realloc(prime_factors, sizeof(unsigned long *) *
|
||
|
++prime_factors_length);
|
||
|
prime_factors[prime_factors_length - 1] = i_prime_factors;
|
||
|
}
|
||
|
/* find which calculated prime factors are common to all inputs */
|
||
|
unsigned long *common_factors = NULL;
|
||
|
size_t common_factors_length = 0;
|
||
|
unsigned long *j;
|
||
|
/* loop though each factor in prime_factors[0] */
|
||
|
for (j = prime_factors[0]; *j != 0; ++j) {
|
||
|
/* loop though every set of factors except prime_factors[0] */
|
||
|
/* size_t i; */
|
||
|
for (i = 1; i < prime_factors_length; ++i) {
|
||
|
unsigned long *i_prime_factors;
|
||
|
for (i_prime_factors = prime_factors[i]; *i_prime_factors != 0;
|
||
|
++i_prime_factors) {
|
||
|
if (*i_prime_factors == *j) {
|
||
|
*i_prime_factors = 4; /* remove the factor (by setting it to
|
||
|
a composite number) */
|
||
|
goto found_factor;
|
||
|
}
|
||
|
}
|
||
|
/* the factor was not found, therefore we can't add it */
|
||
|
goto dont_add;
|
||
|
/* we found the factor, check the next set of factors */
|
||
|
found_factor:
|
||
|
continue;
|
||
|
}
|
||
|
/* the factor was a common factor, add it */
|
||
|
common_factors = qtb_realloc(
|
||
|
common_factors, sizeof(unsigned long) * ++common_factors_length);
|
||
|
common_factors[common_factors_length - 1] = *j;
|
||
|
/* the factor was not a common factor */
|
||
|
dont_add:
|
||
|
continue;
|
||
|
}
|
||
|
/* qtb_free sets of factors */
|
||
|
/* size_t i; */
|
||
|
for (i = 0; i < prime_factors_length; ++i) {
|
||
|
qtb_free(prime_factors[i]);
|
||
|
}
|
||
|
qtb_free(prime_factors);
|
||
|
/* finally, calculate the gcf */
|
||
|
unsigned long gcf = 1;
|
||
|
/* size_t i; */
|
||
|
for (i = 0; i < common_factors_length; ++i) {
|
||
|
gcf *= common_factors[i];
|
||
|
}
|
||
|
qtb_free(common_factors);
|
||
|
return gcf;
|
||
|
}
|
||
|
|
||
|
/* return value will be 0 terminated. if out_len is not NULL,
|
||
|
it's value will be set to the number of primes found (length of return value
|
||
|
- 1) */
|
||
|
static unsigned long *find_prime_factors(unsigned long n, size_t *out_len) {
|
||
|
unsigned long *out_arr = NULL;
|
||
|
size_t arr_len = 0;
|
||
|
while (n % 2 == 0) {
|
||
|
out_arr = qtb_realloc(out_arr, sizeof(unsigned long) * ++arr_len);
|
||
|
out_arr[arr_len - 1] = 2;
|
||
|
n /= 2;
|
||
|
}
|
||
|
unsigned long i;
|
||
|
for (i = 3; i <= sqrtl(n); i += 2) {
|
||
|
while (n % i == 0) {
|
||
|
out_arr =
|
||
|
qtb_realloc(out_arr, sizeof(unsigned long) * ++arr_len);
|
||
|
out_arr[arr_len - 1] = i;
|
||
|
n /= i;
|
||
|
}
|
||
|
}
|
||
|
if (n > 2) {
|
||
|
out_arr = qtb_realloc(out_arr, sizeof(unsigned long) * ++arr_len);
|
||
|
out_arr[arr_len - 1] = n;
|
||
|
}
|
||
|
out_arr = qtb_realloc(out_arr, sizeof(unsigned long) * arr_len);
|
||
|
out_arr[arr_len] = 0;
|
||
|
if (out_len) {
|
||
|
*out_len = arr_len;
|
||
|
}
|
||
|
return out_arr;
|
||
|
}
|
||
|
|
||
|
static void initialize_modules() {
|
||
|
int has_error = FALSE;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (modules[i].disabled) {
|
||
|
continue;
|
||
|
}
|
||
|
if (modules[i].signal + SIGRTMIN > SIGRTMAX) {
|
||
|
has_error = TRUE;
|
||
|
qtb_log("%s: error: signal out of range: %d\n",
|
||
|
modules[i].signal + SIGRTMIN);
|
||
|
continue;
|
||
|
}
|
||
|
if (modules[i].init) {
|
||
|
modules[i].init();
|
||
|
}
|
||
|
modules[i].last_text = NULL;
|
||
|
modules[i].last_refresh = 0;
|
||
|
}
|
||
|
if (has_error) {
|
||
|
qtb_log("error: some modules were not initialized, exiting...");
|
||
|
qtb_exit_with_status(ERROR_MODULE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void set_refresh_time() {
|
||
|
#ifdef REFRESH_TIME
|
||
|
unsigned long refresh_ms = REFRESH_TIME;
|
||
|
#else
|
||
|
unsigned long refresh_ms = find_module_refresh_gcf();
|
||
|
if (refresh_ms == 0) { /* if all modules are triggered by signals */
|
||
|
refresh_ms = HOURS(1); /* refresh once an hour */
|
||
|
}
|
||
|
#endif
|
||
|
miliseconds_to_timespec(refresh_ms, &global_refresh_time);
|
||
|
}
|
||
|
|
||
|
static void signal_handler(int signum) {
|
||
|
struct timespec current_time;
|
||
|
clock_gettime(global_cpu_clock_id, ¤t_time);
|
||
|
int module_updated = FALSE;
|
||
|
unsigned long current_time_ms = timespec_to_miliseconds(¤t_time);
|
||
|
int adjusted_signum = signum - SIGRTMIN;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (!modules[i].disabled && modules[i].signal == adjusted_signum) {
|
||
|
refresh_module(&modules[i]);
|
||
|
modules[i].last_refresh = current_time_ms;
|
||
|
module_updated = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (module_updated) {
|
||
|
rewrite_bar();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void setup_signals() {
|
||
|
sigemptyset(&global_real_time_mask);
|
||
|
int i;
|
||
|
for (i = SIGRTMIN; i <= SIGRTMAX; ++i) {
|
||
|
sigaddset(&global_real_time_mask, i);
|
||
|
struct sigaction action;
|
||
|
sigemptyset(&action.sa_mask);
|
||
|
action.sa_flags = 0;
|
||
|
action.sa_handler = &signal_handler;
|
||
|
if (sigaction(i, &action, NULL) != 0) {
|
||
|
qtb_log("error: could not setup handler for signal: %d", i);
|
||
|
qtb_exit_with_status(ERROR_UNKNOWN);
|
||
|
}
|
||
|
}
|
||
|
sigprocmask(SIG_BLOCK, &global_real_time_mask, NULL);
|
||
|
}
|
||
|
|
||
|
static unsigned long find_module_refresh_gcf() {
|
||
|
size_t refresh_times_length = 0;
|
||
|
unsigned long *refresh_times = NULL;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (!modules[i].disabled && modules[i].refresh_time != NO_REFRESH) {
|
||
|
refresh_times = qtb_realloc(
|
||
|
refresh_times, sizeof(unsigned long) * ++refresh_times_length);
|
||
|
refresh_times[refresh_times_length - 1] = modules[i].refresh_time;
|
||
|
}
|
||
|
}
|
||
|
unsigned long gcf = find_gcf(refresh_times, refresh_times_length);
|
||
|
qtb_free(refresh_times);
|
||
|
return gcf;
|
||
|
}
|
||
|
|
||
|
static unsigned long timespec_to_miliseconds(struct timespec *ts) {
|
||
|
return (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
|
||
|
}
|
||
|
|
||
|
static void miliseconds_to_timespec(unsigned long ms, struct timespec *out_ts) {
|
||
|
out_ts->tv_sec = floorl(ms / 1000);
|
||
|
out_ts->tv_nsec = (ms % 1000) * 1000000;
|
||
|
}
|
||
|
|
||
|
static void do_initial_refresh() {
|
||
|
int has_normal_module = FALSE;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (modules[i].disabled) {
|
||
|
continue;
|
||
|
}
|
||
|
if (modules[i].refresh_time != NO_REFRESH) {
|
||
|
has_normal_module = TRUE;
|
||
|
}
|
||
|
if (modules[i].poll) {
|
||
|
refresh_module(&modules[i]);
|
||
|
}
|
||
|
}
|
||
|
if (!has_normal_module) {
|
||
|
rewrite_bar();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void refresh_module(struct BarModule *module) {
|
||
|
sigprocmask(SIG_BLOCK, &global_real_time_mask, NULL);
|
||
|
if (module->last_text) {
|
||
|
qtb_free(module->last_text);
|
||
|
module->last_text = NULL;
|
||
|
}
|
||
|
module->last_text = module->poll();
|
||
|
if (module->last_text) {
|
||
|
module->last_text_length = strlen(module->last_text);
|
||
|
} else {
|
||
|
module->last_text_length = 0;
|
||
|
}
|
||
|
sigprocmask(SIG_UNBLOCK, &global_real_time_mask, NULL);
|
||
|
}
|
||
|
|
||
|
/* return TRUE if the module was refreshed, FALSE otherwise */
|
||
|
static int check_and_refresh_module(struct BarModule *module,
|
||
|
unsigned long current_time) {
|
||
|
if (module->disabled || module->refresh_time == NO_REFRESH) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
unsigned long module_last_refresh =
|
||
|
module->last_refresh + module->refresh_time;
|
||
|
if (module_last_refresh >= global_last_refresh && module->poll) {
|
||
|
refresh_module(module);
|
||
|
module->last_refresh = current_time;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void module_poll_loop() {
|
||
|
while (TRUE) {
|
||
|
struct timespec current_time;
|
||
|
clock_gettime(global_cpu_clock_id, ¤t_time);
|
||
|
unsigned long current_time_ms = timespec_to_miliseconds(¤t_time);
|
||
|
int module_refreshed = FALSE;
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count; ++i) {
|
||
|
if (check_and_refresh_module(&modules[i], current_time_ms)) {
|
||
|
module_refreshed = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (module_refreshed) {
|
||
|
rewrite_bar();
|
||
|
}
|
||
|
/* calculate how long it took us to run this iteration
|
||
|
and subtract it from the refresh time */
|
||
|
struct timespec end_time;
|
||
|
clock_gettime(global_cpu_clock_id, &end_time);
|
||
|
struct timespec span_time;
|
||
|
timespec_diff(¤t_time, &end_time, &span_time);
|
||
|
struct timespec final_sleep_time;
|
||
|
timespec_diff(&span_time, &global_refresh_time, &final_sleep_time);
|
||
|
nanosleep(&final_sleep_time, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void rewrite_bar() {
|
||
|
/* start with size of prefix, suffix, and null byte */
|
||
|
size_t bar_length = global_prefix_length + global_suffix_length + 1;
|
||
|
char *bar_text = qtb_malloc(bar_length);
|
||
|
bar_text[0] = '\0';
|
||
|
strcat(bar_text, PREFIX_TEXT);
|
||
|
size_t i;
|
||
|
for (i = 0; i < global_module_count - 1; ++i) {
|
||
|
if (modules[i].last_text) {
|
||
|
bar_text = qtb_realloc(
|
||
|
bar_text, (bar_length += modules[i].last_text_length +
|
||
|
global_seperator_length));
|
||
|
strcat(bar_text, modules[i].last_text);
|
||
|
strcat(bar_text, SEPERATOR_TEXT);
|
||
|
}
|
||
|
}
|
||
|
struct BarModule *last_module = &modules[global_module_count - 1];
|
||
|
if (last_module->last_text) {
|
||
|
bar_text = qtb_realloc(
|
||
|
bar_text, (bar_length += last_module->last_text_length));
|
||
|
strcat(bar_text, last_module->last_text);
|
||
|
}
|
||
|
strcat(bar_text, SUFFIX_TEXT);
|
||
|
if (flag_set_root_title) {
|
||
|
set_x_root_title(bar_text);
|
||
|
} else {
|
||
|
printf("%s\n", bar_text);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
qtb_free(bar_text);
|
||
|
}
|
||
|
|
||
|
/* subtract start from end */
|
||
|
static void timespec_diff(const struct timespec *start,
|
||
|
const struct timespec *end, struct timespec *out) {
|
||
|
out->tv_nsec = end->tv_nsec - start->tv_nsec;
|
||
|
out->tv_sec = end->tv_sec - start->tv_sec;
|
||
|
if (out->tv_sec < 0) { /* start > end */
|
||
|
out->tv_sec = 0;
|
||
|
out->tv_nsec = 0;
|
||
|
} else if (out->tv_nsec < 0) {
|
||
|
out->tv_nsec += 1000000000l;
|
||
|
--out->tv_sec;
|
||
|
if (out->tv_sec < 0) {
|
||
|
out->tv_sec = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void init_x() {
|
||
|
#ifdef HAS_X11
|
||
|
global_x_display = XOpenDisplay(flag_x_display_name);
|
||
|
if (!global_x_display) {
|
||
|
const char *display_name;
|
||
|
if (flag_x_display_name) {
|
||
|
display_name = flag_x_display_name;
|
||
|
} else {
|
||
|
display_name = XDisplayName(NULL);
|
||
|
}
|
||
|
qtb_log("error: failed to open X display: '%s'", display_name);
|
||
|
if (flag_set_root_title) {
|
||
|
qtb_exit_with_status(ERROR_X11);
|
||
|
}
|
||
|
} else {
|
||
|
global_x_screen = XDefaultScreen(global_x_display);
|
||
|
global_x_root = XRootWindow(global_x_display, global_x_screen);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void set_x_root_title(const char *title) {
|
||
|
#ifdef HAS_X11
|
||
|
if (global_x_display) {
|
||
|
XStoreName(global_x_display, global_x_root, title);
|
||
|
XFlush(global_x_display);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* module api definitions */
|
||
|
void _qtb_internal_log(const char *fmt, ...) {
|
||
|
fprintf(stderr, "%s: ", global_exec_name);
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
vfprintf(stderr, fmt, args);
|
||
|
va_end(args);
|
||
|
fwrite("\n", 1, 1, stderr);
|
||
|
}
|
||
|
|
||
|
void qtb_die() {
|
||
|
qtb_exit_with_status(ERROR_MODULE);
|
||
|
}
|
||
|
|
||
|
void qtb_signal_modules(int sig) {
|
||
|
pthread_kill(global_main_thread, SIGRTMIN + sig);
|
||
|
}
|
||
|
|
||
|
void *qtb_malloc(size_t size) {
|
||
|
return qtb_realloc(NULL, size);
|
||
|
}
|
||
|
|
||
|
static int is_overflow(size_t n1, size_t n2) {
|
||
|
if (n1 <= 1 || n2 <= 1) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
return (n1 * n2) / n1 != n2;
|
||
|
}
|
||
|
|
||
|
void *qtb_calloc(size_t n, size_t size) {
|
||
|
if (is_overflow(n, size)) {
|
||
|
qtb_log(
|
||
|
"attempt to allocate %zu bytes %zu times failed due to overflow", n,
|
||
|
size);
|
||
|
qtb_exit_with_status(ERROR_MEMORY);
|
||
|
}
|
||
|
size_t total_size = n * size;
|
||
|
void *ptr = qtb_malloc(total_size);
|
||
|
memset(ptr, 0, total_size);
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
void *qtb_realloc(void *ptr, size_t size) {
|
||
|
void *new_ptr = realloc(ptr, size);
|
||
|
if (!new_ptr) {
|
||
|
qtb_log("error: out of memory");
|
||
|
qtb_exit_with_status(ERROR_MEMORY);
|
||
|
}
|
||
|
return new_ptr;
|
||
|
}
|
||
|
|
||
|
char *qtb_strdup(const char *str) {
|
||
|
size_t len = strlen(str);
|
||
|
char *new_str = qtb_malloc(len + 1);
|
||
|
memcpy(new_str, str, len + 1);
|
||
|
return new_str;
|
||
|
}
|
||
|
|
||
|
void qtb_free(void *ptr) {
|
||
|
if (ptr) {
|
||
|
free(ptr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef HAS_X11
|
||
|
Display *qtb_get_x_display(void) {
|
||
|
return global_x_display;
|
||
|
}
|
||
|
#endif
|