summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--view.c122
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);
+	}
 }