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
parent
4dc87ab9cd
commit
149cafc5ab
5
buffer.c
5
buffer.c
|
@ -178,6 +178,11 @@ int bufferPush(
|
||||||
return flow(&buffer->hard, cols, soft);
|
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) {
|
void bufferReflow(struct Buffer *buffer, int cols) {
|
||||||
buffer->hard.len = 0;
|
buffer->hard.len = 0;
|
||||||
for (size_t i = 0; i < BufferCap; ++i) {
|
for (size_t i = 0; i < BufferCap; ++i) {
|
||||||
|
|
1
chat.h
1
chat.h
|
@ -291,6 +291,7 @@ int bufferPush(
|
||||||
struct Buffer *buffer, int cols,
|
struct Buffer *buffer, int cols,
|
||||||
enum Heat heat, time_t time, const char *str
|
enum Heat heat, time_t time, const char *str
|
||||||
);
|
);
|
||||||
|
int bufferBlank(struct Buffer *buffer);
|
||||||
void bufferReflow(struct Buffer *buffer, int cols);
|
void bufferReflow(struct Buffer *buffer, int cols);
|
||||||
|
|
||||||
enum Edit {
|
enum Edit {
|
||||||
|
|
208
ui.c
208
ui.c
|
@ -59,7 +59,6 @@
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
StatusLines = 1,
|
StatusLines = 1,
|
||||||
WindowLines = 1024,
|
|
||||||
MarkerLines = 1,
|
MarkerLines = 1,
|
||||||
SplitLines = 5,
|
SplitLines = 5,
|
||||||
InputLines = 1,
|
InputLines = 1,
|
||||||
|
@ -71,12 +70,11 @@ enum {
|
||||||
#define MAIN_LINES (LINES - StatusLines - InputLines)
|
#define MAIN_LINES (LINES - StatusLines - InputLines)
|
||||||
|
|
||||||
static WINDOW *status;
|
static WINDOW *status;
|
||||||
static WINDOW *marker;
|
static WINDOW *main;
|
||||||
static WINDOW *input;
|
static WINDOW *input;
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
uint id;
|
uint id;
|
||||||
WINDOW *pad;
|
|
||||||
int scroll;
|
int scroll;
|
||||||
bool mark;
|
bool mark;
|
||||||
bool mute;
|
bool mute;
|
||||||
|
@ -136,10 +134,6 @@ static uint windowFor(uint id) {
|
||||||
if (!window) err(EX_OSERR, "malloc");
|
if (!window) err(EX_OSERR, "malloc");
|
||||||
|
|
||||||
window->id = id;
|
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->mark = true;
|
||||||
window->ignore = true;
|
window->ignore = true;
|
||||||
window->buffer = bufferAlloc();
|
window->buffer = bufferAlloc();
|
||||||
|
@ -149,7 +143,6 @@ static uint windowFor(uint id) {
|
||||||
|
|
||||||
static void windowFree(struct Window *window) {
|
static void windowFree(struct Window *window) {
|
||||||
bufferFree(window->buffer);
|
bufferFree(window->buffer);
|
||||||
delwin(window->pad);
|
|
||||||
free(window);
|
free(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,11 +268,8 @@ void uiInit(void) {
|
||||||
status = newwin(StatusLines, COLS, 0, 0);
|
status = newwin(StatusLines, COLS, 0, 0);
|
||||||
if (!status) err(EX_OSERR, "newwin");
|
if (!status) err(EX_OSERR, "newwin");
|
||||||
|
|
||||||
marker = newwin(
|
main = newwin(MAIN_LINES, COLS, StatusLines, 0);
|
||||||
MarkerLines, COLS,
|
if (!main) err(EX_OSERR, "newwin");
|
||||||
LINES - InputLines - SplitLines - MarkerLines, 0
|
|
||||||
);
|
|
||||||
wbkgd(marker, ACS_BULLET);
|
|
||||||
|
|
||||||
input = newpad(InputLines, InputCols);
|
input = newpad(InputLines, InputCols);
|
||||||
if (!input) err(EX_OSERR, "newpad");
|
if (!input) err(EX_OSERR, "newpad");
|
||||||
|
@ -299,30 +289,7 @@ static char prevTitle[sizeof(title)];
|
||||||
void uiDraw(void) {
|
void uiDraw(void) {
|
||||||
if (hidden) return;
|
if (hidden) return;
|
||||||
wnoutrefresh(status);
|
wnoutrefresh(status);
|
||||||
const struct Window *window = windows.ptrs[windows.show];
|
wnoutrefresh(main);
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
int y, x;
|
int y, x;
|
||||||
getyx(input, y, x);
|
getyx(input, y, x);
|
||||||
pnoutrefresh(
|
pnoutrefresh(
|
||||||
|
@ -383,12 +350,12 @@ static short stylePair(struct Style style) {
|
||||||
return colorPair(Colors[style.fg], Colors[style.bg]);
|
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;
|
struct Style style = StyleDefault;
|
||||||
while (*str) {
|
while (*str) {
|
||||||
size_t len = styleParse(&style, &str);
|
size_t len = styleParse(&style, &str);
|
||||||
wattr_set(status, styleAttr(style), stylePair(style), NULL);
|
wattr_set(win, styleAttr(style), stylePair(style), NULL);
|
||||||
waddnstr(status, str, len);
|
waddnstr(win, str, len);
|
||||||
str += len;
|
str += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +398,7 @@ static void statusUpdate(void) {
|
||||||
if (window->scroll) {
|
if (window->scroll) {
|
||||||
catf(&cat, "~%d ", window->scroll);
|
catf(&cat, "~%d ", window->scroll);
|
||||||
}
|
}
|
||||||
statusAdd(buf);
|
styleAdd(status, buf);
|
||||||
}
|
}
|
||||||
wclrtoeol(status);
|
wclrtoeol(status);
|
||||||
|
|
||||||
|
@ -480,14 +447,35 @@ void uiHide(void) {
|
||||||
endwin();
|
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) {
|
static void windowScroll(struct Window *window, int n) {
|
||||||
mark(window);
|
mark(window);
|
||||||
window->scroll += n;
|
window->scroll += n;
|
||||||
if (window->scroll > WindowLines - MAIN_LINES) {
|
if (window->scroll > BufferCap - MAIN_LINES) {
|
||||||
window->scroll = WindowLines - MAIN_LINES;
|
window->scroll = BufferCap - MAIN_LINES;
|
||||||
}
|
}
|
||||||
if (window->scroll < 0) window->scroll = 0;
|
if (window->scroll < 0) window->scroll = 0;
|
||||||
unmark(window);
|
unmark(window);
|
||||||
|
windowUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void windowScrollPage(struct Window *window, int n) {
|
static void windowScrollPage(struct Window *window, int n) {
|
||||||
|
@ -499,72 +487,6 @@ static void windowScrollUnread(struct Window *window) {
|
||||||
windowScroll(window, window->unreadHard - MAIN_LINES);
|
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;
|
struct Util uiNotifyUtil;
|
||||||
static void notify(uint id, const char *str) {
|
static void notify(uint id, const char *str) {
|
||||||
if (!uiNotifyUtil.argc) return;
|
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) {
|
void uiWrite(uint id, enum Heat heat, const time_t *src, const char *str) {
|
||||||
struct Window *window = windows.ptrs[windowFor(id)];
|
struct Window *window = windows.ptrs[windowFor(id)];
|
||||||
time_t ts = (src ? *src : time(NULL));
|
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;
|
if (heat < Cold && window->ignore) return;
|
||||||
|
|
||||||
int lines = 0;
|
|
||||||
if (!window->unreadSoft++) window->unreadHard = 0;
|
if (!window->unreadSoft++) window->unreadHard = 0;
|
||||||
if (window->mark && heat > Cold) {
|
if (window->mark && heat > Cold) {
|
||||||
if (!window->unreadWarm++) {
|
if (!window->unreadWarm++) {
|
||||||
lines++;
|
lines += bufferBlank(window->buffer);
|
||||||
waddch(window->pad, '\n');
|
|
||||||
}
|
}
|
||||||
if (heat > window->heat) window->heat = heat;
|
if (heat > window->heat) window->heat = heat;
|
||||||
statusUpdate();
|
statusUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
lines += wordWrap(window->pad, str);
|
|
||||||
window->unreadHard += lines;
|
window->unreadHard += lines;
|
||||||
if (window->scroll) windowScroll(window, lines);
|
if (window->scroll) windowScroll(window, lines);
|
||||||
|
windowUpdate();
|
||||||
|
|
||||||
if (window->mark && heat > Warm) {
|
if (window->mark && heat > Warm) {
|
||||||
beep();
|
beep();
|
||||||
|
@ -632,45 +552,13 @@ void uiFormat(
|
||||||
uiWrite(id, heat, time, buf);
|
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) {
|
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();
|
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) {
|
static void bufferList(const struct Buffer *buffer) {
|
||||||
|
@ -793,12 +681,12 @@ static void inputUpdate(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void windowShow(uint num) {
|
static void windowShow(uint num) {
|
||||||
touchwin(windows.ptrs[num]->pad);
|
|
||||||
windows.swap = windows.show;
|
windows.swap = windows.show;
|
||||||
windows.show = num;
|
windows.show = num;
|
||||||
windows.user = num;
|
windows.user = num;
|
||||||
mark(windows.ptrs[windows.swap]);
|
mark(windows.ptrs[windows.swap]);
|
||||||
unmark(windows.ptrs[windows.show]);
|
unmark(windows.ptrs[windows.show]);
|
||||||
|
windowUpdate();
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,6 +718,7 @@ static void windowClose(uint num) {
|
||||||
windows.swap = windows.show;
|
windows.swap = windows.show;
|
||||||
} else if (windows.show > num) {
|
} else if (windows.show > num) {
|
||||||
windows.show--;
|
windows.show--;
|
||||||
|
windowUpdate();
|
||||||
}
|
}
|
||||||
statusUpdate();
|
statusUpdate();
|
||||||
}
|
}
|
||||||
|
@ -844,7 +733,7 @@ void uiCloseNum(uint num) {
|
||||||
|
|
||||||
static void toggleIgnore(struct Window *window) {
|
static void toggleIgnore(struct Window *window) {
|
||||||
window->ignore ^= true;
|
window->ignore ^= true;
|
||||||
reflow(window);
|
windowUpdate();
|
||||||
statusUpdate();
|
statusUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -889,8 +778,8 @@ static void keyCode(int code) {
|
||||||
break; case KeyMetaMinus: toggleIgnore(window);
|
break; case KeyMetaMinus: toggleIgnore(window);
|
||||||
break; case KeyMetaSlash: windowShow(windows.swap);
|
break; case KeyMetaSlash: windowShow(windows.swap);
|
||||||
|
|
||||||
break; case KeyMetaGt: windowScroll(window, -WindowLines);
|
break; case KeyMetaGt: windowScroll(window, -BufferCap);
|
||||||
break; case KeyMetaLt: windowScroll(window, +WindowLines);
|
break; case KeyMetaLt: windowScroll(window, +BufferCap);
|
||||||
|
|
||||||
break; case KeyMeta0 ... KeyMeta9: uiShowNum(code - KeyMeta0);
|
break; case KeyMeta0 ... KeyMeta9: uiShowNum(code - KeyMeta0);
|
||||||
break; case KeyMetaA: showAuto();
|
break; case KeyMetaA: showAuto();
|
||||||
|
@ -898,7 +787,7 @@ static void keyCode(int code) {
|
||||||
break; case KeyMetaD: edit(id, EditDeleteNextWord, 0);
|
break; case KeyMetaD: edit(id, EditDeleteNextWord, 0);
|
||||||
break; case KeyMetaF: edit(id, EditNextWord, 0);
|
break; case KeyMetaF: edit(id, EditNextWord, 0);
|
||||||
break; case KeyMetaL: bufferList(window->buffer);
|
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 KeyMetaQ: edit(id, EditCollapse, 0);
|
||||||
break; case KeyMetaU: windowScrollUnread(window);
|
break; case KeyMetaU: windowScrollUnread(window);
|
||||||
break; case KeyMetaV: windowScrollPage(window, +1);
|
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_NPAGE: windowScrollPage(window, -1);
|
||||||
break; case KEY_PPAGE: windowScrollPage(window, +1);
|
break; case KEY_PPAGE: windowScrollPage(window, +1);
|
||||||
break; case KEY_RIGHT: edit(id, EditNext, 0);
|
break; case KEY_RIGHT: edit(id, EditNext, 0);
|
||||||
break; case KEY_SEND: windowScroll(window, -WindowLines);
|
break; case KEY_SEND: windowScroll(window, -BufferCap);
|
||||||
break; case KEY_SHOME: windowScroll(window, +WindowLines);
|
break; case KEY_SHOME: windowScroll(window, +BufferCap);
|
||||||
break; case KEY_UP: windowScroll(window, +1);
|
break; case KEY_UP: windowScroll(window, +1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1095,7 +984,6 @@ void uiLoad(const char *name) {
|
||||||
readString(file, &buf, &cap);
|
readString(file, &buf, &cap);
|
||||||
bufferPush(window->buffer, COLS, heat, time, buf);
|
bufferPush(window->buffer, COLS, heat, time, buf);
|
||||||
}
|
}
|
||||||
reflow(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
Loading…
Reference in New Issue