aboutsummaryrefslogtreecommitdiff
path: root/host/bdf.c
diff options
context:
space:
mode:
Diffstat (limited to 'host/bdf.c')
-rw-r--r--host/bdf.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/host/bdf.c b/host/bdf.c
new file mode 100644
index 0000000..e6ab827
--- /dev/null
+++ b/host/bdf.c
@@ -0,0 +1,356 @@
+
+#include "config.h"
+#include "bdf.h"
+#include "color.h"
+#include "font.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <wchar.h>
+#include <locale.h>
+#include <sys/timeb.h>
+#include <sys/queue.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED
+ * ...also, it does a hardcoded setlocale of LC_CTYPE to en_US.utf8 for... reasons. */
+int framebuffer_get_text_bounds(char *s, glyphtable_t *glyph_table, size_t *outw, size_t *outh){
+ size_t gbufwidth = 0;
+ size_t gbufheight = 0;
+ char *p = s;
+
+ /* Calculate screen width of string prior to allocating memory for the frame buffer */
+ wchar_t c;
+ mbstate_t ps = {0};
+ memset(&ps, 0, sizeof(mbstate_t));
+ if(!setlocale(LC_CTYPE, "en_US.utf8")){
+ fprintf(stderr, "Cannot set locale\n");
+ goto error;
+ }
+ for(;;){
+ while(*p == '\033'){
+ p++;
+ /* Jump over escape sequences */
+ for(;;p++){
+ if(!(*p == ';' || *p == '[' || ('0' <= *p && *p <= '9'))){
+ p++;
+ break;
+ }
+ }
+ memset(&ps, 0, sizeof(mbstate_t));
+ }
+
+ size_t inc = mbrtowc(&c, p, MB_CUR_MAX, &ps); /* MB_CUR_MAX is safe since p is \0-terminated */
+ if(inc == -1 || inc == -2){
+ fprintf(stderr, "Error rendering string: No valid UTF-8 input.\n");
+ goto error;
+ }
+ if(inc == 0) /* Reached end of string */
+ break;
+ p += inc;
+
+ if(c > glyph_table->size){
+ fprintf(stderr, "Error rendering string: Codepoint 0x%lx out of valid range (0-%zd).\n", (long int)c, glyph_table->size);
+ goto error;
+ }
+
+ glyph_t *g = glyph_table->data[c];
+ if(!g){
+ fprintf(stderr, "Error rendering string: Codepoint 0x%lx not in font.\n", (long int)c);
+ goto error;
+ }
+
+ if(g->height > gbufheight)
+ gbufheight = g->height;
+
+
+ gbufwidth += g->width;
+ }
+ *outw = gbufwidth;
+ *outh = gbufheight;
+ return 0;
+error:
+ return 1;
+}
+
+/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED
+ * ...also, it does a hardcoded setlocale of LC_CTYPE to en_US.utf8 for... reasons. */
+/* Render the string beginning from the specified x offset (in pixels) */
+int framebuffer_render_text(char *s, glyphtable_t *glyph_table, color_t *gbuf, size_t gbufwidth, size_t gbufheight, size_t offx){
+ unsigned int len = strlen(s);
+ char *p = s;
+
+ if(!setlocale(LC_CTYPE, "en_US.utf8")){
+ fprintf(stderr, "Cannot set locale\n");
+ goto error;
+ }
+
+ memset(gbuf, 0, gbufwidth*gbufheight*sizeof(color_t));
+
+ unsigned int x = 0;
+ wchar_t c;
+ p = s;
+ mbstate_t ps = {0};
+ memset(&ps, 0, sizeof(mbstate_t));
+ struct {
+ color_t fg;
+ color_t bg;
+ unsigned int blink:4;
+ unsigned int bold:1; /* TODO */
+ unsigned int underline:1;
+ unsigned int strikethrough:1;
+ unsigned int fraktur:1; /* TODO See: Flat10 Fraktur font */
+ unsigned int invert:1;
+ } style = {
+ colortable[DEFAULT_FG_COLOR], colortable[DEFAULT_BG_COLOR], 0, 0, 0, 0, 0, 0
+ };
+ /* Render glyphs (now with escape sequence rendering!) */
+ for(;;){
+ /* NOTE: This nested escape sequence parsing does not contain any unicode-awareness whatsoever */
+ if(*p == '\033'){ /* Escape sequence YAY */
+ char *sequence_start = ++p;
+ if(*p == '['){ /* This was a CSI! */
+ /* Disassemble the list of numbers, only accept SGR sequences (those ending with 'm') */
+ long elems[MAX_CSI_ELEMENTS];
+ int nelems;
+ for(nelems = 0; nelems<MAX_CSI_ELEMENTS; nelems++){
+ p++;
+ char *endptr;
+ elems[nelems] = strtol(p, &endptr, 10);
+ if(p == endptr){
+ fprintf(stderr, "Invalid escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ p = endptr;
+ if(*endptr == 'm')
+ break;
+ if(*endptr != ';'){
+ fprintf(stderr, "Invalid escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ }
+ p++; /* gobble up trailing 'm' of "\033[23;42m" */
+ nelems++;
+ /* By now we know it's a SGR since we error'ed out on anything else */
+ if(nelems < 1){
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ /* Parse the sequence numbers */
+ for(int i=0; i<nelems; i++){
+ switch(elems[i]){
+ case 0: /* reset style */
+ style.fg = colortable[DEFAULT_FG_COLOR];
+ style.bg = colortable[DEFAULT_BG_COLOR];
+ style.bold = 0;
+ style.underline = 0;
+ style.blink = 0;
+ style.strikethrough = 0;
+ style.fraktur = 0;
+ style.invert = 0;
+ break;
+ case 1: /* bold */
+ style.bold = 1;
+ break;
+ case 4: /* underline */
+ style.underline = 1;
+ break;
+ case 5: /* slow blink */
+ style.blink = 1;
+ break;
+ case 6: /* rapid blink */
+ style.blink = 8;
+ break;
+ case 7: /* color invert on */
+ style.invert = 1;
+ break;
+ case 9: /* strike-through */
+ style.strikethrough = 1;
+ break;
+ case 20:/* Fraktur */
+ style.fraktur = 1;
+ break;
+ case 22:/* Bold off */
+ style.bold = 0;
+ break;
+ case 24:/* Underline off */
+ style.underline = 0;
+ break;
+ case 25:/* Blink off */
+ style.blink = 0;
+ break;
+ case 27:/* color invert off */
+ style.invert = 0;
+ break;
+ case 29:/* strike-through off */
+ style.strikethrough = 0;
+ break;
+ case 30: /* Set foreground color, "dim" colors */
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ style.fg = colortable[elems[i]-30];
+ break;
+ case 38: /* Set xterm-256 foreground color */
+ i++;
+ if(nelems-i < 2 || elems[i] != 5){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ style.fg = colortable[elems[++i]];
+ break;
+ case 39: /* Reset foreground color to default */
+ style.bg = colortable[DEFAULT_FG_COLOR];
+ break;
+ case 40: /* Set background color, "dim" colors */
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ style.bg = colortable[elems[i]-40];
+ break;
+ case 48: /* Set xterm-256 background color */
+ i++;
+ if(nelems-i < 2 || elems[i] != 5){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ style.bg = colortable[elems[++i]];
+ break;
+ case 49: /* Reset background color to default */
+ style.bg = colortable[DEFAULT_BG_COLOR];
+ break;
+ case 90: /* Set foreground color, "bright" colors */
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97:
+ style.fg = colortable[elems[i]-90+8];
+ break;
+ case 100: /* Set background color, "bright" colors */
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107:
+ style.bg = colortable[elems[i]-100+8];
+ break;
+ default:
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ }
+
+ }else{
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+
+ continue;
+ }
+
+ size_t inc = mbrtowc(&c, p, (s+len+1)-p, NULL);
+ /* If p contained */
+ if(inc == 0) /* Reached end of string */
+ break;
+ p += inc;
+
+ /* Render glyph into frame buffer */
+ struct timeb time = {0};
+ ftime(&time);
+ unsigned long int t = time.time*1000 + time.millitm;
+ int blink = style.blink && (t % (1000/style.blink) < (333/style.blink));
+ int inv = !(style.invert ^ blink);
+ color_t fg = inv ? style.fg : style.bg;
+ color_t bg = inv ? style.bg : style.fg;
+
+ glyph_t *g = glyph_table->data[c];
+ /* Is the glyph within the buffer's bounds? */
+ if(x+g->width > offx && x < offx+gbufwidth){
+ /* x-offx might be negative down to -g->width+1, but that's ok */
+ render_glyph(g, gbuf, gbufwidth, x-offx, 0, fg, bg);
+ if(style.strikethrough || style.underline){
+ int sty = gbufheight/2;
+ /* g->y usually is a negative index of the glyph's baseline measured from the glyph's bottom */
+ int uly = gbufheight + g->y;
+ for(int i=0; i<g->width; i++){
+ if(x+i >= offx){ /* Stay within the frame buffer's bounds */
+ if(style.strikethrough)
+ gbuf[sty*gbufwidth + x + i - offx] = fg;
+ if(style.underline)
+ gbuf[uly*gbufwidth + x + i - offx] = fg;
+ }
+ }
+ }
+ }
+ x += g->width;
+ }
+ return 0;
+error:
+ return 1;
+}
+
+void console_render_buffer(color_t *data, size_t w, size_t h){
+ /* Render framebuffer to terminal, two pixels per character using Unicode box drawing stuff */
+ color_t lastfg = {0, 0, 0}, lastbg = {0, 0, 0};
+ printf("\e[38;5;0;48;5;0m\e[K");
+ for(size_t y=0; y < h; y+=2){
+ for(size_t x=0; x < w; x++){
+ /* Da magicks: ▀█▄ */
+ color_t ct = data[y*w + x]; /* Top pixel */
+ color_t cb = data[(y+1)*w + x]; /* Bottom pixel */
+ /* The following, rather convoluted logic tries to "save" escape sequences when rendering. */
+ if(!memcmp(&ct, &lastfg, sizeof(color_t))){
+ if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf("▀");
+ }else if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("█");
+ }else{
+ printf("\033[48;5;%dm▀", xterm_color_index(cb));
+ lastbg = cb;
+ }
+ }else if(!memcmp(&ct, &lastbg, sizeof(color_t))){
+ if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("▄");
+ }else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf(" ");
+ }else{
+ printf("\033[38;5;%dm▄", xterm_color_index(cb));
+ lastfg = cb;
+ }
+ }else{ /* No matches for the upper pixel */
+ if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("\033[48;5;%dm▄", xterm_color_index(ct));
+ lastbg = ct;
+ }else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf("\033[38;5;%dm▀", xterm_color_index(ct));
+ lastfg = ct;
+ }else{
+ printf("\033[38;5;%d;48;5;%dm▀", xterm_color_index(ct), xterm_color_index(cb));
+ lastfg = ct;
+ lastbg = cb;
+ }
+ }
+ }
+ printf("\n\e[K");
+ }
+ printf("\033[0m");
+}
+