summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-08-01 16:46:54 -0400
committerJune McEnroe <june@causal.agency>2019-08-01 16:46:54 -0400
commitb33d011b79b777c0d51a59b13a4229839a6a77dd (patch)
tree9b8789e53288fbbb9ddddc3188360a06e381c1ed
parentFactor out SGR handling (diff)
downloadstream-b33d011b79b777c0d51a59b13a4229839a6a77dd.tar.gz
stream-b33d011b79b777c0d51a59b13a4229839a6a77dd.zip
Implement termSnapshot
-rw-r--r--ingest.c6
-rw-r--r--stream.h3
-rw-r--r--term.c109
3 files changed, 111 insertions, 7 deletions
diff --git a/ingest.c b/ingest.c
index 1dfae36..67328a8 100644
--- a/ingest.c
+++ b/ingest.c
@@ -107,7 +107,11 @@ int main(void) {
 				continue;
 			}
 
-			// TODO: Send snapshot.
+			error = termSnapshot(client);
+			if (error) {
+				close(client);
+				continue;
+			}
 
 			maxClient++;
 			assert(client == maxClient);
diff --git a/stream.h b/stream.h
index d03d30d..56889c2 100644
--- a/stream.h
+++ b/stream.h
@@ -14,7 +14,10 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <wchar.h>
+
 typedef unsigned uint;
 
 void termInit(uint rows, uint cols);
 void termUpdate(wchar_t ch);
+int termSnapshot(int fd);
diff --git a/term.c b/term.c
index 6fb07da..a79a084 100644
--- a/term.c
+++ b/term.c
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sysexits.h>
+#include <unistd.h>
 #include <wchar.h>
 
 #include "stream.h"
@@ -36,8 +37,9 @@ enum {
 
 struct Style {
 	bool bold, italic, underline, reverse;
-	uint bg, fg;
+	int bg, fg;
 };
+static const struct Style Default = { .bg = -1, .fg = -1 };
 
 struct Cell {
 	struct Style style;
@@ -52,7 +54,7 @@ static struct {
 	uint top, bot;
 } scroll;
 
-static struct Style style = { .bg = -1, .fg = -1 };
+static struct Style style;
 static struct Cell *cells;
 
 static struct Cell *cell(uint y, uint x) {
@@ -146,7 +148,7 @@ static char updateESC(wchar_t ch) {
 }
 
 enum {
-	Default,
+	Reset,
 	Bold,
 	Italic = 3,
 	Underline,
@@ -173,7 +175,7 @@ enum {
 static void updateSGR(uint ps[], uint n) {
 	for (uint i = 0; i < n; ++i) {
 		switch (ps[i]) {
-			break; case Default: style = (struct Style) { .bg = -1, .fg = -1 };
+			break; case Reset: style = Default;
 
 			break; case Bold:      style.bold = true;
 			break; case Italic:    style.italic = true;
@@ -235,6 +237,10 @@ enum {
 	DECSTBM = 'r',
 };
 
+enum {
+	Insert = 4,
+};
+
 static char updateCSI(wchar_t ch) {
 	static bool dec;
 	if (ch == DEC) {
@@ -300,14 +306,14 @@ static char updateCSI(wchar_t ch) {
 		break; case SM: {
 			if (dec) break;
 			switch (ps[0]) {
-				break; case 4: insert = true;
+				break; case Insert: insert = true;
 				break; default: warnx("unhandled SM %u", ps[0]);
 			}
 		}
 		break; case RM: {
 			if (dec) break;
 			switch (ps[0]) {
-				break; case 4: insert = false;
+				break; case Insert: insert = false;
 				break; default: warnx("unhandled RM %u", ps[0]);
 			}
 		}
@@ -359,6 +365,97 @@ void termInit(uint _rows, uint _cols) {
 	cells = calloc(rows * cols, sizeof(*cells));
 	if (!cells) err(EX_OSERR, "calloc");
 
+	style = Default;
 	clear(cell(0, 0), cell(rows - 1, cols - 1));
 	scroll.bot = rows - 1;
 }
+
+static int
+styleDiff(FILE *file, const struct Style *prev, const struct Style *next) {
+	if (!memcmp(prev, next, sizeof(*prev))) return 0;
+	if (!memcmp(next, &Default, sizeof(*next))) {
+		return fprintf(file, "%c%c%c", ESC, CSI, SGR);
+	}
+	uint ps[10];
+	uint n = 0;
+	if (next->bold != prev->bold) {
+		ps[n++] = (next->bold ? Bold : NotBold);
+	}
+	if (next->italic != prev->italic) {
+		ps[n++] = (next->italic ? Italic : NotItalic);
+	}
+	if (next->underline != prev->underline) {
+		ps[n++] = (next->underline ? Underline : NotUnderline);
+	}
+	if (next->reverse != prev->reverse) {
+		ps[n++] = (next->reverse ? Reverse : NotReverse);
+	}
+	if (next->bg != prev->bg) {
+		if (next->bg == -1) {
+			ps[n++] = BgDefault;
+		} else if (next->bg < 8) {
+			ps[n++] = Bg0 + next->bg;
+		} else if (next->bg < 16) {
+			ps[n++] = Bg8 + next->bg - 8;
+		} else {
+			ps[n++] = Bg;
+			ps[n++] = Color256;
+			ps[n++] = next->bg;
+		}
+	}
+	if (next->fg != prev->fg) {
+		if (next->fg == -1) {
+			ps[n++] = FgDefault;
+		} else if (next->fg < 8) {
+			ps[n++] = Fg0 + next->fg;
+		} else if (next->fg < 16) {
+			ps[n++] = Fg8 + next->fg - 8;
+		} else {
+			ps[n++] = Fg;
+			ps[n++] = Color256;
+			ps[n++] = next->fg;
+		}
+	}
+	if (0 > fprintf(file, "%c%c%u", ESC, CSI, ps[0])) return -1;
+	for (uint i = 1; i < n; ++i) {
+		if (0 > fprintf(file, ";%u", ps[i])) return -1;
+	}
+	return fprintf(file, "%c", SGR);
+}
+
+int termSnapshot(int _fd) {
+	int fd = dup(_fd);
+	if (fd < 0) return -1;
+
+	FILE *file = fdopen(fd, "w");
+	if (!file) return -1;
+
+	struct Style prev = Default;
+	for (uint y = 0; y < rows; ++y) {
+		if (y && 0 > fprintf(file, "\r\n")) goto fail;
+		for (uint x = 0; x < cols; ++x) {
+			if (!cell(y, x)->ch) continue;
+			if (0 > styleDiff(file, &prev, &cell(y, x)->style)) goto fail;
+			if (0 > fprintf(file, "%lc", cell(y, x)->ch)) goto fail;
+			prev = cell(y, x)->style;
+		}
+	}
+	if (0 > styleDiff(file, &prev, &style)) goto fail;
+
+	int n = fprintf(
+		file,
+		"%c%c%d%c"
+		"%c%c%u;%u%c"
+		"%c%c%u;%u%c",
+		ESC, CSI, Insert, (insert ? SM : RM),
+		ESC, CSI, scroll.top, scroll.bot, DECSTBM,
+		ESC, CSI, y, x, CUP
+	);
+	if (n < 0) goto fail;
+
+	return fclose(file);
+
+fail:
+	fclose(file);
+	return -1;
+}