772 lines
23 KiB
C
772 lines
23 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 __OpenBSD__
|
|
#define SIG_MIN SIGUSR1
|
|
#define SIG_MAX 31
|
|
#else
|
|
#define SIG_MIN SIGRTMIN
|
|
#define SIG_MAX SIGRTMAX
|
|
#endif
|
|
|
|
#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 void initialize_modules(void);
|
|
static unsigned long gcd(unsigned long n1, unsigned long n2);
|
|
static unsigned long find_module_refresh_time(void);
|
|
static void set_refresh_time(void);
|
|
static void signal_handler(int signum);
|
|
static void setup_signals(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 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 + SIG_MIN > SIG_MAX) {
|
|
has_error = TRUE;
|
|
qtb_log("%s: error: signal out of range: %d\n",
|
|
modules[i].signal + SIG_MIN);
|
|
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);
|
|
}
|
|
}
|
|
|
|
unsigned long gcd(unsigned long n1, unsigned long n2) {
|
|
if (n1 == 0) {
|
|
return n2;
|
|
}
|
|
return gcd(n2 % n1, n1);
|
|
}
|
|
|
|
unsigned long find_module_refresh_time() {
|
|
size_t i;
|
|
size_t ignore_index;
|
|
size_t result;
|
|
for (i = 0; i < global_module_count; ++i) {
|
|
if (!modules[i].disabled && modules[i].refresh_time != NO_REFRESH) {
|
|
result = modules[i].refresh_time;
|
|
ignore_index = i;
|
|
}
|
|
}
|
|
for (i = 0; i < global_module_count; ++i) {
|
|
if (i == ignore_index) {
|
|
continue;
|
|
}
|
|
if (!modules[i].disabled && modules[i].refresh_time != NO_REFRESH) {
|
|
result = gcd(result, modules[i].refresh_time);
|
|
}
|
|
if (result == 1) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void set_refresh_time() {
|
|
#ifdef REFRESH_TIME
|
|
unsigned long refresh_ms = REFRESH_TIME;
|
|
#else
|
|
unsigned long refresh_ms = find_module_refresh_time();
|
|
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 - SIG_MIN;
|
|
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 = SIG_MIN; i <= SIG_MAX; ++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 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) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
sigprocmask(SIG_BLOCK, &global_real_time_mask, NULL);
|
|
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);
|
|
sigprocmask(SIG_UNBLOCK, &global_real_time_mask, NULL);
|
|
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, SIG_MIN + 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 (size != 0 && !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
|