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 <nbd@nbd.name>
This commit is contained in:
Felix Fietkau 2025-02-27 11:21:22 +01:00
parent 7953376400
commit 8835ecf29b
4 changed files with 78 additions and 33 deletions

View File

@ -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

View File

@ -101,13 +101,16 @@ update_window_size(struct uline_state *s, bool init)
#ifdef TIOCGWINSZ
struct winsize ws = {};
if (!ioctl(fileno(s->output), TIOCGWINSZ, &ws)) {
if (ws.ws_col)
if (s->ioctl_winsize &&
!ioctl(fileno(s->output), TIOCGWINSZ, &ws) &&
ws.ws_col && ws.ws_row) {
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 +537,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 +555,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 +694,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 +730,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 +914,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 +929,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)

View File

@ -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;
};

View File

@ -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,12 +45,11 @@ 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);
case '~':
switch (code) {
case 1:
return VT100_HOME;
@ -65,6 +64,20 @@ enum vt100_escape vt100_esc_decode(const char *str)
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;
}
default:
return VT100_UNKNOWN;
}