From ba836e5cd42ed4b18f966c501e598201b7500786 Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 03:30:29 +0200 Subject: [PATCH 1/6] feat: add refresh_rate option for clock --- assets/man/lidm-config.5 | 3 + include/config.h | 5 +- include/util.h | 2 +- src/config.c | 17 +++- src/ui.c | 188 +++++++++++++++++++------------------ src/util.c | 3 +- themes/cherry.ini | 1 + themes/default.ini | 1 + themes/kanagawa-dragon.ini | 1 + themes/kanagawa-wave.ini | 1 + themes/minimal.ini | 1 + themes/nature.ini | 1 + themes/nord.ini | 1 + themes/nothing.ini | 1 + themes/old-blue.ini | 1 + themes/tasteless.ini | 1 + 16 files changed, 133 insertions(+), 95 deletions(-) diff --git a/assets/man/lidm-config.5 b/assets/man/lidm-config.5 index 8f25ac2..db33986 100644 --- a/assets/man/lidm-config.5 +++ b/assets/man/lidm-config.5 @@ -98,6 +98,9 @@ Types \fBSTRING_ARRAY\fP. Specify paths to source on login if they exist, simple .TP \fBtimefmt\fP Specify the time format string to be displayed. Check \fBstrftime (3)\fP to know the possible formatting variables. +.TP +\fBrefresh_rate\fP +Rate (in milliseconds) at which the UI should refresh, affects clock and resize behavior. .SH "SEE ALSO" diff --git a/include/config.h b/include/config.h index 108923b..73a66c3 100644 --- a/include/config.h +++ b/include/config.h @@ -11,12 +11,14 @@ enum introspection_type { STRING, BOOL, + NUMBER, KEY, STRING_ARRAY, }; static const char* NNULLABLE const INTROS_TYS_NAMES[] = { [STRING] = "STRING", [BOOL] = "BOOL", + [NUMBER] = "NUMBER", [KEY] = "KEY", [STRING_ARRAY] = "STRING_ARRAY", }; @@ -116,7 +118,8 @@ BUILD(strings, STRINGS, TABLE_STRINGS); F(bool, include_defshell, BOOL, true, name) \ F(struct Vector, source, STRING_ARRAY, NULL_VEC, name) \ F(struct Vector, user_source, STRING_ARRAY, NULL_VEC, name) \ - F(char* NNULLABLE, timefmt, STRING, "%X %x", name) + F(char* NNULLABLE, timefmt, STRING, "%X %x", name) \ + F(long long, refresh_rate, NUMBER, 100, name) BUILD(behavior, BEHAVIOR, TABLE_BEHAVIOR); diff --git a/include/util.h b/include/util.h index 9dce5fd..b45fd53 100644 --- a/include/util.h +++ b/include/util.h @@ -13,7 +13,7 @@ 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 +// non blocking, waits up to tv or interrupt, returns true if actually read bool read_press_nb(u_char* length, char* out, struct timeval* tv); bool utf8_iscont(char byte); diff --git a/src/config.c b/src/config.c index b379f20..ea71559 100644 --- a/src/config.c +++ b/src/config.c @@ -1,10 +1,13 @@ +#include #include #include #include +#include #include #include #include #include +#include #include "config.h" #include "desktop.h" @@ -111,6 +114,7 @@ unfinished: #define NOFAIL return (struct parser_error){NULL, 0} union typ_ptr { char** string; + long long* number; bool* boolean; enum keys* key; struct Vector* vec; @@ -147,6 +151,13 @@ struct parser_error parse_key(enum introspection_type typ, union typ_ptr at, FAIL("Invalid key value, wasn't 'true' nor 'false'"); } break; + case NUMBER: + errno = 0; + *at.number = strtol(key, NULL, 10); + if (errno) { + FAIL("strtol failed"); + } + break; case KEY: // NOLINTNEXTLINE(performance-no-int-to-ptr) if (find_keyname(at.key, key) != 0) { @@ -205,6 +216,8 @@ struct status config_line_handler(void* _config, char* table, char* k, size_t offset = this_intros_table->offset + this_intros_key->offset; union typ_ptr k_addr = {.ptr = (uintptr_t)config + offset}; + log_printf("[I] parsing [%s.%s] as %s\n", table, k, + INTROS_TYS_NAMES[this_intros_key->typ]); struct parser_error err = parse_key(this_intros_key->typ, k_addr, v, offset); if (err.msg != NULL) { log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k, @@ -213,7 +226,9 @@ struct status config_line_handler(void* _config, char* table, char* k, return ret; } - if (this_intros_key->typ == STRING) + if (this_intros_key->typ == NUMBER) + log_printf("[I] cfg parsed [%s.%s] (%lld)\n", table, k, *k_addr.number); + else if (this_intros_key->typ == STRING) log_printf("[I] cfg parsed [%s.%s] (%s)\n", table, k, *k_addr.string); else log_printf("[I] cfg parsed [%s.%s]\n", table, k); diff --git a/src/ui.c b/src/ui.c index 0d6a271..41c7622 100644 --- a/src/ui.c +++ b/src/ui.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -31,21 +32,20 @@ const u_char INPUTS_N = 3; -static void print_box(); -static void print_footer(); -static void restore_all(); -static void signal_handler(int code); - struct uint_point { uint x; uint y; }; -static void print_session(struct uint_point origin, struct session session, - bool multiple); -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_box(); +static void print_head(); +static void print_footer(); +static void restore_all(); +static void signal_handler(int code); + +static void print_session(struct session session, bool multiple); +static void print_user(struct user user, bool multiple); +static void print_passwd(uint length, bool err); static void scratch_print_ui(); // ansi resource: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 @@ -79,14 +79,10 @@ void setup(struct config* config) { (void)atexit(restore_all); (void)signal(SIGINT, signal_handler); (void)signal(SIGWINCH, process_sigwinch); + (void)fflush(stdout); } -static struct uint_point box_start() { - return (struct uint_point){ - .x = ((window.ws_col - BOX_WIDTH) / 2) + 1, // looks better - .y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under - }; -} +static struct uint_point box_start = {.x = 0, .y = 0}; #define STRFTIME_PREALLOC 64 #define TM_YEAR_EPOCH 1900 @@ -98,7 +94,8 @@ static char* fmt_time(const char* fmt) { char* buf = malloc(alloc_size); if (!buf) return NULL; while (true) { - if (strftime(buf, alloc_size, fmt, &tm) != 0) return buf; + if (strftime(buf, alloc_size, fmt, &tm) != 0 && strlen(fmt) != 0) + return buf; alloc_size *= 2; char* nbuf = realloc(buf, alloc_size); @@ -111,9 +108,8 @@ static char* fmt_time(const char* fmt) { } void ui_update_cursor_focus() { - struct uint_point bstart = box_start(); - u_char line = bstart.y; - u_char col = bstart.x + VALUES_COL; + u_char line = box_start.y; + u_char col = box_start.x + VALUES_COL; struct opts_field* ofield = get_opts_ffield(); u_char maxlen = VALUE_MAXLEN; @@ -133,19 +129,16 @@ void ui_update_cursor_focus() { line += PASSWD_ROW; (void)printf("\x1b[%d;%dH", line, col); - (void)fflush(stdout); } void ui_update_field(enum input focused_input) { - struct uint_point origin = box_start(); - if (focused_input == PASSWD) { - print_passwd(origin, utf8len(of_passwd.efield.content), false); + print_passwd(utf8len(of_passwd.efield.content), false); } else if (focused_input == SESSION) { - print_session(origin, st_session(g_config->behavior.include_defshell), + print_session(st_session(g_config->behavior.include_defshell), of_session.opts > 1); } else if (focused_input == USER) { - print_user(origin, st_user(), of_user.opts > 1); + print_user(st_user(), of_user.opts > 1); ui_update_field(SESSION); } @@ -173,6 +166,10 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) { static char* unknown_str = "unknown"; void scratch_print_ui() { ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); + box_start = (struct uint_point){ + .x = ((window.ws_col - BOX_WIDTH) / 2) + 1, // looks better + .y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under + }; if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { @@ -184,39 +181,11 @@ void scratch_print_ui() { 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]; - char* hostname = hostname_buf; - if (gethostname(hostname_buf, HOSTNAME_SIZE) != 0) { - hostname = unknown_str; - } else { - // Ig "successful completion" doesn't contemplate truncation case, so need - // to append the unspecified nullbyte - - // char* hidx = - // (char*)utf8back(&hostname[VALUES_COL - VALUES_SEPR - BOX_HMARGIN - - // 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); - + print_head(); print_footer(); ui_update_field(SESSION); @@ -225,12 +194,12 @@ void scratch_print_ui() { ui_update_cursor_focus(); } -#define READ_NONBLOCK_DELAY 100000 +#define MS_PER_S 1000 +#define US_PER_MS 1000 // 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 + long long ms_time = g_config->behavior.refresh_rate; + ms_time = ms_time < 0 ? 0 : ms_time; /// SETUP gusers = users; @@ -279,8 +248,17 @@ int load(struct Vector* users, struct Vector* sessions) { if (need_resize) { need_resize = 0; scratch_print_ui(); + } else { + // partial refresh + print_head(); + ui_update_cursor_focus(); } + struct timeval tv; + tv.tv_usec = (ms_time % MS_PER_S) * US_PER_MS; + tv.tv_sec = ms_time / MS_PER_S; + + (void)fflush(stdout); if (!read_press_nb(&len, seq, &tv)) continue; if (*seq == '\x1b') { enum keys ansi_code = find_ansi(seq); @@ -318,7 +296,7 @@ int load(struct Vector* users, struct Vector* sessions) { if (!launch(st_user().username, of_passwd.efield.content, st_session(g_config->behavior.include_defshell), &restore_all, g_config)) { - print_passwd(box_start(), utf8len(of_passwd.efield.content), true); + print_passwd(utf8len(of_passwd.efield.content), true); ui_update_cursor_focus(); } } else @@ -344,9 +322,43 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { return pos - ofield_display_cursor_col(self, maxlen); } -void print_session(struct uint_point origin, struct session session, - bool multiple) { - clean_line(origin, SESSION_ROW); +#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN) +void print_head() { + // hostname doesn't just change on runtime + static char* hostname = NULL; + static char hostname_buf[HOSTNAME_SIZE + 1]; + if (!hostname) { + // hostnames larger won't render properly + hostname = hostname_buf; + if (gethostname(hostname_buf, HOSTNAME_SIZE) != 0) { + hostname = unknown_str; + } else { + // Ig "successful completion" doesn't contemplate truncation case, so need + // to append the unspecified nullbyte + + // char* hidx = + // (char*)utf8back(&hostname[VALUES_COL - VALUES_SEPR - BOX_HMARGIN - + // 1]); + // *hidx = '\0'; + } + } + + clean_line(box_start, HEAD_ROW); + // put hostname + printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", + box_start.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[%dG\x1b[%sm%s\x1b[%sm", + box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time), + g_config->colors.e_date, fmtd_time, g_config->colors.fg); + free(fmtd_time); +} + +void print_session(struct session session, bool multiple) { + clean_line(box_start, SESSION_ROW); const char* NNULLABLE session_type; if (session.type == XORG) { @@ -361,10 +373,10 @@ void print_session(struct uint_point origin, struct session session, // already in the box, - 1 bcs no need to step over margin, same reasoning in // other places - printf( - "\r\x1b[%luC\x1b[%sm%s\x1b[%sm", - (ulong)(origin.x + VALUES_COL - VALUES_SEPR - utf8len(session_type) - 1), - g_config->colors.e_header, session_type, g_config->colors.fg); + printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", + (ulong)(box_start.x + VALUES_COL - VALUES_SEPR - + utf8len(session_type) - 1), + g_config->colors.e_header, session_type, g_config->colors.fg); char* session_color; if (session.type == XORG) { @@ -382,21 +394,21 @@ void print_session(struct uint_point origin, struct session session, toprint += get_render_pos_offset(&of_session, maxlen); size_t printlen = utf8seekn(toprint, maxlen) - toprint; - printf("\r\x1b[%dC%s\x1b[%sm%.*s\x1b[%sm%s", origin.x + VALUES_COL - 1, + printf("\r\x1b[%dC%s\x1b[%sm%.*s\x1b[%sm%s", box_start.x + VALUES_COL - 1, g_config->strings.opts_pre, session_color, (int)printlen, toprint, g_config->colors.fg, g_config->strings.opts_post); } else { toprint += get_render_pos_offset(&of_session, VALUE_MAXLEN); size_t printlen = utf8seekn(toprint, VALUE_MAXLEN) - toprint; - printf("\r\x1b[%dC\x1b[%sm%.*s\x1b[%sm", origin.x + VALUES_COL - 1, + printf("\r\x1b[%dC\x1b[%sm%.*s\x1b[%sm", box_start.x + VALUES_COL - 1, session_color, (int)printlen, toprint, g_config->colors.fg); } } -void print_user(struct uint_point origin, struct user user, bool multiple) { - clean_line(origin, USER_ROW); +void print_user(struct user user, bool multiple) { + clean_line(box_start, USER_ROW); printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", - (ulong)(origin.x + VALUES_COL - VALUES_SEPR - + (ulong)(box_start.x + VALUES_COL - VALUES_SEPR - utf8len(g_config->strings.e_user) - 1), g_config->colors.e_header, g_config->strings.e_user, g_config->colors.fg); @@ -410,21 +422,21 @@ void print_user(struct uint_point origin, struct user user, bool multiple) { toprint += get_render_pos_offset(&of_session, maxlen); size_t printlen = utf8seekn(toprint, maxlen) - toprint; - printf("\r\x1b[%dC< \x1b[%sm%.*s\x1b[%sm >", origin.x + VALUES_COL - 1, + printf("\r\x1b[%dC< \x1b[%sm%.*s\x1b[%sm >", box_start.x + VALUES_COL - 1, user_color, (int)printlen, toprint, g_config->colors.fg); } else { toprint += get_render_pos_offset(&of_user, VALUE_MAXLEN); size_t printlen = utf8seekn(toprint, VALUE_MAXLEN) - toprint; - printf("\r\x1b[%dC\x1b[%sm%.*s\x1b[%sm", origin.x + VALUES_COL - 1, + printf("\r\x1b[%dC\x1b[%sm%.*s\x1b[%sm", box_start.x + VALUES_COL - 1, user_color, (int)printlen, toprint, g_config->colors.fg); } } -void print_passwd(struct uint_point origin, uint length, bool err) { +void print_passwd(uint length, bool err) { char passwd_prompt[VALUE_MAXLEN + 1]; - clean_line(origin, PASSWD_ROW); + clean_line(box_start, PASSWD_ROW); printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", - (ulong)(origin.x + VALUES_COL - VALUES_SEPR - + (ulong)(box_start.x + VALUES_COL - VALUES_SEPR - utf8len(g_config->strings.e_passwd) - 1), g_config->colors.e_header, g_config->strings.e_passwd, g_config->colors.fg); @@ -440,7 +452,7 @@ void print_passwd(struct uint_point origin, uint length, bool err) { memset(passwd_prompt, '*', actual_len); passwd_prompt[VALUE_MAXLEN] = '\0'; - printf("\r\x1b[%dC\x1b[%sm", origin.x + VALUES_COL - 1, pass_color); + printf("\r\x1b[%dC\x1b[%sm", box_start.x + VALUES_COL - 1, pass_color); printf("%s", passwd_prompt); printf("\x1b[%sm", g_config->colors.fg); @@ -464,9 +476,8 @@ static void print_row(uint wid, uint n, char* edge1, char* edge2, } static void print_box() { - const struct uint_point BSTART = box_start(); - - printf("\x1b[%d;%dH\x1b[%sm", BSTART.y, BSTART.x, g_config->colors.e_box); + printf("\x1b[%d;%dH\x1b[%sm", box_start.y, box_start.x, + g_config->colors.e_box); print_row(BOX_WIDTH - 2, 1, g_config->chars.ctl, g_config->chars.ctr, g_config->chars.hb); print_empty_row(BOX_WIDTH - 2, BOX_HEIGHT - 2, g_config->chars.vb, @@ -474,7 +485,6 @@ static void print_box() { print_row(BOX_WIDTH - 2, 1, g_config->chars.cbl, g_config->chars.cbr, g_config->chars.hb); printf("\x1b[%sm", g_config->colors.fg); - (void)fflush(stdout); } static void print_footer() { @@ -498,24 +508,21 @@ static void print_footer() { KEY_NAMES[g_config->functions.reboot], KEY_NAMES[g_config->functions.refresh], g_config->strings.f_poweroff, g_config->strings.f_reboot, g_config->strings.f_refresh); - (void)fflush(stdout); } void print_err(const char* msg) { - struct uint_point origin = box_start(); - (void)fprintf(stderr, "\x1b[%d;%dH%s(%d): %s", origin.y - 1, origin.x, msg, - errno, strerror(errno)); + (void)fprintf(stderr, "\x1b[%d;%dH%s(%d): %s", box_start.y - 1, box_start.x, + msg, errno, strerror(errno)); } void print_errno(const char* descr) { - struct uint_point origin = box_start(); if (descr == NULL) (void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s", - origin.y - 1, origin.x, g_config->colors.err, errno, + box_start.y - 1, box_start.x, g_config->colors.err, errno, strerror(errno)); else { - (void)fprintf(stderr, "\x1b[%d;%dH\x1b[%sm%s(%d): %s", origin.y - 1, - origin.x, g_config->colors.err, descr, errno, + (void)fprintf(stderr, "\x1b[%d;%dH\x1b[%sm%s(%d): %s", box_start.y - 1, + box_start.x, g_config->colors.err, descr, errno, strerror(errno)); } } @@ -523,7 +530,6 @@ void print_errno(const char* descr) { void restore_all() { // restore cursor pos, restore screen and show cursor (void)printf("\x1b[u\x1b[?47l\x1b[?25h"); - (void)fflush(stdout); tcsetattr(STDOUT_FILENO, TCSANOW, &orig_term); } diff --git a/src/util.c b/src/util.c index 12864d4..981ddfe 100644 --- a/src/util.c +++ b/src/util.c @@ -59,8 +59,9 @@ bool read_press_nb(u_char* length, char* out, struct timeval* tv) { fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); + errno = 0; int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv); - if ((ret < 0 && errno == EINTR) || ret == 0) return false; + if (errno || ret <= 0) return false; read_press(length, out); return true; diff --git a/themes/cherry.ini b/themes/cherry.ini index 9e047dd..1e736a6 100644 --- a/themes/cherry.ini +++ b/themes/cherry.ini @@ -42,6 +42,7 @@ e_key = "1;23;38;5;197" [behavior] # include_defshell = true # timefmt = "%X %x" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/default.ini b/themes/default.ini index 0853f10..9737f76 100644 --- a/themes/default.ini +++ b/themes/default.ini @@ -42,6 +42,7 @@ [behavior] # include_defshell = true # timefmt = "%X %x" +refresh_rate = 2000 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/kanagawa-dragon.ini b/themes/kanagawa-dragon.ini index b1426ea..ebc302f 100644 --- a/themes/kanagawa-dragon.ini +++ b/themes/kanagawa-dragon.ini @@ -42,6 +42,7 @@ e_user = "38;2;148;89;84" [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/kanagawa-wave.ini b/themes/kanagawa-wave.ini index 617b635..775375f 100644 --- a/themes/kanagawa-wave.ini +++ b/themes/kanagawa-wave.ini @@ -42,6 +42,7 @@ e_user = "38;2;211;137;88" [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/minimal.ini b/themes/minimal.ini index d823179..5a10c99 100644 --- a/themes/minimal.ini +++ b/themes/minimal.ini @@ -43,6 +43,7 @@ s_shell = "$" [behavior] # include_defshell = true timefmt = "\xef\x80\x97 %X \xef\x84\xb3 %x" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/nature.ini b/themes/nature.ini index 1158f64..1436e3b 100644 --- a/themes/nature.ini +++ b/themes/nature.ini @@ -42,6 +42,7 @@ e_key = "32" [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/nord.ini b/themes/nord.ini index a7b9bc7..4eea960 100644 --- a/themes/nord.ini +++ b/themes/nord.ini @@ -42,6 +42,7 @@ e_key = "34" [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/nothing.ini b/themes/nothing.ini index 1580af1..fc75736 100644 --- a/themes/nothing.ini +++ b/themes/nothing.ini @@ -44,6 +44,7 @@ s_shell = "" [behavior] # include_defshell = true timefmt = " " +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/old-blue.ini b/themes/old-blue.ini index cea1cf0..146b747 100644 --- a/themes/old-blue.ini +++ b/themes/old-blue.ini @@ -42,6 +42,7 @@ s_shell = "swell" [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" diff --git a/themes/tasteless.ini b/themes/tasteless.ini index de40376..f3b99d1 100644 --- a/themes/tasteless.ini +++ b/themes/tasteless.ini @@ -43,6 +43,7 @@ opts_post = " " [behavior] # include_defshell = true timefmt = "%Y-%m-%d %H:%M:%S" +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" From 5b5adf76b78da6c840cff3a3647c57274a94917f Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 03:56:51 +0200 Subject: [PATCH 2/6] feat: support long hostnames close #49 --- src/ui.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/ui.c b/src/ui.c index 41c7622..2d79e46 100644 --- a/src/ui.c +++ b/src/ui.c @@ -322,25 +322,21 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { return pos - ofield_display_cursor_col(self, maxlen); } -#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN) +#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN - 1) +// TODO: make hostname utf8 compatible void print_head() { // hostname doesn't just change on runtime - static char* hostname = NULL; - static char hostname_buf[HOSTNAME_SIZE + 1]; - if (!hostname) { - // hostnames larger won't render properly - hostname = hostname_buf; - if (gethostname(hostname_buf, HOSTNAME_SIZE) != 0) { - hostname = unknown_str; - } else { - // Ig "successful completion" doesn't contemplate truncation case, so need - // to append the unspecified nullbyte - - // char* hidx = - // (char*)utf8back(&hostname[VALUES_COL - VALUES_SEPR - BOX_HMARGIN - - // 1]); - // *hidx = '\0'; + static bool hostname_set = false; + static char hostname[HOSTNAME_SIZE + 1]; + if (!hostname_set) { + strcpy(hostname, unknown_str); + int ret = gethostname(hostname, HOSTNAME_SIZE); + if (hostname[HOSTNAME_SIZE] != '\0' || ret < 0) { + // assume any type of truncation + hostname[HOSTNAME_SIZE] = '\0'; + hostname[HOSTNAME_SIZE - 1] = '_'; // "ellipsis" } + hostname_set = true; } clean_line(box_start, HEAD_ROW); From 9084dfdcdaedd87d1c28d40faae2128e96a8e061 Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 04:12:22 +0200 Subject: [PATCH 3/6] fix: wrong default.ini value --- themes/default.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/default.ini b/themes/default.ini index 9737f76..3b97d7c 100644 --- a/themes/default.ini +++ b/themes/default.ini @@ -42,7 +42,7 @@ [behavior] # include_defshell = true # timefmt = "%X %x" -refresh_rate = 2000 +# refresh_rate = 100 source = "/etc/lidm.env" source = "/etc/locale.conf" user_source = ".lidm.env" From 5a6471dd001ff072797947ab48806f3aad69512f Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 04:40:10 +0200 Subject: [PATCH 4/6] feat: utf8 hostname and ellipsis --- src/ui.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/ui.c b/src/ui.c index 2d79e46..6c57cd1 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,6 +1,7 @@ // i'm sorry // really sorry +#include #include #include #include @@ -107,6 +108,31 @@ static char* fmt_time(const char* fmt) { } } +char* trunc_gethostname(const size_t MAXSIZE, const char* const ELLIPSIS) { + size_t alloc_size = MAXSIZE + 1; + char* buf = malloc(alloc_size); + if (!buf) return NULL; + while (true) { + if (gethostname(buf, alloc_size) == 0) return buf; + if (errno == ENAMETOOLONG) { + buf[alloc_size] = '\0'; + if (utf8len(buf) > MAXSIZE) { + strcpy(&buf[MAXSIZE - utf8len(ELLIPSIS)], ELLIPSIS); + return buf; + } + + alloc_size *= 2; + char* nbuf = realloc(buf, alloc_size); + if (!nbuf) goto fail; + buf = nbuf; + } else + goto fail; + } +fail: + free(buf); + return NULL; +} + void ui_update_cursor_focus() { u_char line = box_start.y; u_char col = box_start.x + VALUES_COL; @@ -163,7 +189,6 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) { ui_update_field(input); } -static char* unknown_str = "unknown"; void scratch_print_ui() { ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); box_start = (struct uint_point){ @@ -323,21 +348,11 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { } #define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN - 1) -// TODO: make hostname utf8 compatible void print_head() { // hostname doesn't just change on runtime - static bool hostname_set = false; - static char hostname[HOSTNAME_SIZE + 1]; - if (!hostname_set) { - strcpy(hostname, unknown_str); - int ret = gethostname(hostname, HOSTNAME_SIZE); - if (hostname[HOSTNAME_SIZE] != '\0' || ret < 0) { - // assume any type of truncation - hostname[HOSTNAME_SIZE] = '\0'; - hostname[HOSTNAME_SIZE - 1] = '_'; // "ellipsis" - } - hostname_set = true; - } + static char* hostname = NULL; + if (!hostname) hostname = trunc_gethostname(HOSTNAME_SIZE, "..."); + if (!hostname) hostname = "unknown"; clean_line(box_start, HEAD_ROW); // put hostname From a316afecb28340ae3af54b76870d631a9288f026 Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 04:47:32 +0200 Subject: [PATCH 5/6] fix: possible memory issues --- src/ui.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui.c b/src/ui.c index 6c57cd1..840154d 100644 --- a/src/ui.c +++ b/src/ui.c @@ -109,6 +109,7 @@ static char* fmt_time(const char* fmt) { } char* trunc_gethostname(const size_t MAXSIZE, const char* const ELLIPSIS) { + if(utf8len(ELLIPSIS) > MAXSIZE) return NULL; size_t alloc_size = MAXSIZE + 1; char* buf = malloc(alloc_size); if (!buf) return NULL; @@ -116,7 +117,7 @@ char* trunc_gethostname(const size_t MAXSIZE, const char* const ELLIPSIS) { if (gethostname(buf, alloc_size) == 0) return buf; if (errno == ENAMETOOLONG) { buf[alloc_size] = '\0'; - if (utf8len(buf) > MAXSIZE) { + if (utf8len(buf) > MAXSIZE - utf8len(ELLIPSIS)) { strcpy(&buf[MAXSIZE - utf8len(ELLIPSIS)], ELLIPSIS); return buf; } From 9c022c58137a01ad6efefabd566e5b774e9be17c Mon Sep 17 00:00:00 2001 From: javalsai Date: Fri, 4 Jul 2025 04:48:20 +0200 Subject: [PATCH 6/6] feat(cfg): add ellipsis to config --- assets/man/lidm-config.5 | 3 +++ include/config.h | 3 ++- src/ui.c | 5 +++-- themes/cherry.ini | 1 + themes/default.ini | 1 + themes/kanagawa-dragon.ini | 1 + themes/kanagawa-wave.ini | 1 + themes/minimal.ini | 1 + themes/nature.ini | 1 + themes/nord.ini | 1 + themes/nothing.ini | 1 + themes/old-blue.ini | 1 + themes/tasteless.ini | 1 + 13 files changed, 18 insertions(+), 3 deletions(-) diff --git a/assets/man/lidm-config.5 b/assets/man/lidm-config.5 index db33986..f5d6e16 100644 --- a/assets/man/lidm-config.5 +++ b/assets/man/lidm-config.5 @@ -86,6 +86,9 @@ Text to display as the header for such sessions. .TP \fBopts_pre, opts_post\fP To be used for items with multiple options on the left and right sides \fB"< "\fP and \fB" >"\fP. +.TP +\fBellipsis\fP +Ellipsis to be used in multiple parts such as overflowing hostnames. .SS behavior Other miscellaneous behavior strings, of mixed types diff --git a/include/config.h b/include/config.h index 73a66c3..2b120a9 100644 --- a/include/config.h +++ b/include/config.h @@ -106,7 +106,8 @@ BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS); F(char* NNULLABLE, s_xorg, STRING, "xorg", name) \ F(char* NNULLABLE, s_shell, STRING, "shell", name) \ F(char* NNULLABLE, opts_pre, STRING, "< ", name) \ - F(char* NNULLABLE, opts_post, STRING, " >", name) + F(char* NNULLABLE, opts_post, STRING, " >", name) \ + F(char* NNULLABLE, ellipsis, STRING, "…", name) BUILD(strings, STRINGS, TABLE_STRINGS); diff --git a/src/ui.c b/src/ui.c index 840154d..725a71f 100644 --- a/src/ui.c +++ b/src/ui.c @@ -109,7 +109,7 @@ static char* fmt_time(const char* fmt) { } char* trunc_gethostname(const size_t MAXSIZE, const char* const ELLIPSIS) { - if(utf8len(ELLIPSIS) > MAXSIZE) return NULL; + if (utf8len(ELLIPSIS) > MAXSIZE) return NULL; size_t alloc_size = MAXSIZE + 1; char* buf = malloc(alloc_size); if (!buf) return NULL; @@ -352,7 +352,8 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { void print_head() { // hostname doesn't just change on runtime static char* hostname = NULL; - if (!hostname) hostname = trunc_gethostname(HOSTNAME_SIZE, "..."); + if (!hostname) + hostname = trunc_gethostname(HOSTNAME_SIZE, g_config->strings.ellipsis); if (!hostname) hostname = "unknown"; clean_line(box_start, HEAD_ROW); diff --git a/themes/cherry.ini b/themes/cherry.ini index 1e736a6..4fce938 100644 --- a/themes/cherry.ini +++ b/themes/cherry.ini @@ -38,6 +38,7 @@ e_key = "1;23;38;5;197" # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/default.ini b/themes/default.ini index 3b97d7c..a0859e6 100644 --- a/themes/default.ini +++ b/themes/default.ini @@ -38,6 +38,7 @@ # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/kanagawa-dragon.ini b/themes/kanagawa-dragon.ini index ebc302f..3690f34 100644 --- a/themes/kanagawa-dragon.ini +++ b/themes/kanagawa-dragon.ini @@ -38,6 +38,7 @@ e_user = "38;2;148;89;84" # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/kanagawa-wave.ini b/themes/kanagawa-wave.ini index 775375f..f69636f 100644 --- a/themes/kanagawa-wave.ini +++ b/themes/kanagawa-wave.ini @@ -38,6 +38,7 @@ e_user = "38;2;211;137;88" # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/minimal.ini b/themes/minimal.ini index 5a10c99..4665c84 100644 --- a/themes/minimal.ini +++ b/themes/minimal.ini @@ -39,6 +39,7 @@ s_xorg = "X" s_shell = "$" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/nature.ini b/themes/nature.ini index 1436e3b..b44c7b3 100644 --- a/themes/nature.ini +++ b/themes/nature.ini @@ -38,6 +38,7 @@ e_key = "32" # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/nord.ini b/themes/nord.ini index 4eea960..56916cd 100644 --- a/themes/nord.ini +++ b/themes/nord.ini @@ -38,6 +38,7 @@ e_key = "34" # s_shell = "shell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/nothing.ini b/themes/nothing.ini index fc75736..5c43574 100644 --- a/themes/nothing.ini +++ b/themes/nothing.ini @@ -40,6 +40,7 @@ s_xorg = "" s_shell = "" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/old-blue.ini b/themes/old-blue.ini index 146b747..847029a 100644 --- a/themes/old-blue.ini +++ b/themes/old-blue.ini @@ -38,6 +38,7 @@ s_xorg = "xworg" s_shell = "swell" # opts_pre = "< " # opts_post = " >" +# ellipsis = "…" [behavior] # include_defshell = true diff --git a/themes/tasteless.ini b/themes/tasteless.ini index f3b99d1..e0e0b2c 100644 --- a/themes/tasteless.ini +++ b/themes/tasteless.ini @@ -39,6 +39,7 @@ s_xorg = "xorg" s_shell = "sh" opts_pre = "· " opts_post = " " +ellipsis = "…" [behavior] # include_defshell = true