commit 863a5e9c80be3cd18f4b257f7403359850833c50
parent debd3e3e17ce2a0dbc222a92e9d8e405205ea513
Author: Andrew Kloet <andrew@kloet.net>
Date: Tue, 14 Apr 2026 13:28:03 -0400
fix leftover garbage on term resize
Resizing the window had a lot of issues with the input box. Namely the
topbar would bleed into the message box and input would be visually
cleared on resize until the user triggered tgetch() by typing. Ncurses
refreshes are now batched using wnoutrefresh()/doupdate().
Diffstat:
| M | cio.c | | | 141 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
1 file changed, 80 insertions(+), 61 deletions(-)
diff --git a/cio.c b/cio.c
@@ -102,6 +102,13 @@ static struct Chan {
char join; /* Channel was 'j'-oined. */
} chl[MaxChans];
+static struct {
+ char buf[BufSz];
+ size_t len;
+ size_t cu;
+ size_t shft;
+} inp;
+
static int ssl = 1;
static int sslverify = 1;
static struct {
@@ -124,6 +131,7 @@ static const Rune utfmax[UtfSz + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static void scmd(char *, char *, int, char **);
static void tdrawbar(void);
+static void tdrawinput(size_t);
static void tredraw(void);
static void treset(void);
@@ -488,7 +496,7 @@ pushf(int cn, const char *fmt, ...)
if (p != c->buf)
waddch(scr.mw, '\n');
pushl(p, c->eol - 1);
- wrefresh(scr.mw);
+ wnoutrefresh(scr.mw);
}
}
@@ -717,12 +725,16 @@ tresize(void)
die("cio: Ioctl (TIOCGWINSZ) failed:");
if (ws.ws_row <= 2) return;
resizeterm(scr.y = ws.ws_row, scr.x = ws.ws_col);
+ erase();
+ wnoutrefresh(stdscr);
wresize(scr.mw, scr.y - 2, scr.x);
wresize(scr.iw, 1, scr.x);
wresize(scr.sw, 1, scr.x);
mvwin(scr.iw, scr.y - 1, 0);
tredraw();
tdrawbar();
+ tdrawinput(0);
+ doupdate();
}
static void
@@ -734,7 +746,7 @@ tredraw(void)
if (c->eol == c->buf) {
wclear(scr.mw);
- wrefresh(scr.mw);
+ wnoutrefresh(scr.mw);
return;
}
p = c->eol - 1;
@@ -761,7 +773,7 @@ tredraw(void)
wmove(scr.mw, 0, 0);
while (q < p)
q = pushl(q, p);
- wrefresh(scr.mw);
+ wnoutrefresh(scr.mw);
}
static void
@@ -789,15 +801,37 @@ tdrawbar(void)
if (fst == ch)
wattroff(scr.sw, A_BOLD);
}
- wrefresh(scr.sw);
+ wnoutrefresh(scr.sw);
+}
+
+static void
+tdrawinput(size_t dirty)
+{
+ size_t i;
+
+ while (inp.cu < inp.shft)
+ dirty = 0, inp.shft -= inp.shft >= scr.x / 2 ? scr.x / 2 : inp.shft;
+ while (inp.cu >= scr.x + inp.shft)
+ dirty = 0, inp.shft += scr.x / 2;
+ if (dirty <= inp.shft)
+ i = inp.shft;
+ else if (dirty > scr.x + inp.shft || dirty > inp.len)
+ goto mvcur;
+ else
+ i = dirty;
+ wmove(scr.iw, 0, i - inp.shft);
+ wclrtoeol(scr.iw);
+ for (; i - inp.shft < scr.x && i < inp.len; i++)
+ waddch(scr.iw, inp.buf[i]);
+mvcur:
+ wmove(scr.iw, 0, inp.cu - inp.shft);
+ wnoutrefresh(scr.iw);
}
static void
tgetch(void)
{
- static char l[BufSz];
- static size_t shft, cu, len;
- size_t dirty = len + 1, i;
+ size_t dirty = inp.len + 1, i;
int c;
c = wgetch(scr.iw);
@@ -825,83 +859,69 @@ tgetch(void)
tredraw();
return;
case CTRL('a'):
- cu = 0;
+ inp.cu = 0;
break;
case CTRL('e'):
- cu = len;
+ inp.cu = inp.len;
break;
case CTRL('b'):
case KEY_LEFT:
- if (cu)
- cu--;
+ if (inp.cu)
+ inp.cu--;
break;
case CTRL('f'):
case KEY_RIGHT:
- if (cu < len)
- cu++;
+ if (inp.cu < inp.len)
+ inp.cu++;
break;
case CTRL('k'):
- dirty = len = cu;
+ dirty = inp.len = inp.cu;
break;
case CTRL('u'):
- if (cu == 0) return;
- len -= cu;
- memmove(l, &l[cu], len);
- dirty = cu = 0;
+ if (inp.cu == 0) return;
+ inp.len -= inp.cu;
+ memmove(inp.buf, &inp.buf[inp.cu], inp.len);
+ dirty = inp.cu = 0;
break;
case CTRL('d'):
- if (cu >= len) return;
- memmove(&l[cu], &l[cu + 1], len - cu - 1);
- dirty = cu;
- len--;
+ if (inp.cu >= inp.len) return;
+ memmove(&inp.buf[inp.cu], &inp.buf[inp.cu + 1], inp.len - inp.cu - 1);
+ dirty = inp.cu;
+ inp.len--;
break;
case CTRL('h'):
case KEY_BACKSPACE:
- if (cu == 0) return;
- memmove(&l[cu - 1], &l[cu], len - cu);
- dirty = --cu;
- len--;
+ if (inp.cu == 0) return;
+ memmove(&inp.buf[inp.cu - 1], &inp.buf[inp.cu], inp.len - inp.cu);
+ dirty = --inp.cu;
+ inp.len--;
break;
case CTRL('w'):
- if (cu == 0) break;
+ if (inp.cu == 0) break;
i = 1;
- while (l[cu - i] == ' ' && cu - i != 0) i++;
- while (l[cu - i] != ' ' && cu - i != 0) i++;
- if (cu - i != 0) i--;
- memmove(&l[cu - i], &l[cu], len - cu);
- cu -= i;
- dirty = cu;
- len -= i;
+ while (inp.buf[inp.cu - i] == ' ' && inp.cu - i != 0) i++;
+ while (inp.buf[inp.cu - i] != ' ' && inp.cu - i != 0) i++;
+ if (inp.cu - i != 0) i--;
+ memmove(&inp.buf[inp.cu - i], &inp.buf[inp.cu], inp.len - inp.cu);
+ inp.cu -= i;
+ dirty = inp.cu;
+ inp.len -= i;
break;
case '\n':
- l[len] = 0;
- uparse(l);
- dirty = cu = len = 0;
+ inp.buf[inp.len] = 0;
+ uparse(inp.buf);
+ dirty = inp.cu = inp.len = 0;
break;
default:
- if (c > CHAR_MAX || len >= BufSz - 1)
- return; /* Skip other curses codes. */
- memmove(&l[cu + 1], &l[cu], len - cu);
- dirty = cu;
- len++;
- l[cu++] = c;
+ if (c > CHAR_MAX || inp.len >= BufSz - 1)
+ return;
+ memmove(&inp.buf[inp.cu + 1], &inp.buf[inp.cu], inp.len - inp.cu);
+ dirty = inp.cu;
+ inp.len++;
+ inp.buf[inp.cu++] = c;
break;
}
- while (cu < shft)
- dirty = 0, shft -= shft >= scr.x / 2 ? scr.x / 2 : shft;
- while (cu >= scr.x + shft)
- dirty = 0, shft += scr.x / 2;
- if (dirty <= shft)
- i = shft;
- else if (dirty > scr.x + shft || dirty > len)
- goto mvcur;
- else
- i = dirty;
- wmove(scr.iw, 0, i - shft);
- wclrtoeol(scr.iw);
- for (; i - shft < scr.x && i < len; i++)
- waddch(scr.iw, l[i]);
-mvcur: wmove(scr.iw, 0, cu - shft);
+ tdrawinput(dirty);
}
static void
@@ -1030,10 +1050,9 @@ if (pledge("stdio tty rpath inet dns", NULL) == -1)
outp -= wr;
memmove(outb, outb + wr, outp - outb);
}
- if (FD_ISSET(0, &rfs)) {
+ if (FD_ISSET(0, &rfs))
tgetch();
- wrefresh(scr.iw);
- }
+ doupdate();
if ((!FD_ISSET(srv.fd, &wfs))
&& (!FD_ISSET(srv.fd, &rfs))
&& (outp == outb)