diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | view.c | 122 |
2 files changed, 126 insertions, 3 deletions
diff --git a/Makefile b/Makefile index e1fea15..9b8c704 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CHROOT_GROUP = ${CHROOT_USER} CFLAGS += -Wall -Wextra -Wpedantic LDFLAGS = -static -LDLIBS = -lutil +LDLIBS = -lcurses -lutil -include config.mk @@ -19,7 +19,10 @@ all: tags ${BINS} ingest: ingest.o term.o ${CC} ${LDFLAGS} ingest.o term.o ${LDLIBS} -o $@ -ingest.o term.o: stream.h +view: view.o term.o + ${CC} ${LDFLAGS} view.o term.o ${LDLIBS} -o $@ + +ingest.o term.o view.o: stream.h tags: *.c *.h ctags -w *.c *.h diff --git a/view.c b/view.c index 77a236d..a5861c4 100644 --- a/view.c +++ b/view.c @@ -14,11 +14,131 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _XOPEN_SOURCE_EXTENDED + +#include <curses.h> #include <err.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> #include <sysexits.h> +#include <unistd.h> + +#include "stream.h" + +#ifndef A_ITALIC +#define A_ITALIC A_UNDERLINE +#endif + +static short colorPair(int bg, int fg) { + if (bg >= COLORS || fg >= COLORS) return 0; + if (bg < 8 && fg < 8) { + return 9 * (bg + 1) + (fg + 1); + } + + enum { + Base = 9 * 9, + Ring = 256 - Base, + }; + static struct { + int bg, fg; + } pairs[Ring]; + static size_t len = 0; + + short i; + for (i = 0; i < Ring; ++i) { + if (pairs[i].bg == bg && pairs[i].fg == fg) return Base + i; + if (!pairs[i].bg && !pairs[i].fg) break; + } + + i = len++ % Ring; + pairs[i].bg = bg; + pairs[i].fg = fg; + init_pair(Base + i, fg, bg); + touchwin(stdscr); + return Base + i; +} + +static void curse(void) { + initscr(); + cbreak(); + noecho(); + start_color(); + use_default_colors(); + for (int bg = -1; bg < 8; ++bg) { + for (int fg = -1; fg < 8; ++fg) { + init_pair(colorPair(bg, fg), fg, bg); + } + } +} + +static attr_t styleAttr(struct Style style) { + attr_t attr = A_NORMAL; + if (style.attr & Bold) attr |= A_BOLD; + if (style.attr & Dim) attr |= A_DIM; + if (style.attr & Italic) attr |= A_ITALIC; + if (style.attr & Underline) attr |= A_UNDERLINE; + if (style.attr & Blink) attr |= A_BLINK; + if (style.attr & Reverse) attr |= A_REVERSE; + return attr; +} + +static void render(WINDOW *win, struct Display term) { + for (uint y = 0; y < term.rows; ++y) { + for (uint x = 0; x < term.cols; ++x) { + struct Cell cell = term.cells[term.cols * y + x]; + if (!cell.ch) continue; + wattr_set( + win, + styleAttr(cell.style), + colorPair(cell.style.bg, cell.style.fg), + NULL + ); + mvwaddnwstr(win, y, x, &cell.ch, 1); + } + } + curs_set(term.cursor); + leaveok(win, !term.cursor); + wmove(win, term.y, term.x); +} int main(void) { - // TODO: Connect to socket, emulate terminal. + int error; + setlocale(LC_CTYPE, ""); + + // TODO: Read info from file. + const char *path = "example.sock"; + termInit(24, 80); + + int client = socket(PF_LOCAL, SOCK_STREAM, 0); + if (client < 0) err(EX_OSERR, "socket"); + + struct sockaddr_un addr = { .sun_family = AF_LOCAL }; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + error = connect(client, (struct sockaddr *)&addr, SUN_LEN(&addr)); + if (error) err(EX_NOINPUT, "%s", path); + + curse(); + WINDOW *win = newwin(24, 80, 0, 0); + + for (;;) { + char buf[4096]; + ssize_t len = read(client, buf, sizeof(buf)); + if (len < 0) err(EX_IOERR, "read"); + + ssize_t pos = 0; + while (pos < len) { + wchar_t ch; + int n = mbtowc(&ch, &buf[pos], len - pos); + if (n <= 0) break; + termUpdate(ch); + pos += n; + } + + render(win, termDisplay()); + wrefresh(win); + } } |