diff --git a/buffer.c b/buffer.c index 32eb1fb..8c6d6b3 100644 --- a/buffer.c +++ b/buffer.c @@ -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) { diff --git a/chat.h b/chat.h index d05121f..72b0246 100644 --- a/chat.h +++ b/chat.h @@ -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 { diff --git a/ui.c b/ui.c index 455d057..3ecea9f 100644 --- a/ui.c +++ b/ui.c @@ -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);