From ded2b6afb602719a52c3b4934cec5327dc8d3d1a Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Tue, 1 Sep 2020 20:35:17 -0400 Subject: [PATCH] Factor buffer out of ui In preparation for doing line wrapping outside of ncurses. --- Makefile | 1 + README.7 | 4 ++- buffer.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ chat.h | 17 +++++++++ ui.c | 81 ++++++++++++++----------------------------- 5 files changed, 150 insertions(+), 56 deletions(-) create mode 100644 buffer.c diff --git a/Makefile b/Makefile index 08e90ab..2c3061d 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ LDLIBS = -lncursesw -ltls -include config.mk +OBJS += buffer.o OBJS += chat.o OBJS += command.o OBJS += complete.o diff --git a/README.7 b/README.7 index 6842fd3..aa05fac 100644 --- a/README.7 +++ b/README.7 @@ -1,4 +1,4 @@ -.Dd August 4, 2020 +.Dd September 1, 2020 .Dt README 7 .Os "Causal Agency" .\" To view this file, run: man ./README.7 @@ -154,6 +154,8 @@ curses interface IRC message handling .It Pa command.c input command handling +.It Pa buffer.c +line wrapping .It Pa edit.c line editing .It Pa complete.c diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..9eb117f --- /dev/null +++ b/buffer.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2020 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. + */ + +#include +#include +#include +#include +#include + +#include "chat.h" + +struct Lines { + size_t len; + struct Line lines[BufferCap]; +}; +_Static_assert(!(BufferCap & (BufferCap - 1)), "BufferCap is power of two"); + +struct Buffer { + struct Lines soft; + struct Lines hard; +}; + +struct Buffer *bufferAlloc(void) { + struct Buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) err(EX_OSERR, "calloc"); + return buffer; +} + +void bufferFree(struct Buffer *buffer) { + for (size_t i = 0; i < BufferCap; ++i) { + free(buffer->soft.lines[i].str); + free(buffer->hard.lines[i].str); + } + free(buffer); +} + +static const struct Line *linesLine(const struct Lines *lines, size_t i) { + const struct Line *line = &lines->lines[(lines->len + i) % BufferCap]; + return (line->str ? line : NULL); +} + +const struct Line *bufferSoft(const struct Buffer *buffer, size_t i) { + return linesLine(&buffer->soft, i); +} + +const struct Line *bufferHard(const struct Buffer *buffer, size_t i) { + return linesLine(&buffer->hard, i); +} + +static void flow(struct Lines *hard, int cols, const struct Line *soft) { + (void)hard; + (void)cols; + (void)soft; +} + +void bufferPush( + struct Buffer *buffer, int cols, + enum Heat heat, time_t time, const char *str +) { + struct Line *soft = &buffer->soft.lines[buffer->soft.len++ % BufferCap]; + free(soft->str); + soft->heat = heat; + soft->time = time; + soft->str = strdup(str); + if (!soft->str) err(EX_OSERR, "strdup"); + flow(&buffer->hard, cols, soft); +} + +void bufferReflow(struct Buffer *buffer, int cols) { + buffer->hard.len = 0; + for (size_t i = 0; i < BufferCap; ++i) { + free(buffer->hard.lines[i].str); + buffer->hard.lines[i].str = NULL; + } + for (size_t i = 0; i < BufferCap; ++i) { + const struct Line *soft = bufferSoft(buffer, i); + if (soft) flow(&buffer->hard, cols, soft); + } +} diff --git a/chat.h b/chat.h index 3668bcd..d53c7fe 100644 --- a/chat.h +++ b/chat.h @@ -276,6 +276,23 @@ void uiFormat( void uiLoad(const char *name); int uiSave(const char *name); +enum { BufferCap = 1024 }; +struct Buffer; +struct Line { + enum Heat heat; + time_t time; + char *str; +}; +struct Buffer *bufferAlloc(void); +void bufferFree(struct Buffer *buffer); +const struct Line *bufferSoft(const struct Buffer *buffer, size_t i); +const struct Line *bufferHard(const struct Buffer *buffer, size_t i); +void bufferPush( + struct Buffer *buffer, int cols, + enum Heat heat, time_t time, const char *str +); +void bufferReflow(struct Buffer *buffer, int cols); + enum Edit { EditHead, EditTail, diff --git a/ui.c b/ui.c index d1f2ea6..ba4ce67 100644 --- a/ui.c +++ b/ui.c @@ -74,34 +74,6 @@ static WINDOW *status; static WINDOW *marker; static WINDOW *input; -struct Line { - enum Heat heat; - time_t time; - char *str; -}; - -enum { BufferCap = 1024 }; -struct Buffer { - size_t len; - struct Line lines[BufferCap]; -}; -_Static_assert(!(BufferCap & (BufferCap - 1)), "BufferCap is power of two"); - -static void bufferPush( - struct Buffer *buffer, enum Heat heat, time_t time, const char *str -) { - struct Line *line = &buffer->lines[buffer->len++ % BufferCap]; - free(line->str); - line->heat = heat; - line->time = time; - line->str = strdup(str); - if (!line->str) err(EX_OSERR, "strdup"); -} - -static struct Line bufferLine(const struct Buffer *buffer, size_t i) { - return buffer->lines[(buffer->len + i) % BufferCap]; -} - struct Window { uint id; WINDOW *pad; @@ -113,7 +85,7 @@ struct Window { uint unreadHard; uint unreadSoft; uint unreadWarm; - struct Buffer buffer; + struct Buffer *buffer; }; static struct { @@ -170,14 +142,13 @@ static uint windowFor(uint id) { wmove(window->pad, WindowLines - 1, 0); window->mark = true; window->ignore = true; + window->buffer = bufferAlloc(); return windowPush(window); } static void windowFree(struct Window *window) { - for (size_t i = 0; i < BufferCap; ++i) { - free(window->buffer.lines[i].str); - } + bufferFree(window->buffer); delwin(window->pad); free(window); } @@ -625,7 +596,7 @@ 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, heat, ts, str); + bufferPush(window->buffer, COLS, heat, ts, str); if (heat < Cold && window->ignore) return; int lines = 0; @@ -668,15 +639,15 @@ static void reflow(struct Window *window) { int flowed = 0; window->unreadSoft = 0; for (size_t i = 0; i < BufferCap; ++i) { - struct Line line = bufferLine(&window->buffer, i); - if (!line.str) continue; - if (line.heat < Cold && window->ignore) continue; + 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->unreadHard)) { waddch(window->pad, '\n'); lines++; } - lines += wordWrap(window->pad, line.str); + lines += wordWrap(window->pad, line->str); if (i >= (size_t)(BufferCap - window->unreadHard)) { window->unreadSoft += lines; } @@ -695,7 +666,7 @@ static void resize(void) { getmaxyx(windows.ptrs[0]->pad, height, width); if (width == COLS) return; for (uint num = 0; num < windows.len; ++num) { - wresize(windows.ptrs[num]->pad, BufferCap, COLS); + wresize(windows.ptrs[num]->pad, WindowLines, COLS); reflow(windows.ptrs[num]); } (void)height; @@ -707,10 +678,10 @@ static void bufferList(const struct Buffer *buffer) { waiting = true; for (size_t i = 0; i < BufferCap; ++i) { - struct Line line = bufferLine(buffer, i); - if (!line.str) continue; + const struct Line *line = bufferSoft(buffer, i); + if (!line) continue; - struct tm *tm = localtime(&line.time); + struct tm *tm = localtime(&line->time); if (!tm) err(EX_OSERR, "localtime"); char buf[sizeof("00:00:00")]; @@ -720,20 +691,20 @@ static void bufferList(const struct Buffer *buffer) { bool align = false; struct Style style = StyleDefault; - while (*line.str) { - if (*line.str == '\t') { + for (const char *str = line->str; *str;) { + if (*str == '\t') { printf("%c", (align ? '\t' : ' ')); align = true; - line.str++; + str++; } - size_t len = styleParse(&style, (const char **)&line.str); - size_t tab = strcspn(line.str, "\t"); + size_t len = styleParse(&style, &str); + size_t tab = strcspn(str, "\t"); if (tab < len) len = tab; vid_attr(styleAttr(style), stylePair(style), NULL); - printf("%.*s", (int)len, line.str); - line.str += len; + printf("%.*s", (int)len, str); + str += len; } printf("\n"); } @@ -926,7 +897,7 @@ static void keyCode(int code) { break; case KeyMetaB: edit(id, EditPrevWord, 0); break; case KeyMetaD: edit(id, EditDeleteNextWord, 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 KeyMetaQ: edit(id, EditCollapse, 0); break; case KeyMetaU: windowScrollUnread(window); @@ -1060,11 +1031,11 @@ int uiSave(const char *name) { if (writeTime(file, window->unreadHard)) return -1; if (writeTime(file, window->unreadWarm)) return -1; for (size_t i = 0; i < BufferCap; ++i) { - struct Line line = bufferLine(&window->buffer, i); - if (!line.str) continue; - if (writeTime(file, line.time)) return -1; - if (writeTime(file, line.heat)) return -1; - if (writeString(file, line.str)) return -1; + const struct Line *line = bufferSoft(window->buffer, i); + if (!line) continue; + if (writeTime(file, line->time)) return -1; + if (writeTime(file, line->heat)) return -1; + if (writeString(file, line->str)) return -1; } if (writeTime(file, 0)) return -1; } @@ -1122,7 +1093,7 @@ void uiLoad(const char *name) { if (!time) break; enum Heat heat = (version > 2 ? readTime(file) : Cold); readString(file, &buf, &cap); - bufferPush(&window->buffer, heat, time, buf); + bufferPush(window->buffer, COLS, heat, time, buf); } reflow(window); }