From cb04eada02bedc5b748e01b09a899dc139c4970f Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Mon, 8 Aug 2022 20:18:56 +0200
Subject: user/lib: shared libdraw for framebuffer handling

---
 src/user/app/dvd/dvd.c     | 117 ++++-----------------------------------------
 src/user/app/vterm/draw.c  |  58 ----------------------
 src/user/app/vterm/font.c  |   3 +-
 src/user/app/vterm/vterm.c |  49 ++-----------------
 src/user/app/vterm/vterm.h |  13 +----
 src/user/lib/draw/draw.c   |  50 +++++++++++++++++++
 src/user/lib/draw/draw.h   |  19 ++++++++
 src/user/lib/draw/flush.c  |  43 +++++++++++++++++
 8 files changed, 129 insertions(+), 223 deletions(-)
 create mode 100644 src/user/lib/draw/draw.c
 create mode 100644 src/user/lib/draw/draw.h
 create mode 100644 src/user/lib/draw/flush.c

diff --git a/src/user/app/dvd/dvd.c b/src/user/app/dvd/dvd.c
index 1b85fb4..ed9778c 100644
--- a/src/user/app/dvd/dvd.c
+++ b/src/user/app/dvd/dvd.c
@@ -4,129 +4,28 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <user/lib/draw/draw.h>
 
 #define eprintf(fmt, ...) fprintf(stderr, "vterm: "fmt"\n" __VA_OPT__(,) __VA_ARGS__)
 
-struct framebuf {
-	size_t len, width, height, pitch;
-	uint8_t bpp;
-	char *b;
-};
-
-struct rect {
-	uint32_t x1, y1, x2, y2;
-};
-
 struct framebuf fb;
-handle_t fb_fd;
 struct rect dirty;
 
-
-void dirty_mark(uint32_t x, uint32_t y) {
-	if (dirty.x1 > x) dirty.x1 = x;
-	if (dirty.x2 < x) dirty.x2 = x;
-	if (dirty.y1 > y) dirty.y1 = y;
-	if (dirty.y2 < y) dirty.y2 = y;
-}
-
-static void flush_combined(struct rect pix) {
-	size_t low  = fb.pitch * pix.y1 + 4 * pix.x1;
-	size_t high = fb.pitch * pix.y2 + 4 * pix.y2 + 4;
-
-	_syscall_write(fb_fd, fb.b + low, high - low, low, 0);
-}
-
-static void flush_split(struct rect pix) {
-	static uint64_t execbuf[EXECBUF_MAX_LEN / sizeof(uint64_t)];
-	if (7 * (pix.y2 - pix.y1) * sizeof(uint64_t) >= sizeof execbuf) {
-		flush_combined(pix);
-		return;
-	}
-
-	size_t epos = 0;
-	for (uint32_t y = pix.y1; y < pix.y2; y++) {
-		size_t low  = fb.pitch * y + 4 * pix.x1;
-		size_t high = fb.pitch * y + 4 * pix.x2 + 4;
-
-		execbuf[epos++] = EXECBUF_SYSCALL;
-		execbuf[epos++] = _SYSCALL_WRITE;
-		execbuf[epos++] = fb_fd;
-		execbuf[epos++] = fb.b + low;
-		execbuf[epos++] = high - low;
-		execbuf[epos++] = low;
-		execbuf[epos++] = 0;
-	}
-	_syscall_execbuf(execbuf, epos * sizeof(uint64_t));
-}
-
-void flush(void) {
-	if (~dirty.x1 == 0) return;
-
-	if (dirty.x2 >= fb.width)  dirty.x2 = fb.width - 1;
-	if (dirty.y2 >= fb.height) dirty.y2 = fb.height - 1;
-
-	/* the threshold is mostly arbitrary, wasn't based on any real benchmarks */
-	if (dirty.x2 - dirty.x1 > fb.width - 600) flush_combined(dirty);
-	else flush_split(dirty);
-
-	dirty.x1 = ~0; dirty.y1 = ~0;
-	dirty.x2 =  0; dirty.y2 =  0;
-}
-
 void draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t col) {
 	for (uint32_t i = 0; i < w; i++) {
 		for (uint32_t j = 0; j < h; j++) {
 			*((uint32_t*)(fb.b + fb.pitch * (y+j) + 4 * (x+i))) = col;
 		}
 	}
-	if (dirty.x1 > x) dirty.x1 = x;
-	if (dirty.y1 > y) dirty.y1 = y;
-	if (dirty.x2 < x + w) dirty.x2 = x + w;
-	if (dirty.y2 < y + h) dirty.y2 = y + h;
-}
-
-void fb_setup(void) {
-	#define BASEPATH "/kdev/video/"
-	char path[64], *spec;
-	size_t pos;
-	FILE *f;
-
-	f = fopen(BASEPATH, "r");
-	if (!f) {
-		eprintf("couldn't open %s", BASEPATH);
-		exit(1);
-	}
-
-	pos = strlen(BASEPATH);
-	memcpy(path, BASEPATH, pos);
-	spec = path + pos;
-	fread(spec, 1, sizeof(path) - pos, f);
-	/* assumes the read went correctly */
-	fclose(f);
-
-	fb_fd = _syscall_open(path, strlen(path), 0);
-	if (fb_fd < 0) {
-		eprintf("failed to open %s", path);
-		exit(1);
-	}
-
-	fb.width  = strtol(spec, &spec, 0);
-	if (*spec++ != 'x') { eprintf("bad filename format"); exit(1); }
-	fb.height = strtol(spec, &spec, 0);
-	if (*spec++ != 'x') { eprintf("bad filename format"); exit(1); }
-	fb.bpp = strtol(spec, &spec, 0);
-	fb.len = _syscall_getsize(fb_fd);
-	fb.pitch = fb.len / fb.height;
-	fb.b = malloc(fb.len);
-
-	if (fb.bpp != 32) {
-		eprintf("unsupported format %ux%ux%u", fb.width, fb.height, fb.bpp);
-		exit(1);
-	}
+	dirty_mark(&dirty, x, y);
+	dirty_mark(&dirty, x + w, y + h);
 }
 
 int main(void) {
-	fb_setup();
+	if (fb_setup(&fb, "/kdev/video/") < 0) {
+		eprintf("fb_setup error");
+		return 1;
+	}
 	int dx = 2, dy = 2, x = 100, y = 100, w = 150, h = 70;
 	uint32_t col = 0x800000;
 
@@ -136,7 +35,7 @@ int main(void) {
 		x += dx;
 		y += dy;
 		draw_rect(x, y, w, h, col++);
-		flush();
+		dirty_flush(&dirty, &fb);
 		_syscall_sleep(1000 / 60);
 	}
 
diff --git a/src/user/app/vterm/draw.c b/src/user/app/vterm/draw.c
index 1a40ede..50c56ed 100644
--- a/src/user/app/vterm/draw.c
+++ b/src/user/app/vterm/draw.c
@@ -4,66 +4,8 @@
 #include <string.h>
 
 struct framebuf fb;
-handle_t fb_fd;
 struct rect dirty;
 
-
-void dirty_mark(uint32_t x, uint32_t y) {
-	if (dirty.x1 > x) dirty.x1 = x;
-	if (dirty.x2 < x) dirty.x2 = x;
-	if (dirty.y1 > y) dirty.y1 = y;
-	if (dirty.y2 < y) dirty.y2 = y;
-}
-
-static void flush_combined(struct rect pix) {
-	size_t low  = fb.pitch * pix.y1 + 4 * pix.x1;
-	size_t high = fb.pitch * pix.y2 + 4 * pix.y2 + 4;
-
-	_syscall_write(fb_fd, fb.b + low, high - low, low, 0);
-}
-
-static void flush_split(struct rect pix) {
-	static uint64_t execbuf[EXECBUF_MAX_LEN / sizeof(uint64_t)];
-	if (7 * (pix.y2 - pix.y1) * sizeof(uint64_t) >= sizeof execbuf) {
-		flush_combined(pix);
-		return;
-	}
-
-	size_t epos = 0;
-	for (uint32_t y = pix.y1; y < pix.y2; y++) {
-		size_t low  = fb.pitch * y + 4 * pix.x1;
-		size_t high = fb.pitch * y + 4 * pix.x2 + 4;
-
-		execbuf[epos++] = EXECBUF_SYSCALL;
-		execbuf[epos++] = _SYSCALL_WRITE;
-		execbuf[epos++] = fb_fd;
-		execbuf[epos++] = fb.b + low;
-		execbuf[epos++] = high - low;
-		execbuf[epos++] = low;
-		execbuf[epos++] = 0;
-	}
-	_syscall_execbuf(execbuf, epos * sizeof(uint64_t));
-}
-
-void flush(void) {
-	if (~dirty.x1 == 0) return;
-
-	struct rect pix;
-	pix.x1 = dirty.x1 * font.w;
-	pix.x2 = dirty.x2 * font.w + font.w - 1;
-	pix.y1 = dirty.y1 * font.h;
-	pix.y2 = dirty.y2 * font.h + font.h - 1;
-	if (pix.x2 >= fb.width)  pix.x2 = fb.width - 1;
-	if (pix.y2 >= fb.height) pix.y2 = fb.height - 1;
-
-	/* the threshold is mostly arbitrary, wasn't based on any real benchmarks */
-	if (pix.x2 - pix.x1 > fb.width - 600) flush_combined(pix);
-	else flush_split(pix);
-
-	dirty.x1 = ~0; dirty.y1 = ~0;
-	dirty.x2 =  0; dirty.y2 =  0;
-}
-
 void scroll(void) {
 	// TODO memmove. this is UD
 	size_t row_len = fb.pitch * font.h;
diff --git a/src/user/app/vterm/font.c b/src/user/app/vterm/font.c
index 368d534..e4f124f 100644
--- a/src/user/app/vterm/font.c
+++ b/src/user/app/vterm/font.c
@@ -53,7 +53,8 @@ void font_blit(uint32_t glyph, int x, int y) {
 		return;
 	}
 
-	dirty_mark(x, y);
+	dirty_mark(&dirty, x * font.w, y * font.h);
+	dirty_mark(&dirty, (x+1) * font.w - 1, (y+1) * font.h - 1);
 
 	char *bitmap = font_data + font.glyph_size * glyph;
 	for (size_t i = 0; i < font.w; i++) {
diff --git a/src/user/app/vterm/vterm.c b/src/user/app/vterm/vterm.c
index 50a7b98..7ae24c2 100644
--- a/src/user/app/vterm/vterm.c
+++ b/src/user/app/vterm/vterm.c
@@ -29,50 +29,11 @@ void in_char(char c) {
 	while ((cursor.y + 1) * font.h >= fb.height) scroll();
 }
 
-int main();
-
-void fb_setup(void) {
-	#define BASEPATH "/kdev/video/"
-	char path[64], *spec;
-	size_t pos;
-	FILE *f;
-
-	f = fopen(BASEPATH, "r");
-	if (!f) {
-		eprintf("couldn't open %s", BASEPATH);
-		exit(1);
-	}
-
-	pos = strlen(BASEPATH);
-	memcpy(path, BASEPATH, pos);
-	spec = path + pos;
-	fread(spec, 1, sizeof(path) - pos, f);
-	/* assumes the read went correctly */
-	fclose(f);
-
-	fb_fd = _syscall_open(path, strlen(path), 0);
-	if (fb_fd < 0) {
-		eprintf("failed to open %s", path);
-		exit(1);
-	}
-
-	fb.width  = strtol(spec, &spec, 0);
-	if (*spec++ != 'x') { eprintf("bad filename format"); exit(1); }
-	fb.height = strtol(spec, &spec, 0);
-	if (*spec++ != 'x') { eprintf("bad filename format"); exit(1); }
-	fb.bpp = strtol(spec, &spec, 0);
-	fb.len = _syscall_getsize(fb_fd);
-	fb.pitch = fb.len / fb.height;
-	fb.b = malloc(fb.len);
-
-	if (fb.bpp != 32) {
-		eprintf("unsupported format %ux%ux%u", fb.width, fb.height, fb.bpp);
-		exit(1);
-	}
-}
-
 int main(void) {
-	fb_setup();
+	if (fb_setup(&fb, "/kdev/video/") < 0) {
+		eprintf("fb_setup error");
+		return 1;
+	}
 	font_load("/init/font.psf");
 
 	static char buf[512];
@@ -90,7 +51,7 @@ int main(void) {
 				} else {
 					for (size_t i = 0; i < res.len; i++)
 						in_char(buf[i]);
-					flush();
+					dirty_flush(&dirty, &fb);
 					_syscall_fs_respond(NULL, res.len, 0);
 				}
 				break;
diff --git a/src/user/app/vterm/vterm.h b/src/user/app/vterm/vterm.h
index 5cfc609..b006ede 100644
--- a/src/user/app/vterm/vterm.h
+++ b/src/user/app/vterm/vterm.h
@@ -2,6 +2,7 @@
 #include <camellia/types.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <user/lib/draw/draw.h>
 
 #define eprintf(fmt, ...) fprintf(stderr, "vterm: "fmt"\n" __VA_OPT__(,) __VA_ARGS__)
 
@@ -21,20 +22,10 @@ extern void *font_data;
 void font_load(const char *path);
 void font_blit(uint32_t glyph, int x, int y);
 
-
-struct framebuf {
-	size_t len, width, height, pitch;
-	uint8_t bpp;
-	char *b;
-};
 extern struct framebuf fb;
-extern handle_t fb_fd;
 
-struct rect {
-	uint32_t x1, y1, x2, y2;
-};
 extern struct rect dirty;
-void dirty_mark(uint32_t x, uint32_t y);
+void vdirty_mark(uint32_t x, uint32_t y);
 void flush(void);
 void scroll(void);
 
diff --git a/src/user/lib/draw/draw.c b/src/user/lib/draw/draw.c
new file mode 100644
index 0000000..c4508da
--- /dev/null
+++ b/src/user/lib/draw/draw.c
@@ -0,0 +1,50 @@
+#include <camellia/syscalls.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <user/lib/draw/draw.h>
+
+void dirty_reset(struct rect *d) {
+	d->x1 = ~0; d->y1 = ~0;
+	d->x2 =  0; d->y2 =  0;
+}
+
+void dirty_mark(struct rect *d, uint32_t x, uint32_t y) {
+	if (d->x1 > x) d->x1 = x;
+	if (d->x2 < x) d->x2 = x;
+	if (d->y1 > y) d->y1 = y;
+	if (d->y2 < y) d->y2 = y;
+}
+
+int fb_setup(struct framebuf *fb, const char *base) {
+	char path[64], *spec;
+	size_t pos;
+	FILE *f;
+
+	f = fopen(base, "r");
+	if (!f) return -errno;
+
+	pos = strlen(base);
+	memcpy(path, base, pos);
+	spec = path + pos;
+	fread(spec, 1, sizeof(path) - pos, f);
+	/* assumes the read went correctly */
+	fclose(f);
+
+	fb->fd = _syscall_open(path, strlen(path), 0);
+	if (fb->fd < 0) return fb->fd;
+
+	fb->width  = strtol(spec, &spec, 0);
+	if (*spec++ != 'x') return -EINVAL;
+	fb->height = strtol(spec, &spec, 0);
+	if (*spec++ != 'x') return -EINVAL;
+	fb->bpp = strtol(spec, &spec, 0);
+	fb->len = _syscall_getsize(fb->fd);
+	fb->pitch = fb->len / fb->height;
+	fb->b = malloc(fb->len);
+
+	if (fb->bpp != 32) return -EINVAL;
+
+	return 0;
+}
diff --git a/src/user/lib/draw/draw.h b/src/user/lib/draw/draw.h
new file mode 100644
index 0000000..ba15840
--- /dev/null
+++ b/src/user/lib/draw/draw.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <camellia/types.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct framebuf {
+	size_t len, width, height, pitch;
+	uint8_t bpp;
+	char *b;
+
+	handle_t fd;
+};
+
+struct rect { uint32_t x1, y1, x2, y2; };
+void dirty_reset(struct rect *d);
+void dirty_mark(struct rect *d, uint32_t x, uint32_t y);
+void dirty_flush(struct rect *d, struct framebuf *fb);
+
+int fb_setup(struct framebuf *fb, const char *base);
diff --git a/src/user/lib/draw/flush.c b/src/user/lib/draw/flush.c
new file mode 100644
index 0000000..060fdaf
--- /dev/null
+++ b/src/user/lib/draw/flush.c
@@ -0,0 +1,43 @@
+#include <camellia/execbuf.h>
+#include <camellia/syscalls.h>
+#include <user/lib/draw/draw.h>
+
+static void flush_combined(struct rect pix, struct framebuf *fb) {
+	size_t low  = fb->pitch * pix.y1 + 4 * pix.x1;
+	size_t high = fb->pitch * pix.y2 + 4 * pix.y2 + 4;
+	_syscall_write(fb->fd, fb->b + low, high - low, low, 0);
+}
+
+static void flush_split(struct rect pix, struct framebuf *fb) {
+	static uint64_t execbuf[EXECBUF_MAX_LEN / sizeof(uint64_t)];
+	size_t epos = 0;
+	if (7 * (pix.y2 - pix.y1) * sizeof(uint64_t) >= sizeof execbuf) {
+		flush_combined(pix, fb);
+		return;
+	}
+
+	for (uint32_t y = pix.y1; y < pix.y2; y++) {
+		size_t low  = fb->pitch * y + 4 * pix.x1;
+		size_t high = fb->pitch * y + 4 * pix.x2 + 4;
+
+		execbuf[epos++] = EXECBUF_SYSCALL;
+		execbuf[epos++] = _SYSCALL_WRITE;
+		execbuf[epos++] = fb->fd;
+		execbuf[epos++] = (uintptr_t)fb->b + low;
+		execbuf[epos++] = high - low;
+		execbuf[epos++] = low;
+		execbuf[epos++] = 0;
+	}
+	_syscall_execbuf(execbuf, epos * sizeof(uint64_t));
+}
+
+void dirty_flush(struct rect *d, struct framebuf *fb) {
+	if (~d->x1 == 0) return;
+	if (d->x2 >= fb->width)  d->x2 = fb->width - 1;
+	if (d->y2 >= fb->height) d->y2 = fb->height - 1;
+
+	/* the threshold is mostly arbitrary, wasn't based on any real benchmarks */
+	if (d->x2 - d->x1 > fb->width - 600) flush_combined(*d, fb);
+	else flush_split(*d, fb);
+	dirty_reset(d);
+}
-- 
cgit v1.2.3