From: Felix Fietkau Date: Thu, 27 Feb 2025 10:21:22 +0000 (+0100) Subject: ucode-mod-uline: add support for querying window size from terminal if ioctl fails X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=c07e6f9d128f96ef8970e863ce61df46b861f6d9;p=openwrt%2Fstaging%2Fnbd.git ucode-mod-uline: add support for querying window size from terminal if ioctl fails This is useful for running the cli on a serial console Signed-off-by: Felix Fietkau --- diff --git a/package/utils/ucode-mod-uline/src/private.h b/package/utils/ucode-mod-uline/src/private.h index fa38d06737..e53ec22e03 100644 --- a/package/utils/ucode-mod-uline/src/private.h +++ b/package/utils/ucode-mod-uline/src/private.h @@ -52,6 +52,7 @@ enum vt100_escape { VT100_CURSOR_WORD_LEFT, VT100_CURSOR_RIGHT, VT100_CURSOR_WORD_RIGHT, + VT100_CURSOR_POS, VT100_HOME, VT100_END, VT100_INSERT, @@ -63,7 +64,7 @@ enum vt100_escape { }; ssize_t utf8_nsyms(const char *str, size_t len); -enum vt100_escape vt100_esc_decode(const char *str); +enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data); // helpers: void __vt100_csi_num(FILE *out, int num, char code); @@ -191,4 +192,15 @@ static inline void vt100_ding(FILE *out) fflush(out); } +static inline void vt100_request_window_size(FILE *out) +{ + fputs( + "\e7" /* save cursor position */ + "\e[r" /* reset margins */ + "\e[999;999H" /* move cursor to bottom right */ + "\e[6n" /* report cursor position */ + "\e8", /* restore cursor position */ + out); +} + #endif diff --git a/package/utils/ucode-mod-uline/src/uline.c b/package/utils/ucode-mod-uline/src/uline.c index 26cea6fa24..54febe92d5 100644 --- a/package/utils/ucode-mod-uline/src/uline.c +++ b/package/utils/ucode-mod-uline/src/uline.c @@ -101,13 +101,17 @@ update_window_size(struct uline_state *s, bool init) #ifdef TIOCGWINSZ struct winsize ws = {}; - if (!ioctl(fileno(s->output), TIOCGWINSZ, &ws)) { + if (s->ioctl_winsize && + !ioctl(fileno(s->output), TIOCGWINSZ, &ws)) { if (ws.ws_col) cols = ws.ws_col; if (ws.ws_row) rows = ws.ws_row; - } + } else #endif + { + s->ioctl_winsize = false; + } s->sigwinch_count = sigwinch_count; if (s->cols == cols && s->rows == rows) @@ -534,7 +538,7 @@ move_word_right(struct uline_state *s, struct linebuf *line) } static bool -process_esc(struct uline_state *s, enum vt100_escape esc) +process_esc(struct uline_state *s, enum vt100_escape esc, uint32_t data) { struct linebuf *line = &s->line; @@ -552,6 +556,15 @@ process_esc(struct uline_state *s, enum vt100_escape esc) return move_right(s, line); case VT100_CURSOR_WORD_RIGHT: return move_word_right(s, line); + case VT100_CURSOR_POS: + if (s->rows == (data & 0xffff) && + s->cols == data >> 16) + return false; + s->rows = data & 0xffff; + s->cols = data >> 16; + s->full_update = true; + s->cb->event(s, EDITLINE_EV_WINDOW_CHANGED); + return true; case VT100_HOME: line->pos = 0; return true; @@ -682,9 +695,9 @@ process_ctrl(struct uline_state *s, char c) linebuf_reset(line); return true; case KEY_SOH: - return process_esc(s, VT100_HOME); + return process_esc(s, VT100_HOME, 0); case KEY_ENQ: - return process_esc(s, VT100_END); + return process_esc(s, VT100_END, 0); case KEY_VT: // TODO: kill return false; @@ -718,18 +731,19 @@ static void process_char(struct uline_state *s, char c) { enum vt100_escape esc; + uint32_t data = 0; check_key_repeat(s, c); if (s->esc_idx >= 0) { s->esc_seq[s->esc_idx++] = c; s->esc_seq[s->esc_idx] = 0; - esc = vt100_esc_decode(s->esc_seq); + esc = vt100_esc_decode(s->esc_seq, &data); if (esc == VT100_INCOMPLETE && s->esc_idx < (int)sizeof(s->esc_seq) - 1) return; s->esc_idx = -1; - if (!process_esc(s, esc)) + if (!process_esc(s, esc, data)) return; } else if (s->cb->key_input && !check_utf8(s, (unsigned char )c) && @@ -901,7 +915,7 @@ void uline_init(struct uline_state *s, const struct uline_cb *cb, s->utf8 = utf8; s->input = in_fd; s->output = out_stream; - update_window_size(s, true); + s->ioctl_winsize = true; reset_input_state(s); #ifdef USE_SYSTEM_WCHAR @@ -916,6 +930,12 @@ void uline_init(struct uline_state *s, const struct uline_cb *cb, s->has_termios = true; termios_set_native_mode(s); } + + update_window_size(s, true); + if (!s->ioctl_winsize) { + vt100_request_window_size(s->output); + fflush(s->output); + } } void uline_free(struct uline_state *s) diff --git a/package/utils/ucode-mod-uline/src/uline.h b/package/utils/ucode-mod-uline/src/uline.h index 6f7b75542f..514675e799 100644 --- a/package/utils/ucode-mod-uline/src/uline.h +++ b/package/utils/ucode-mod-uline/src/uline.h @@ -82,12 +82,13 @@ struct uline_state { unsigned int rows, cols; struct pos cursor_pos; struct pos end_pos; + bool ioctl_winsize; bool full_update; bool stop; bool utf8; - char esc_seq[8]; + char esc_seq[32]; int8_t esc_idx; uint8_t utf8_cont; }; diff --git a/package/utils/ucode-mod-uline/src/vt100.c b/package/utils/ucode-mod-uline/src/vt100.c index b13e6a6722..f81b11d3ad 100644 --- a/package/utils/ucode-mod-uline/src/vt100.c +++ b/package/utils/ucode-mod-uline/src/vt100.c @@ -7,10 +7,10 @@ #include "uline.h" #include "private.h" -enum vt100_escape vt100_esc_decode(const char *str) +enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data) { - unsigned long code; - size_t idx; + unsigned long code, code2; + char *err; switch (*(str++)) { case 0: @@ -45,23 +45,36 @@ enum vt100_escape vt100_esc_decode(const char *str) case '0' ... '4': case '6' ... '9': str--; - idx = strspn(str, "0123456789"); - if (!str[idx]) + code = strtoul(str, &err, 10); + switch (*err) { + case 0: return VT100_INCOMPLETE; - if (str[idx] != '~') - return VT100_UNKNOWN; - code = strtoul(str, NULL, 10); - switch (code) { - case 1: - return VT100_HOME; - case 3: - return VT100_DELETE; - case 4: - return VT100_END; - case 200: - case 201: - // paste start/end - return VT100_IGNORE; + case '~': + switch (code) { + case 1: + return VT100_HOME; + case 3: + return VT100_DELETE; + case 4: + return VT100_END; + case 200: + case 201: + // paste start/end + return VT100_IGNORE; + default: + return VT100_UNKNOWN; + } + case ';': + code2 = strtoul(err + 1, &err, 10); + switch (*err) { + case 0: + return VT100_INCOMPLETE; + case 'R': + *data = (code2 << 16) | (code & 0xffff); + return VT100_CURSOR_POS; + default: + return VT100_UNKNOWN; + } default: return VT100_UNKNOWN; }