diff options
Diffstat (limited to 'host/bdf.c')
-rw-r--r-- | host/bdf.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/host/bdf.c b/host/bdf.c new file mode 100644 index 0000000..a95ed8b --- /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 && x+i-offx < gbufwidth){ /* 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"); +} + |