Render one main window from buffers

Still missing: split scrolling and preserving a blank on reflow either
from resize or ignore toggling.

Anecdata: on one of my instances of catgirl, RAM usage of the previous
release was ~30M, RAM usage of this commit was ~12M.
master
C. McEnroe 2020-09-02 00:27:16 -04:00
parent 4dc87ab9cd
commit 149cafc5ab
3 changed files with 54 additions and 160 deletions

View File

@ -178,6 +178,11 @@ int bufferPush(
return flow(&buffer->hard, cols, soft);
}
int bufferBlank(struct Buffer *buffer) {
struct Line blank = { .heat = Cold, .str = "" };
return flow(&buffer->hard, 1, &blank);
}
void bufferReflow(struct Buffer *buffer, int cols) {
buffer->hard.len = 0;
for (size_t i = 0; i < BufferCap; ++i) {

1
chat.h
View File

@ -291,6 +291,7 @@ int bufferPush(
struct Buffer *buffer, int cols,
enum Heat heat, time_t time, const char *str
);
int bufferBlank(struct Buffer *buffer);
void bufferReflow(struct Buffer *buffer, int cols);
enum Edit {

208
ui.c
View File

@ -59,7 +59,6 @@
enum {
StatusLines = 1,
WindowLines = 1024,
MarkerLines = 1,
SplitLines = 5,
InputLines = 1,
@ -71,12 +70,11 @@ enum {
#define MAIN_LINES (LINES - StatusLines - InputLines)
static WINDOW *status;
static WINDOW *marker;
static WINDOW *main;
static WINDOW *input;
struct Window {
uint id;
WINDOW *pad;
int scroll;
bool mark;
bool mute;
@ -136,10 +134,6 @@ static uint windowFor(uint id) {
if (!window) err(EX_OSERR, "malloc");
window->id = id;
window->pad = newpad(WindowLines, COLS);
if (!window->pad) err(EX_OSERR, "newpad");
scrollok(window->pad, true);
wmove(window->pad, WindowLines - 1, 0);
window->mark = true;
window->ignore = true;
window->buffer = bufferAlloc();
@ -149,7 +143,6 @@ static uint windowFor(uint id) {
static void windowFree(struct Window *window) {
bufferFree(window->buffer);
delwin(window->pad);
free(window);
}
@ -275,11 +268,8 @@ void uiInit(void) {
status = newwin(StatusLines, COLS, 0, 0);
if (!status) err(EX_OSERR, "newwin");
marker = newwin(
MarkerLines, COLS,
LINES - InputLines - SplitLines - MarkerLines, 0
);
wbkgd(marker, ACS_BULLET);
main = newwin(MAIN_LINES, COLS, StatusLines, 0);
if (!main) err(EX_OSERR, "newwin");
input = newpad(InputLines, InputCols);
if (!input) err(EX_OSERR, "newpad");
@ -299,30 +289,7 @@ static char prevTitle[sizeof(title)];
void uiDraw(void) {
if (hidden) return;
wnoutrefresh(status);
const struct Window *window = windows.ptrs[windows.show];
if (!window->scroll) {
pnoutrefresh(
window->pad,
WindowLines - MAIN_LINES, 0,
StatusLines, 0,
BOTTOM - InputLines, RIGHT
);
} else {
pnoutrefresh(
window->pad,
WindowLines - window->scroll - MAIN_LINES + MarkerLines, 0,
StatusLines, 0,
BOTTOM - InputLines - SplitLines - MarkerLines, RIGHT
);
touchwin(marker);
wnoutrefresh(marker);
pnoutrefresh(
window->pad,
WindowLines - SplitLines, 0,
LINES - InputLines - SplitLines, 0,
BOTTOM - InputLines, RIGHT
);
}
wnoutrefresh(main);
int y, x;
getyx(input, y, x);
pnoutrefresh(
@ -383,12 +350,12 @@ static short stylePair(struct Style style) {
return colorPair(Colors[style.fg], Colors[style.bg]);
}
static void statusAdd(const char *str) {
static void styleAdd(WINDOW *win, const char *str) {
struct Style style = StyleDefault;
while (*str) {
size_t len = styleParse(&style, &str);
wattr_set(status, styleAttr(style), stylePair(style), NULL);
waddnstr(status, str, len);
wattr_set(win, styleAttr(style), stylePair(style), NULL);
waddnstr(win, str, len);
str += len;
}
}
@ -431,7 +398,7 @@ static void statusUpdate(void) {
if (window->scroll) {
catf(&cat, "~%d ", window->scroll);
}
statusAdd(buf);
styleAdd(status, buf);
}
wclrtoeol(status);
@ -480,14 +447,35 @@ void uiHide(void) {
endwin();
}
static void windowUpdate(void) {
struct Window *window = windows.ptrs[windows.show];
int y = MAIN_LINES - 1;
for (size_t i = BufferCap - 1 - window->scroll; i < BufferCap; --i) {
const struct Line *line = bufferHard(window->buffer, i);
if (!line) continue;
if (line->heat < Cold && window->ignore) continue;
wmove(main, y, 0);
styleAdd(main, line->str);
wclrtoeol(main);
if (!y--) break;
}
while (y >= 0) {
wmove(main, y--, 0);
wclrtoeol(main);
}
}
static void windowScroll(struct Window *window, int n) {
mark(window);
window->scroll += n;
if (window->scroll > WindowLines - MAIN_LINES) {
window->scroll = WindowLines - MAIN_LINES;
if (window->scroll > BufferCap - MAIN_LINES) {
window->scroll = BufferCap - MAIN_LINES;
}
if (window->scroll < 0) window->scroll = 0;
unmark(window);
windowUpdate();
}
static void windowScrollPage(struct Window *window, int n) {
@ -499,72 +487,6 @@ static void windowScrollUnread(struct Window *window) {
windowScroll(window, window->unreadHard - MAIN_LINES);
}
static int wordWidth(const char *str) {
size_t len = strcspn(str, " \t");
int width = 0;
while (len) {
wchar_t wc;
int n = mbtowc(&wc, str, len);
if (n < 1) return width + len;
width += (iswprint(wc) ? wcwidth(wc) : 0);
str += n;
len -= n;
}
return width;
}
// XXX: ncurses likes to render zero-width characters as spaces...
static int waddnstrnzw(WINDOW *win, const char *str, int len) {
wchar_t wc;
while (len) {
int n = mbtowc(&wc, str, len);
if (n < 1) return waddnstr(win, str, len);
if (wcwidth(wc)) waddnstr(win, str, n);
str += n;
len -= n;
}
return OK;
}
static int wordWrap(WINDOW *win, const char *str) {
int y, x, width;
getmaxyx(win, y, width);
waddch(win, '\n');
int lines = 1;
int align = 0;
struct Style style = StyleDefault;
while (*str) {
char ch = *str;
if (ch == ' ' || ch == '\t') {
getyx(win, y, x);
const char *word = &str[strspn(str, " \t")];
if (width - x - 1 <= wordWidth(word)) {
lines += 1 + (align + wordWidth(word)) / width;
waddch(win, '\n');
getyx(win, y, x);
wmove(win, y, align);
str = word;
} else {
waddch(win, (align ? ch : ' '));
str++;
}
}
if (ch == '\t' && !align) {
getyx(win, y, align);
}
size_t len = styleParse(&style, &str);
size_t ws = strcspn(str, " \t");
if (ws < len) len = ws;
wattr_set(win, styleAttr(style), stylePair(style), NULL);
waddnstrnzw(win, str, len);
str += len;
}
return lines;
}
struct Util uiNotifyUtil;
static void notify(uint id, const char *str) {
if (!uiNotifyUtil.argc) return;
@ -596,23 +518,21 @@ static void notify(uint id, const char *str) {
void uiWrite(uint id, enum Heat heat, const time_t *src, const char *str) {
struct Window *window = windows.ptrs[windowFor(id)];
time_t ts = (src ? *src : time(NULL));
bufferPush(window->buffer, COLS, heat, ts, str);
int lines = bufferPush(window->buffer, COLS, heat, ts, str);
if (heat < Cold && window->ignore) return;
int lines = 0;
if (!window->unreadSoft++) window->unreadHard = 0;
if (window->mark && heat > Cold) {
if (!window->unreadWarm++) {
lines++;
waddch(window->pad, '\n');
lines += bufferBlank(window->buffer);
}
if (heat > window->heat) window->heat = heat;
statusUpdate();
}
lines += wordWrap(window->pad, str);
window->unreadHard += lines;
if (window->scroll) windowScroll(window, lines);
windowUpdate();
if (window->mark && heat > Warm) {
beep();
@ -632,45 +552,13 @@ void uiFormat(
uiWrite(id, heat, time, buf);
}
static void reflow(struct Window *window) {
werase(window->pad);
wmove(window->pad, 0, 0);
int flowed = 0;
window->unreadHard = 0;
for (size_t i = 0; i < BufferCap; ++i) {
const struct Line *line = bufferSoft(window->buffer, i);
if (!line) continue;
if (line->heat < Cold && window->ignore) continue;
int lines = 0;
if (i == (size_t)(BufferCap - window->unreadSoft)) {
waddch(window->pad, '\n');
lines++;
}
lines += wordWrap(window->pad, line->str);
if (i >= (size_t)(BufferCap - window->unreadSoft)) {
window->unreadHard += lines;
}
flowed += lines;
}
if (flowed < WindowLines) {
wscrl(window->pad, -(WindowLines - 1 - flowed));
wmove(window->pad, WindowLines - 1, RIGHT);
}
}
static void resize(void) {
mvwin(marker, LINES - InputLines - SplitLines - MarkerLines, 0);
int height, width;
getmaxyx(windows.ptrs[0]->pad, height, width);
if (width == COLS) return;
for (uint num = 0; num < windows.len; ++num) {
wresize(windows.ptrs[num]->pad, WindowLines, COLS);
reflow(windows.ptrs[num]);
}
(void)height;
statusUpdate();
wresize(main, MAIN_LINES, COLS);
for (uint num = 0; num < windows.len; ++num) {
bufferReflow(windows.ptrs[num]->buffer, COLS);
}
windowUpdate();
}
static void bufferList(const struct Buffer *buffer) {
@ -793,12 +681,12 @@ static void inputUpdate(void) {
}
static void windowShow(uint num) {
touchwin(windows.ptrs[num]->pad);
windows.swap = windows.show;
windows.show = num;
windows.user = num;
mark(windows.ptrs[windows.swap]);
unmark(windows.ptrs[windows.show]);
windowUpdate();
inputUpdate();
}
@ -830,6 +718,7 @@ static void windowClose(uint num) {
windows.swap = windows.show;
} else if (windows.show > num) {
windows.show--;
windowUpdate();
}
statusUpdate();
}
@ -844,7 +733,7 @@ void uiCloseNum(uint num) {
static void toggleIgnore(struct Window *window) {
window->ignore ^= true;
reflow(window);
windowUpdate();
statusUpdate();
}
@ -889,8 +778,8 @@ static void keyCode(int code) {
break; case KeyMetaMinus: toggleIgnore(window);
break; case KeyMetaSlash: windowShow(windows.swap);
break; case KeyMetaGt: windowScroll(window, -WindowLines);
break; case KeyMetaLt: windowScroll(window, +WindowLines);
break; case KeyMetaGt: windowScroll(window, -BufferCap);
break; case KeyMetaLt: windowScroll(window, +BufferCap);
break; case KeyMeta0 ... KeyMeta9: uiShowNum(code - KeyMeta0);
break; case KeyMetaA: showAuto();
@ -898,7 +787,7 @@ static void keyCode(int code) {
break; case KeyMetaD: edit(id, EditDeleteNextWord, 0);
break; case KeyMetaF: edit(id, EditNextWord, 0);
break; case KeyMetaL: bufferList(window->buffer);
break; case KeyMetaM: waddch(window->pad, '\n');
break; case KeyMetaM: bufferBlank(window->buffer); windowUpdate();
break; case KeyMetaQ: edit(id, EditCollapse, 0);
break; case KeyMetaU: windowScrollUnread(window);
break; case KeyMetaV: windowScrollPage(window, +1);
@ -913,8 +802,8 @@ static void keyCode(int code) {
break; case KEY_NPAGE: windowScrollPage(window, -1);
break; case KEY_PPAGE: windowScrollPage(window, +1);
break; case KEY_RIGHT: edit(id, EditNext, 0);
break; case KEY_SEND: windowScroll(window, -WindowLines);
break; case KEY_SHOME: windowScroll(window, +WindowLines);
break; case KEY_SEND: windowScroll(window, -BufferCap);
break; case KEY_SHOME: windowScroll(window, +BufferCap);
break; case KEY_UP: windowScroll(window, +1);
}
}
@ -1095,7 +984,6 @@ void uiLoad(const char *name) {
readString(file, &buf, &cap);
bufferPush(window->buffer, COLS, heat, time, buf);
}
reflow(window);
}
free(buf);