From b653fdcc571dfed5f04aa2c3fc042b07716f349b Mon Sep 17 00:00:00 2001 From: rmntgx Date: Sun, 29 Jun 2025 23:32:04 +0500 Subject: [PATCH 1/3] feat: buggy resize implementation --- src/ui.c | 78 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/src/ui.c b/src/ui.c index 85ab74c..49c870e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -46,6 +46,7 @@ static void print_session(struct uint_point origin, struct session session, static void print_user(struct uint_point origin, struct user user, bool multiple); static void print_passwd(struct uint_point origin, uint length, bool err); +static void print_ui(); // ansi resource: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 static struct termios orig_term; @@ -54,6 +55,23 @@ static struct winsize window; #define INNER_BOX_OUT_MARGIN 2 struct config* g_config = NULL; + +void resize_handler(int) { + ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); + + if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || + window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { + printf("\033[2J\033[H"); // Clear screen + printf("\x1b[1;31mScreen too small\x1b[0m\n"); + printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg); + return; + } + + printf("\033[2J\033[H"); // Clear screen + + print_ui(); +} + void setup(struct config* config) { g_config = config; ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); @@ -81,6 +99,7 @@ void setup(struct config* config) { print_footer(); (void)atexit(restore_all); (void)signal(SIGINT, signal_handler); + (void)signal(SIGWINCH, resize_handler); } static struct uint_point box_start() { @@ -173,12 +192,7 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) { } static char* unknown_str = "unknown"; -// NOLINTNEXTLINE(readability-function-cognitive-complexity) -int load(struct Vector* users, struct Vector* sessions) { - /// SETUP - gusers = users; - gsessions = sessions; - +void print_ui() { // hostnames larger won't render properly const u_char HOSTNAME_SIZE = VALUES_COL - VALUES_SEPR - BOX_HMARGIN; char hostname_buf[HOSTNAME_SIZE]; @@ -194,6 +208,35 @@ int load(struct Vector* users, struct Vector* sessions) { // 1]); // *hidx = '\0'; } + /// PRINTING + const struct uint_point BOXSTART = box_start(); + + // printf box + print_box(); + + // put hostname + printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", BOXSTART.y + HEAD_ROW, + BOXSTART.x + VALUES_COL - VALUES_SEPR - (uint)utf8len(hostname), + g_config->colors.e_hostname, hostname, g_config->colors.fg); + + // put date + char* fmtd_time = fmt_time(g_config->behavior.timefmt); + printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", BOXSTART.y + HEAD_ROW, + BOXSTART.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time), + g_config->colors.e_date, fmtd_time, g_config->colors.fg); + free(fmtd_time); + + ui_update_field(SESSION); + ui_update_field(USER); + ui_update_field(PASSWD); + ui_update_cursor_focus(); +} + +// NOLINTNEXTLINE(readability-function-cognitive-complexity) +int load(struct Vector* users, struct Vector* sessions) { + /// SETUP + gusers = users; + gsessions = sessions; of_session = ofield_new(sessions->length + g_config->behavior.include_defshell); @@ -228,28 +271,7 @@ int load(struct Vector* users, struct Vector* sessions) { free(initial_state.session_opt); } - /// PRINTING - const struct uint_point BOXSTART = box_start(); - - // printf box - print_box(); - - // put hostname - printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", BOXSTART.y + HEAD_ROW, - BOXSTART.x + VALUES_COL - VALUES_SEPR - (uint)utf8len(hostname), - g_config->colors.e_hostname, hostname, g_config->colors.fg); - - // put date - char* fmtd_time = fmt_time(g_config->behavior.timefmt); - printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", BOXSTART.y + HEAD_ROW, - BOXSTART.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time), - g_config->colors.e_date, fmtd_time, g_config->colors.fg); - free(fmtd_time); - - ui_update_field(SESSION); - ui_update_field(USER); - ui_update_field(PASSWD); - ui_update_cursor_focus(); + print_ui(); /// INTERACTIVE u_char len; From 68ed11fb2dba639ddbfab3a6b166b386bae3234f Mon Sep 17 00:00:00 2001 From: rmntgx Date: Wed, 2 Jul 2025 13:50:17 +0500 Subject: [PATCH 2/3] feat: cool resize implementation --- src/ui.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ui.c b/src/ui.c index 49c870e..e3bb949 100644 --- a/src/ui.c +++ b/src/ui.c @@ -56,7 +56,13 @@ static struct winsize window; #define INNER_BOX_OUT_MARGIN 2 struct config* g_config = NULL; -void resize_handler(int) { +static volatile sig_atomic_t need_resize = 0; + +static void process_sigwinch(int) { + need_resize = 1; +} + +void resize_ui() { ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || @@ -96,10 +102,9 @@ void setup(struct config* config) { printf("\x1b[s\x1b[?47h\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg); - print_footer(); (void)atexit(restore_all); (void)signal(SIGINT, signal_handler); - (void)signal(SIGWINCH, resize_handler); + (void)signal(SIGWINCH, process_sigwinch); } static struct uint_point box_start() { @@ -226,6 +231,8 @@ void print_ui() { g_config->colors.e_date, fmtd_time, g_config->colors.fg); free(fmtd_time); + print_footer(); + ui_update_field(SESSION); ui_update_field(USER); ui_update_field(PASSWD); @@ -278,6 +285,25 @@ int load(struct Vector* users, struct Vector* sessions) { char seq[0xff]; uint esc = 0; while (true) { + if (need_resize) { + need_resize = 0; + resize_ui(); + } + + // Async press handling (magic) + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; // timeout = 100ms + // Wait for input with timeout + int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) continue; // Interrupted by signal + } + if (ret == 0) continue; // Timeout + read_press(&len, seq); if (*seq == '\x1b') { enum keys ansi_code = find_ansi(seq); From 9f905622a2678d65056d0a525934cae8b54ede6b Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 01:41:56 +0200 Subject: [PATCH 3/3] chore: dry and modularize a bit --- include/util.h | 3 +++ src/ui.c | 69 +++++++++++++++++--------------------------------- src/util.c | 13 ++++++++++ 3 files changed, 39 insertions(+), 46 deletions(-) diff --git a/include/util.h b/include/util.h index 126533c..9dce5fd 100644 --- a/include/util.h +++ b/include/util.h @@ -1,6 +1,7 @@ #ifndef UTILH_ #define UTILH_ +#include #include #include #include @@ -12,6 +13,8 @@ int find_keyname(enum keys* at, const char* name); enum keys find_ansi(const char* seq); void read_press(u_char* length, char* out); +// non blocking, waits up to tv, returns true if actually read +bool read_press_nb(u_char* length, char* out, struct timeval* tv); bool utf8_iscont(char byte); size_t utf8len(const char* str); diff --git a/src/ui.c b/src/ui.c index e3bb949..0d6a271 100644 --- a/src/ui.c +++ b/src/ui.c @@ -46,7 +46,7 @@ static void print_session(struct uint_point origin, struct session session, static void print_user(struct uint_point origin, struct user user, bool multiple); static void print_passwd(struct uint_point origin, uint length, bool err); -static void print_ui(); +static void scratch_print_ui(); // ansi resource: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 static struct termios orig_term; @@ -58,38 +58,12 @@ struct config* g_config = NULL; static volatile sig_atomic_t need_resize = 0; -static void process_sigwinch(int) { +static void process_sigwinch(int signal) { need_resize = 1; } -void resize_ui() { - ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); - - if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || - window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { - printf("\033[2J\033[H"); // Clear screen - printf("\x1b[1;31mScreen too small\x1b[0m\n"); - printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg); - return; - } - - printf("\033[2J\033[H"); // Clear screen - - print_ui(); -} - void setup(struct config* config) { g_config = config; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); - - // at least - // 2 padding top and bottom for footer and vertical compensation - // 2 padding left & right to not overflow footer width - if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || - window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { - (void)fprintf(stderr, "\x1b[1;31mScreen too small\x1b[0m\n"); - exit(1); - } tcgetattr(STDOUT_FILENO, &orig_term); term = orig_term; // save term @@ -197,7 +171,19 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) { } static char* unknown_str = "unknown"; -void print_ui() { +void scratch_print_ui() { + ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); + + if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || + window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { + printf("\033[2J\033[H"); // Clear screen + printf("\x1b[1;31mScreen too small\x1b[0m\n"); + printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg); + return; + } + + printf("\033[2J\033[H"); // Clear screen + // hostnames larger won't render properly const u_char HOSTNAME_SIZE = VALUES_COL - VALUES_SEPR - BOX_HMARGIN; char hostname_buf[HOSTNAME_SIZE]; @@ -239,8 +225,13 @@ void print_ui() { ui_update_cursor_focus(); } +#define READ_NONBLOCK_DELAY 100000 // NOLINTNEXTLINE(readability-function-cognitive-complexity) int load(struct Vector* users, struct Vector* sessions) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = READ_NONBLOCK_DELAY; // timeout = 100ms + /// SETUP gusers = users; gsessions = sessions; @@ -278,7 +269,7 @@ int load(struct Vector* users, struct Vector* sessions) { free(initial_state.session_opt); } - print_ui(); + scratch_print_ui(); /// INTERACTIVE u_char len; @@ -287,24 +278,10 @@ int load(struct Vector* users, struct Vector* sessions) { while (true) { if (need_resize) { need_resize = 0; - resize_ui(); + scratch_print_ui(); } - // Async press handling (magic) - fd_set fds; - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000; // timeout = 100ms - // Wait for input with timeout - int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); - if (ret < 0) { - if (errno == EINTR) continue; // Interrupted by signal - } - if (ret == 0) continue; // Timeout - - read_press(&len, seq); + if (!read_press_nb(&len, seq, &tv)) continue; if (*seq == '\x1b') { enum keys ansi_code = find_ansi(seq); if (ansi_code != -1) { diff --git a/src/util.c b/src/util.c index 7701f9f..12864d4 100644 --- a/src/util.c +++ b/src/util.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -53,6 +55,17 @@ void read_press(u_char* length, char* out) { } } +bool read_press_nb(u_char* length, char* out, struct timeval* tv) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv); + if ((ret < 0 && errno == EINTR) || ret == 0) return false; + + read_press(length, out); + return true; +} + // https://stackoverflow.com/a/48040042 static int selret_magic() { fd_set set;