From 5ba46284feff31b82e401d687eba65cb6756831f Mon Sep 17 00:00:00 2001 From: Squibid Date: Mon, 1 Sep 2025 22:18:12 -0400 Subject: [PATCH] make the timer fancy --- include/util.h | 1 + meson.build | 4 + src/main.c | 12 +-- src/subcmds/clock.c | 251 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 208 insertions(+), 60 deletions(-) create mode 100644 include/util.h diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..8543c6b --- /dev/null +++ b/include/util.h @@ -0,0 +1 @@ +#define numlen(x) (x > 0 ? floor(log10(abs(x))) + 1 : 0) diff --git a/meson.build b/meson.build index e9ce887..eebb62b 100644 --- a/meson.build +++ b/meson.build @@ -27,10 +27,14 @@ srcfiles = files( 'src/conf.c' ) + luafiles + subcmds +cc = meson.get_compiler('c') +math_dep = cc.find_library('m', required: true) + # build the executable executable('wom', srcfiles, dependencies: [ dependency('lua', version: '>=5.1 <6.0'), + math_dep, ], include_directories: [ include_directories('include'), diff --git a/src/main.c b/src/main.c index 9560852..1ba379a 100644 --- a/src/main.c +++ b/src/main.c @@ -69,8 +69,7 @@ main(int argc, char *argv[]) int l; lua_State *L; const char *cag_path; - char *config_path; - bool show_help = false; + char *config_path = { 0 }; cag_option_context context; /* general options for womblic */ @@ -87,8 +86,7 @@ main(int argc, char *argv[]) case 'h': printf("Usage: wom [OPTION]...\n"); cag_option_print(options, CAG_ARRAY_SIZE(options), stdout); - show_help = true; - break; + exit(0); case '?': cag_option_print_error(&context, stdout); break; @@ -117,12 +115,6 @@ main(int argc, char *argv[]) lua_pop(L, lua_gettop(L)); } - /* show help info about which subcmds are available */ - if (show_help) { - subcmds_subcmd(NULL, 0, NULL); - exit(0); - } - run_subcmds(argc, argv); lua_close(L); diff --git a/src/subcmds/clock.c b/src/subcmds/clock.c index 02a5f57..8c8ac66 100644 --- a/src/subcmds/clock.c +++ b/src/subcmds/clock.c @@ -1,10 +1,92 @@ +#include +#include +#include #include #include #include +#include +#include #include +#include "util.h" #include "subcmds.h" +#define S_AS_MSEC 1000000 +#define PB_LEN 30 +#define PB_FULL "┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃" + +#define set_str_len(u, us, hu) str_len += (u > 0) ? (numlen(u) + 1 + (u > 1 ? strlen(us"s") : strlen(us)) + ((hu > 0) ? 1 : 0)) : 0 +#define write_str(u, us) do { \ + if (u > 0) { \ + if (str[0] != '\0') { \ + strcat(str, " "); \ + } \ + size_t sz = numlen(u) + (u > 1 ? strlen(us"s") : strlen(us)) + 2; \ + tmp = calloc(sz, sizeof(char)); \ + snprintf(tmp, sz, "%d %s", u, u > 1 ? us"s" : us); \ + strcat(str, tmp); \ + free(tmp); \ + } \ +} while (0) + +int i, total_delay_s; + +/** + * @brief convert a time to a string in seconds ie: + * 12 -> 12 seconds + * + * 720 -> 12 minutes + * + * 43,200 -> 12 hours + * + * 1,036,800 -> 12 days + * + * @param sec input time + * @return time + */ +static char +*sec2str(int sec) +{ + int days, hours, minutes, seconds, str_len; + char *str, *tmp; + + if (sec < 0) { + return 0; + } + + for (str_len = days = hours = minutes = seconds = 0; sec > 0;) { + if (sec >= 60 * 60 * 24) { + sec -= 60 * 60 * 24; + days++; + } else if (sec >= 60 * 60) { + sec -= 60 * 60; + hours++; + } else if (sec >= 60) { + sec -= 60; + minutes++; + } else if (sec >= 1) { + sec -= 1; + seconds++; + } + } + + set_str_len(days, "day", 0); + set_str_len(hours, "hour", days); + set_str_len(minutes, "minute", hours); + set_str_len(seconds, "second", minutes); + str_len += 1; + + str = malloc(str_len * sizeof(char)); + str[0] = '\0'; + + write_str(days, "day"); + write_str(hours, "hour"); + write_str(minutes, "minute"); + write_str(seconds, "second"); + + return str; +} + /** * @brief convert a sting to a time in seconds ie: * 12s -> 12 seconds @@ -19,75 +101,144 @@ * @return time in seconds */ static int -string2time(char *str) +str2sec(char *str) { - char *d; - int i; - double time; + char *time_buffer; + int i, si, ei, ste, temp_total, total; - /* find the length of the string */ - for (i = 0; str[i] != '\0'; i++) - ; + for (total = si = ei = i = 0; i < strlen(str); i++, temp_total = 0) { + /** + * if the char is not a number we need to take all the previous numbers + * and attempt to convert this to a proper time + */ + if (str[i] < '0' || str[i] > '9') { + ei = i - 1; + ste = ei - si + 1; + time_buffer = malloc((ste * sizeof(char)) + 1); + memcpy(time_buffer, str + si, ste); + time_buffer[ste] = '\0'; + temp_total += strtol(time_buffer, NULL, 10); + free(time_buffer); - /* if there is a trailing character make sure to remove it */ - if (str[i] < '0' || str[i] > '9') { - i--; - } + switch (str[i]) { + default: case 's': break; + case 'm': temp_total *= 60; break; + case 'h': temp_total *= 60 * 60; break; + case 'd': temp_total *= 60 * 60 * 24; break; + } - /* allocate space for numerical string */ - d = (char *)calloc(i, sizeof(char)); - strncpy(d, str, i); + total += temp_total; + si = i + 1; + } + } - /* atoi the numerical string into an int */ - time = atof(d); - free(d); - - /* modify the time using the last trailing character */ - switch (str[i]) { - case 's': break; - case 'm': time *= 60; break; - case 'h': time *= 60 * 60; break; - case 'd': time *= 60 * 60 * 24; break; - default: return -1; break; - } - - return time; + return total; } +static void +timer_exit(void) +{ + char *time_str; + time_t t; + struct tm tm; + + printf("\033[?25h"); + + time_str = sec2str(total_delay_s - i); + t = time(NULL); + tm = *localtime(&t); + printf( + "\033[3A\033[43;30;1m wom timer \033[0m %s elapsed at %d/%02d/%02d %02d:%02d:%02d\n", + time_str, + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec + ); + free(time_str); +} + +static void +timer_sig_exit(int sig) +{ + puts("\n"); + timer_exit(); + exit(sig); +} void timer_subcmd(void *, int argc, char *argv[]) { - int i, l, sum; - char *s; + bool first; + int j, slp, sec, rollover, wi, tick, sub; + char *pb_elapsed, *pb_left, *time_str; - for (i = 1, sum = 0, l = 0; i < argc; i++) { - sum += string2time(argv[i]); - l += strlen(argv[i]); + for (i = 1, total_delay_s = 0; i < argc; i++) { + total_delay_s += str2sec(argv[i]); } - /* allocate and populate a string with the length of the timer */ - s = (char *)calloc(l + i - 1, sizeof(char)); - for (i = 1; i < argc; i++) { - /* TODO: reorder based on magnitude */ - s = strcat(s, argv[i]); - if (i != argc - 1) { - s = strcat(s, " "); - } - } - - if (sum <= 0) { + if (total_delay_s <= 0) { exit(1); } - /* print out the number of time */ - printf("Waiting for %s...\n", s); + /* create the progress bar */ + pb_left = calloc(strlen(PB_FULL), sizeof(char) + 1); + strcpy(pb_left, PB_FULL); + pb_elapsed = calloc(strlen(PB_FULL), sizeof(char) + 1); - /* TODO: instead of sleeping implement polling that way if need be we can have - * a countdown */ - sleep(sum); + /* render ui */ + printf("\033[?25l"); + atexit(timer_exit); + signal(SIGINT, timer_sig_exit); + puts(""); + rollover = 0; + first = true; + tick = (total_delay_s / (float)PB_LEN) * S_AS_MSEC; - puts("returned"); + for (sub = j = 0, i = total_delay_s; i > 0; i--) { + /* sub second handler */ + for (sec = S_AS_MSEC; sec > 0;) { + if (!first) { + printf("\033[2A"); + } + first = false; + + if (rollover > 0) { + sub = rollover; + rollover = 0; + } else { + sub = tick; + } + if (sec - sub < 0) { + slp = sec; + rollover = fabs((float)sec - sub); + sec = 0; + } else { + sec -= sub; + slp = sub; + } + + printf("\033[2K\033[43;30;1m wom timer \033[0m \033[33m%s\033[30m%s\033[0m\n", pb_left, pb_elapsed); + time_str = sec2str(i); + printf("\033[2K\033[90m %s\033[0m\n", time_str); + free(time_str); + + /* change the progress bar */ + if (rollover == 0) { + wi = strlen(PB_FULL) - (j * strlen("┃")) - strlen("┃"); + strncat(pb_elapsed, "┃", strlen(PB_FULL)); + pb_left[wi] = '\0'; + j++; + } + + usleep(slp); + } + } + + free(pb_left); + free(pb_elapsed); return; }