2015-07-20 20:59:50 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
First commit.
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a6c861 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +a.out +*.o +*.gif +*.t +*.d diff --git a/6x11.mbf b/6x11.mbf new file mode 100644 index 0000000..c1c3c85 Binary files /dev/null and b/6x11.mbf differ diff --git a/cs_437.h b/cs_437.h new file mode 100644 index 0000000..a8b9f0c --- /dev/null +++ b/cs_437.h @@ -0,0 +1,34 @@ +static uint16_t cs_437[0x100] = { + 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25D8, 0x2218, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x2302, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, +}; diff --git a/cs_vtg.h b/cs_vtg.h new file mode 100644 index 0000000..c2c45d8 --- /dev/null +++ b/cs_vtg.h @@ -0,0 +1,34 @@ +static uint16_t cs_vtg[0x100] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x2192, 0x2190, 0x2191, 0x2193, 0x002F, + 0x25AE, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x2603, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x25C6, 0x2592, 0x0062, 0x0063, 0x0064, 0x0065, 0x00B0, 0x00B1, + 0x2591, 0x0069, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, + 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, + 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00B7, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +}; diff --git a/default.h b/default.h new file mode 100644 index 0000000..4babf69 --- /dev/null +++ b/default.h @@ -0,0 +1,25 @@ +static uint8_t def_plt[0x30] = { + 0x00, 0x00, 0x00, + 0xCD, 0x00, 0x00, + 0x00, 0xCD, 0x00, + 0xCD, 0xCD, 0x00, + 0x00, 0x00, 0xEE, + 0xCD, 0x00, 0xCD, + 0x00, 0xCD, 0xCD, + 0xCD, 0xCD, 0xCD, + 0x7F, 0x7F, 0x7F, + 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, + 0x5C, 0x5C, 0xFF, + 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF +}; + +#define DEF_FORE 0x7 +#define DEF_BACK 0x0 + +static uint16_t def_mode = M_AUTOWRAP | M_AUTORPT | M_CURSORVIS; +static uint8_t def_attr = A_NORMAL; +static uint8_t def_pair = (DEF_FORE << 4) | DEF_BACK; diff --git a/dump.c b/dump.c new file mode 100644 index 0000000..0d8b6e1 --- /dev/null +++ b/dump.c @@ -0,0 +1,37 @@ +#include <stdint.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "term.h" + +void +dump_txt(Term *term, const char *fname) +{ + int i, j, fd; + uint16_t code; + char ch; + + fd = creat(fname, 0666); + if (fd == -1) + return; + for (i = 0; i < term->rows; i++) { + for (j = 0; j < term->cols; j++) { + code = term->addr[i][j].code; + if (code >= 0x20 && code < 0x7F) + ch = (char) code; + else + ch = 0x20; + write(fd, &ch, 1); + } + write(fd, "\n", 1); + } + close(fd); +} + +void +dump_ppm(Term *term, const char *fname) +{ + uint8_t pix[term->rows*term->cols]; +} diff --git a/dump.h b/dump.h new file mode 100644 index 0000000..bb543c0 --- /dev/null +++ b/dump.h @@ -0,0 +1,2 @@ +void dump_txt(Term *term, const char *fname); +void dump_ppm(Term *term, const char *fname); diff --git a/gen-maps.lua b/gen-maps.lua new file mode 100644 index 0000000..b94fac5 --- /dev/null +++ b/gen-maps.lua @@ -0,0 +1,95 @@ +local function build_vtg() + -- VT100 Graphics + local vtg_kv = { + {0x2B, 0x2192}, {0x2C, 0x2190}, {0x2D, 0x2191}, {0x2E, 0x2193}, + {0x30, 0x25AE}, {0x49, 0x2603}, {0x60, 0x25C6}, {0x61, 0x2592}, + {0x66, 0x00B0}, {0x67, 0x00B1}, {0x68, 0x2591}, {0x6A, 0x2518}, + {0x6B, 0x2510}, {0x6C, 0x250C}, {0x6D, 0x2514}, {0x6E, 0x253C}, + {0x6F, 0x23BA}, {0x70, 0x23BB}, {0x71, 0x2500}, {0x72, 0x23BC}, + {0x73, 0x23BD}, {0x74, 0x251C}, {0x75, 0x2524}, {0x76, 0x2534}, + {0x77, 0x252C}, {0x78, 0x2502}, {0x79, 0x2264}, {0x7A, 0x2265}, + {0x7B, 0x03C0}, {0x7C, 0x2260}, {0x7D, 0x00A3}, {0x7E, 0x00B7}, + } + local vtg = {} + for i, kv in ipairs(vtg_kv) do + local k, v = unpack(kv) + vtg[k] = v + end + return vtg +end + +local function build_ibm() + -- Code Page 437 + local ibm_kv = { + {0x01, 0x263A}, {0x02, 0x263B}, {0x03, 0x2665}, {0x04, 0x2666}, + {0x05, 0x2663}, {0x06, 0x2660}, {0x07, 0x2022}, {0x08, 0x25D8}, + {0x09, 0x2218}, {0x0A, 0x25D9}, {0x0B, 0x2642}, {0x0C, 0x2640}, + {0x0D, 0x266A}, {0x0E, 0x266B}, {0x0F, 0x263C}, {0x10, 0x25BA}, + {0x11, 0x25C4}, {0x12, 0x2195}, {0x13, 0x203C}, {0x14, 0x00B6}, + {0x15, 0x00A7}, {0x16, 0x25AC}, {0x17, 0x21A8}, {0x18, 0x2191}, + {0x19, 0x2193}, {0x1A, 0x2192}, {0x1B, 0x2190}, {0x1C, 0x221F}, + {0x1D, 0x2194}, {0x1E, 0x25B2}, {0x1F, 0x25BC}, {0x7F, 0x2302}, + + {0x80, 0x00C7}, {0x81, 0x00FC}, {0x82, 0x00E9}, {0x83, 0x00E2}, + {0x84, 0x00E4}, {0x85, 0x00E0}, {0x86, 0x00E5}, {0x87, 0x00E7}, + {0x88, 0x00EA}, {0x89, 0x00EB}, {0x8A, 0x00E8}, {0x8B, 0x00EF}, + {0x8C, 0x00EE}, {0x8D, 0x00EC}, {0x8E, 0x00C4}, {0x8F, 0x00C5}, + {0x90, 0x00C9}, {0x91, 0x00E6}, {0x92, 0x00C6}, {0x93, 0x00F4}, + {0x94, 0x00F6}, {0x95, 0x00F2}, {0x96, 0x00FB}, {0x97, 0x00F9}, + {0x98, 0x00FF}, {0x99, 0x00D6}, {0x9A, 0x00DC}, {0x9B, 0x00A2}, + {0x9C, 0x00A3}, {0x9D, 0x00A5}, {0x9E, 0x20A7}, {0x9F, 0x0192}, + {0xA0, 0x00E1}, {0xA1, 0x00ED}, {0xA2, 0x00F3}, {0xA3, 0x00FA}, + {0xA4, 0x00F1}, {0xA5, 0x00D1}, {0xA6, 0x00AA}, {0xA7, 0x00BA}, + {0xA8, 0x00BF}, {0xA9, 0x2310}, {0xAA, 0x00AC}, {0xAB, 0x00BD}, + {0xAC, 0x00BC}, {0xAD, 0x00A1}, {0xAE, 0x00AB}, {0xAF, 0x00BB}, + {0xB0, 0x2591}, {0xB1, 0x2592}, {0xB2, 0x2593}, {0xB3, 0x2502}, + {0xB4, 0x2524}, {0xB5, 0x2561}, {0xB6, 0x2562}, {0xB7, 0x2556}, + {0xB8, 0x2555}, {0xB9, 0x2563}, {0xBA, 0x2551}, {0xBB, 0x2557}, + {0xBC, 0x255D}, {0xBD, 0x255C}, {0xBE, 0x255B}, {0xBF, 0x2510}, + {0xC0, 0x2514}, {0xC1, 0x2534}, {0xC2, 0x252C}, {0xC3, 0x251C}, + {0xC4, 0x2500}, {0xC5, 0x253C}, {0xC6, 0x255E}, {0xC7, 0x255F}, + {0xC8, 0x255A}, {0xC9, 0x2554}, {0xCA, 0x2569}, {0xCB, 0x2566}, + {0xCC, 0x2560}, {0xCD, 0x2550}, {0xCE, 0x256C}, {0xCF, 0x2567}, + {0xD0, 0x2568}, {0xD1, 0x2564}, {0xD2, 0x2565}, {0xD3, 0x2559}, + {0xD4, 0x2558}, {0xD5, 0x2552}, {0xD6, 0x2553}, {0xD7, 0x256B}, + {0xD8, 0x256A}, {0xD9, 0x2518}, {0xDA, 0x250C}, {0xDB, 0x2588}, + {0xDC, 0x2584}, {0xDD, 0x258C}, {0xDE, 0x2590}, {0xDF, 0x2580}, + {0xE0, 0x03B1}, {0xE1, 0x00DF}, {0xE2, 0x0393}, {0xE3, 0x03C0}, + {0xE4, 0x03A3}, {0xE5, 0x03C3}, {0xE6, 0x00B5}, {0xE7, 0x03C4}, + {0xE8, 0x03A6}, {0xE9, 0x0398}, {0xEA, 0x03A9}, {0xEB, 0x03B4}, + {0xEC, 0x221E}, {0xED, 0x03C6}, {0xEE, 0x03B5}, {0xEF, 0x2229}, + {0xF0, 0x2261}, {0xF1, 0x00B1}, {0xF2, 0x2265}, {0xF3, 0x2264}, + {0xF4, 0x2320}, {0xF5, 0x2321}, {0xF6, 0x00F7}, {0xF7, 0x2248}, + {0xF8, 0x00B0}, {0xF9, 0x2219}, {0xFA, 0x00B7}, {0xFB, 0x221A}, + {0xFC, 0x207F}, {0xFD, 0x00B2}, {0xFE, 0x25A0}, {0xFF, 0x00A0} + } + local ibm = {} + for i, kv in ipairs(ibm_kv) do + local k, v = unpack(kv) + ibm[k] = v + end + return ibm +end + +local vtg = build_vtg() +local ibm = build_ibm() + +local function gen_header(name, tab) + local fname = name..".h" + local fp = io.open(fname, "w") + fp:write("static uint16_t "..name.."[0x100] = {") + local i = 0 + for j = 1, 32 do + fp:write("\n ") + for k = 1, 8 do + local c = tab[i] or i + fp:write((" 0x%04X,"):format(c)) + i = i + 1 + end + end + fp:write("\n};\n") + fp:close() +end + +gen_header("cs_vtg", vtg) +gen_header("cs_437", ibm) diff --git a/gif.c b/gif.c new file mode 100644 index 0000000..1cb52a9 --- /dev/null +++ b/gif.c @@ -0,0 +1,218 @@ +/* +gcc -Wall -Wextra -std=c99 -O0 -g -fsanitize=address -fno-omit-frame-pointer -c gif.c +gcc -Wall -Wextra -std=c99 -O2 -c gif.c +gcc -shared -o libgif.so gif.o +*/ + +/* NOTES + - The encoder dumps uint16_t numbers as is into GIF files, which means it + only works on little-endian machines, since this is what GIF requires. + - The encoder is very limited: + * the global palette size is fixed in 16 colors; + * there's no way to add local palettes; + * the only GIF89a extension implemented is GCE (to set delay times). + - The compressor only sends the clear code once per image, as the first + code (no adaptive compression technique is employed). This is suboptimal + for large and complex images. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "gif.h" + +struct Node { + uint16_t key; + struct Node *children[0x10]; +}; +typedef struct Node Node; + +static Node * +new_node(uint16_t key) +{ + Node *node = calloc(1, sizeof(*node)); + if (node) + node->key = key; + return node; +} + +static void +del_trie(Node *root) +{ + if (!root) + return; + for (int i = 0; i < 0x10; i++) + del_trie(root->children[i]); + free(root); +} + +GIF * +new_gif(const char *fname, uint16_t w, uint16_t h, uint8_t *gct) +{ + GIF *gif = calloc(1, sizeof(*gif) + 2*w*h); + gif->w = w; gif->h = h; + gif->cur = (uint8_t *) &gif[1]; + gif->old = &gif->cur[w*h]; + /* fill back-buffer with invalid pixels to force overwrite */ + memset(gif->old, 0x10, w*h); + gif->fd = creat(fname, 0666); + if (gif->fd == -1) + return NULL; + write(gif->fd, "GIF89a", 6); + write(gif->fd, &w, 2); + write(gif->fd, &h, 2); + write(gif->fd, (uint8_t []) {0xF3, 0x00, 0x00}, 3); + write(gif->fd, gct, 0x30); + return gif; +} + +/* Add packed key to buffer, updating offset and partial. + * gif->offset holds position to put next *bit* + * gif->partial holds bits to include in next byte */ +static void +put_key(GIF *gif, uint16_t key, int key_size) +{ + int byte_offset, bit_offset, bits_to_write; + byte_offset = gif->offset / 8; + bit_offset = gif->offset % 8; + gif->partial |= ((uint32_t) key) << bit_offset; + bits_to_write = bit_offset + key_size; + while (bits_to_write >= 8) { + gif->buffer[byte_offset++] = gif->partial & 0xFF; + if (byte_offset == 0xFF) { + write(gif->fd, "\xFF", 1); + write(gif->fd, gif->buffer, 0xFF); + byte_offset = 0; + } + gif->partial >>= 8; + bits_to_write -= 8; + } + gif->offset = (gif->offset + key_size) % (0xFF * 8); +} + +static void +end_key(GIF *gif) +{ + int byte_offset; + byte_offset = gif->offset / 8; + gif->buffer[byte_offset++] = gif->partial & 0xFF; + write(gif->fd, (uint8_t []) {byte_offset}, 1); + write(gif->fd, gif->buffer, byte_offset); + write(gif->fd, "\0", 1); + gif->offset = gif->partial = 0; +} + +static void +put_image(GIF *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y) +{ + int nkeys, key_size, i, j; + Node *node, *child, *root; + + root = malloc(sizeof(*root)); + write(gif->fd, ",", 1); + write(gif->fd, &x, 2); + write(gif->fd, &y, 2); + write(gif->fd, &w, 2); + write(gif->fd, &h, 2); + write(gif->fd, (uint8_t []) {0x00, 0x04}, 2); + /* Create nodes for single pixels. */ + for (nkeys = 0; nkeys < 0x10; nkeys++) + root->children[nkeys] = new_node(nkeys); + node = root; + key_size = 5; + nkeys += 2; /* skip clear code and stop code */ + put_key(gif, 0x10, key_size); /* clear code */ + for (i = y; i < y+h; i++) { + for (j = x; j < x+w; j++) { +#ifdef CROP + uint8_t pixel = 2; +#else + uint8_t pixel = gif->cur[i*gif->w+j]; +#endif + child = node->children[pixel]; + if (child) { + node = child; + } else { + put_key(gif, node->key, key_size); + if (nkeys < 0x1000) { + if (nkeys == (1 << key_size)) + key_size++; + node->children[pixel] = new_node(nkeys++); + } + node = root->children[pixel]; + } + } + } + put_key(gif, node->key, key_size); + put_key(gif, 0x11, key_size); /* stop code */ + end_key(gif); + del_trie(root); +} + +static void +get_bbox(GIF *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y) +{ + int i, j, k; + int left, right, top, bottom; + left = gif->w; right = 0; + top = gif->h; bottom = 0; + k = 0; + for (i = 0; i < gif->h; i++) { + for (j = 0; j < gif->w; j++, k++) { + if (gif->cur[k] != gif->old[k]) { + if (j < left) left = j; + if (j > right) right = j; + if (i < top) top = i; + if (i > bottom) bottom = i; + } + } + } + *x = left; *y = top; + *w = right - left + 1; + *h = bottom - top + 1; +} + +static void +set_delay(GIF *gif, uint16_t d) +{ +#ifdef CROP + write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, 0x08}, 4); +#else + write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4); +#endif + write(gif->fd, &d, 2); + write(gif->fd, "\0\0", 2); +} + +void +add_frame(GIF *gif, uint16_t d) +{ + uint16_t w, h, x, y; + uint8_t *tmp; + + if (d) + set_delay(gif, d); + get_bbox(gif, &w, &h, &x, &y); + if (x == gif->w || y == gif->h) { + /* image haven't changed; save one pixel just to add delay */ + w = h = 1; + x = y = 0; + } + put_image(gif, w, h, x, y); + tmp = gif->old; + gif->old = gif->cur; + gif->cur = tmp; +} + +void +close_gif(GIF* gif) +{ + write(gif->fd, ";", 1); + close(gif->fd); + free(gif); +} diff --git a/gif.h b/gif.h new file mode 100644 index 0000000..a9dbe72 --- /dev/null +++ b/gif.h @@ -0,0 +1,14 @@ +#include <stdint.h> + +typedef struct GIF { + uint16_t w, h; + int fd; + int offset; + uint8_t *cur, *old; + uint32_t partial; + uint8_t buffer[0xFF]; +} GIF; + +GIF *new_gif(const char *fname, uint16_t w, uint16_t h, uint8_t *gct); +void add_frame(GIF *gif, uint16_t d); +void close_gif(GIF* gif); diff --git a/main.c b/main.c new file mode 100644 index 0000000..6ca77d9 --- /dev/null +++ b/main.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "term.h" +//#include "dump.h" +#include "mbf.h" +#include "gif.h" + +void +parse_script(Term *term, const char *timing, const char *dialogue) +{ + FILE *ft; + int fd; + float t; + int n; + uint8_t ch; + + ft = fopen(timing, "r"); + fd = open(dialogue, O_RDONLY); + if (ft == NULL || fd == -1) + return; + /* discard first line of dialogue */ + do read(fd, &ch, 1); while (ch != '\n'); + while (fscanf(ft, "%f %d\n", &t, &n) == 2) { + while (n--) { + read(fd, &ch, 1); + parse(term, ch); + } + } + close(fd); + fclose(ft); +} + +int +get_index(Font *font, uint16_t code) +{ + int index; + + index = search_glyph(font, code); + if (index == -1) + index = search_glyph(font, 0xFFFD); + if (index == -1) + index = search_glyph(font, 0x003F); + if (index == -1) + index = search_glyph(font, 0x0020); + if (index == -1) + index = search_glyph(font, 0x0000); + return index; +} + +uint8_t +get_pair(Term *term, int row, int col) +{ + Cell cell; + uint8_t fore, back; + int inverse; + + /* TODO: add support for A_INVISIBLE */ + inverse = term->mode & M_REVERSE; + //~ if (term->mode & M_CURSORVIS) + //~ inverse = term->row == row && term->col == col ? !inverse : inverse; + cell = term->addr[row][col]; + inverse = cell.attr & A_INVERSE ? !inverse : inverse; + if (inverse) { + fore = cell.pair & 0xF; + back = cell.pair >> 4; + } else { + fore = cell.pair >> 4; + back = cell.pair & 0xF; + } + if (cell.attr & (A_DIM | A_UNDERLINE)) + fore = 0x6; + else if (cell.attr & (A_ITALIC | A_CROSSED)) + fore = 0x2; + if (cell.attr & A_BOLD) + fore |= 0x8; + return (fore << 4) | (back & 0xF); +} + +void +draw_char(Font *font, GIF *gif, uint16_t code, uint8_t pair, int row, int col) +{ + int i, j; + int x, y; + int index; + int pixel; + uint8_t *strip; + + index = get_index(font, code); + if (index == -1) + return; + strip = &font->data[font->stride * font->header.h * index]; + y = font->header.h * row; + for (i = 0; i < font->header.h; i++) { + x = font->header.w * col; + for (j = 0; j < font->header.w; j++) { + pixel = strip[j >> 3] & (1 << (7 - (j & 7))); + gif->cur[y * gif->w + x] = pixel ? pair >> 4 : pair & 0xF; + x++; + } + y++; + strip += font->stride; + } +} + +void +render(Term *term, Font *font, GIF *gif, uint16_t delay) +{ + int i, j; + uint16_t code; + uint8_t pair; + + for (i = 0; i < term->rows; i++) { + for (j = 0; j < term->cols; j++) { + code = term->addr[i][j].code; + pair = get_pair(term, i, j); + draw_char(font, gif, code, pair, i, j); + } + } + add_frame(gif, delay); +} + +void +convert_script(Term *term, const char *timing, const char *dialogue, + const char *mbf, const char *anim) +{ + FILE *ft; + int fd; + float t; + int n; + uint8_t ch; + Font *font; + int w, h; + int i; + uint16_t d; + GIF *gif; + + ft = fopen(timing, "r"); + if (!ft) + goto no_ft; + fd = open(dialogue, O_RDONLY); + if (fd == -1) + goto no_fd; + font = load_font(mbf); + if (!font) + goto no_font; + w = term->cols * font->header.w; + h = term->rows * font->header.h; + gif = new_gif(anim, w, h, term->plt); + if (!gif) + goto no_gif; + /* discard first line of dialogue */ + do read(fd, &ch, 1); while (ch != '\n'); + i = 0; + while (fscanf(ft, "%f %d\n", &t, &n) == 2) { + d = (uint16_t) (t * 100.0 / 3.0); + if (i) + render(term, font, gif, d); + while (n--) { + read(fd, &ch, 1); + parse(term, ch); + } + i++; + } + render(term, font, gif, 0); + close_gif(gif); +no_gif: + free(font); +no_font: + close(fd); +no_fd: + fclose(ft); +no_ft: + return; +} + +int +main(int argc, char *argv[]) +{ + Term *term; + + if (argc != 3) + return 1; + term = new_term(30, 80); + //parse_script(term, argv[1], argv[2]); + //dump_txt(term, "matrix.txt"); + convert_script(term, argv[1], argv[2], "6x11.mbf", "con.gif"); + free(term); + return 0; +} diff --git a/mbf.c b/mbf.c new file mode 100644 index 0000000..b9fa782 --- /dev/null +++ b/mbf.c @@ -0,0 +1,104 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "mbf.h" + +Font * +load_font(const char *fname) +{ + int fd; + char sig[4]; + Header header; + int stride; + size_t ranges_size, data_size; + Font *font; + + fd = open(fname, O_RDONLY); + if (fd == -1) + return NULL; + read(fd, sig, sizeof(sig)); + if (memcmp(sig, (char []) {'M', 'B', 'F', 0x01}, sizeof(sig))) { + close(fd); + return NULL; + } + read(fd, &header, sizeof(header)); + /* stride = ceil(w / 8) = floor(w / 8) + (w % 8 ? 1 : 0) */ + stride = (header.w >> 3) + !!(header.w & 7); + ranges_size = header.nr * sizeof(Range); + data_size = header.ng * stride * header.h; + font = malloc(sizeof(Font) + ranges_size + data_size); + if (!font) { + close(fd); + return NULL; + } + font->stride = stride; + font->header = header; + font->ranges = (Range *) &font[1]; + read(fd, font->ranges, ranges_size); + font->data = (uint8_t *) &font->ranges[header.nr]; + read(fd, font->data, data_size); + close(fd); + return font; +} + +int +search_glyph(Font *font, uint16_t code) +{ + int index, i; + Range r; + + index = 0; + for (i = 0; i < font->header.nr; i++) { + r = font->ranges[i]; + if (code < r.offset) + return -1; + if (code < r.offset + r.length) + return index + code - r.offset; + index += r.length; + } + return -1; +} + +void +print_glyph(Font *font, uint16_t code) +{ + int index, pixel, i, j; + uint8_t *row; + + index = search_glyph(font, code); + if (index == -1) + return; + row = &font->data[font->stride * font->header.h * index]; + for (i = 0; i < font->header.h; i++) { + for (j = 0; j < font->header.w; j++) { + pixel = row[j >> 3] & (1 << (7 - (j & 7))); + putchar(pixel ? 'X' : ' '); + } + putchar('\n'); + row += font->stride; + } +} + +#if 0 +int +main(int argc, char *argv[]) +{ + Font *font; + + if (argc != 2) + return 1; + font = load_font(argv[1]); + if (!font) + return 1; + //printf("%d\n", search_glyph(font, 200)); + print_glyph(font, 0xFFFD); + free(font); + return 0; +} +#endif diff --git a/mbf.h b/mbf.h new file mode 100644 index 0000000..3929984 --- /dev/null +++ b/mbf.h @@ -0,0 +1,19 @@ +typedef struct Header { + uint16_t ng; + uint8_t w, h; + uint16_t nr; +} Header; + +typedef struct Range { + uint16_t offset, length; +} Range; + +typedef struct Font { + Header header; + int stride; + Range *ranges; + uint8_t *data; +} Font; + +Font *load_font(const char *fname); +int search_glyph(Font *font, uint16_t code); diff --git a/term.c b/term.c new file mode 100644 index 0000000..9efe062 --- /dev/null +++ b/term.c @@ -0,0 +1,725 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "term.h" +#include "default.h" +#include "cs_vtg.h" +#include "cs_437.h" + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +void +save_cursor(Term *term) +{ + term->save_cursor.row = term->row; + term->save_cursor.col = term->col; +} + +void +load_cursor(Term *term) +{ + term->row = term->save_cursor.row; + term->col = term->save_cursor.col; +} + +void +save_misc(Term *term) +{ + term->save_misc.row = term->row; + term->save_misc.col = term->col; + term->save_misc.origin_on = term->mode & M_ORIGIN; + term->save_misc.attr = term->attr; + term->save_misc.pair = term->pair; + term->save_misc.cs_array[0] = term->cs_array[0]; + term->save_misc.cs_array[1] = term->cs_array[1]; + term->save_misc.cs_index = term->cs_index; +} + +void +load_misc(Term *term) +{ + term->row = term->save_misc.row; + term->col = term->save_misc.col; + if (term->save_misc.origin_on) + term->mode |= M_ORIGIN; + else + term->mode &= ~M_ORIGIN; + term->attr = term->save_misc.attr; + term->pair = term->save_misc.pair; + term->cs_array[0] = term->save_misc.cs_array[0]; + term->cs_array[1] = term->save_misc.cs_array[1]; + term->cs_index = term->save_misc.cs_index; +} + +void +reset(Term *term) +{ + int i, j; + + term->row = term->col = 0; + term->top = 0; + term->bot = term->rows - 1; + term->mode = def_mode; + term->attr = def_attr; + term->pair = def_pair; + term->cs_array[0] = CS_BMP; + term->cs_array[1] = CS_VTG; + term->cs_index = 0; + term->state = S_ANY; + term->parlen = 0; + memcpy(term->plt, def_plt, sizeof(term->plt)); + for (i = 0; i < term->rows; i++) { + term->addr[i] = &term->cells[i*term->cols]; + for (j = 0; j < term->cols; j++) + term->addr[i][j] = (Cell) {EMPTY, def_attr, def_pair}; + } + save_cursor(term); + save_misc(term); +} + +Term * +new_term(int rows, int cols) +{ + size_t size = sizeof(Term) + rows*sizeof(Cell *) + rows*cols*sizeof(Cell); + Term *term = malloc(size); + if (!term) + return NULL; + term->rows = rows; + term->cols = cols; + term->addr = (Cell **) &term[1]; + term->cells = (Cell *) &term->addr[rows]; + reset(term); + return term; +} + +uint16_t +char_code(Term *term) +{ + int i; + uint16_t code = term->partial[0] & ((1 << (8 - term->parlen)) - 1); + for (i = 1; i < term->parlen; i++) + code = (code << 6) | (term->partial[i] & 0x3F); + return code; +} + +/* Move lines down and put a blank line at the top. */ +void +scroll_up(Term *term) +{ + int row, col; + Cell *addr; + + addr = term->addr[term->bot]; + for (row = term->bot; row > term->top; row--) + term->addr[row] = term->addr[row-1]; + term->addr[term->top] = addr; + for (col = 0; col < term->cols; col++) + term->addr[term->top][col] = BLANK; +} + +/* Move lines up and put a blank line at the bottom. */ +void +scroll_down(Term *term) +{ + int row, col; + Cell *addr; + + addr = term->addr[term->top]; + for (row = term->top; row < term->bot; row++) + term->addr[row] = term->addr[row+1]; + term->addr[term->bot] = addr; + for (col = 0; col < term->cols; col++) + term->addr[term->bot][col] = BLANK; +} + +void +addchar(Term *term, uint16_t code) +{ + Cell cell = (Cell) {code, term->attr, term->pair}; + if (term->mode & M_INSERT) { + Cell next; + int col; + for (col = term->col; col < term->cols; col++) { + next = term->addr[term->row][col]; + term->addr[term->row][col] = cell; + cell = next; + } + } else { + term->addr[term->row][term->col] = cell; + } + if (term->col < term->cols - 1) { + term->col++; + } else if (term->mode & M_AUTOWRAP) { + if (term->row < term->bot) + term->row++; + else + scroll_down(term); + term->col = 0; + } +} + +void +linefeed(Term *term) +{ + if (term->row == term->bot) + scroll_down(term); + else + term->row++; + if (term->mode & M_NEWLINE) + term->col = 0; +} + +void +ctrlchar(Term *term, uint8_t byte) +{ + switch (byte) { + case 0x08: + if (term->col) term->col--; + break; + case 0x09: + /* TODO: go to next tab stop or end of line */ + fprintf(stderr, "NYI: Control Character 0x09 (TAB)\n"); + break; + case 0x0A: case 0x0B: case 0x0C: + linefeed(term); + break; + case 0x0D: + term->col = 0; + break; + case 0x0E: + term->cs_index = 1; + break; + case 0x0F: + term->cs_index = 0; + break; + } +} + +void +escseq(Term *term, uint8_t byte) +{ + uint8_t first, second; + + if (term->parlen) { + first = *term->partial; + second = byte; + } else { + first = byte; + second = 0; + } + switch (first) { + case 'c': + reset(term); + break; + case 'D': + if (term->row == term->bot) + scroll_down(term); + else + term->row++; + break; + case 'E': + if (term->row == term->bot) { + scroll_down(term); + term->col = 0; + } else { + term->row++; + } + break; + case 'H': + /* TODO: set tab stop at current column */ + fprintf(stderr, "NYI: ESC Sequence H (HTS)\n"); + break; + case 'M': + if (term->row == term->top) + scroll_up(term); + else + term->row--; + break; + case 'Z': + /* TODO: DEC private identification */ + fprintf(stderr, "NYI: ESC Sequence Z (DECID)\n"); + break; + case '7': + save_misc(term); + break; + case '8': + load_misc(term); + break; + case '%': + /* TODO: select charset */ + fprintf(stderr, "NYI: ESC Sequence %% (character set selection)\n"); + break; + case '#': + /* TODO: DEC screen alignment test */ + fprintf(stderr, "NYI: ESC Sequence # (DECALN)\n"); + break; + case '(': + switch (second) { + case 'B': + term->cs_array[0] = CS_BMP; + break; + case '0': + term->cs_array[0] = CS_VTG; + break; + case 'U': + term->cs_array[0] = CS_437; + break; + case 'K': + fprintf(stderr, "UNS: user-defined mapping\n"); + } + break; + case ')': + switch (second) { + case 'B': + term->cs_array[1] = CS_BMP; + break; + case '0': + term->cs_array[1] = CS_VTG; + break; + case 'U': + term->cs_array[1] = CS_437; + break; + case 'K': + fprintf(stderr, "UNS: user-defined mapping\n"); + } + break; + case '>': + /* TODO: set numeric keypad mode */ + fprintf(stderr, "NYI: ESC Sequence > (DECPNM)\n"); + break; + case '=': + /* TODO: set application keypad mode */ + fprintf(stderr, "NYI: ESC Sequence = (DECPAM)\n"); + break; + default: + fprintf(stderr, "UNS: ESC Sequence %c\n", first); + } +} + +int +getparams(char *partial, int *params, int n) +{ + char *next; + char *token = partial; + int i = 0; + if (!*partial) { + params[0] = 0; + return 1; + } + while (i < n && *token) { + params[i++] = strtol(token, &next, 10); + if (*next) next++; + token = next; + } + if (i < n && *(token-1) == ';') + params[i++] = 0; + return i; +} + +#define SWITCH(T, F, V) (T)->mode = (V) ? (T)->mode | (F) : (T)->mode & ~(F) + +void +modeswitch(Term *term, int private, int number, int value) +{ + if (private) { + /* DEC modes */ + switch (number) { + case 1: + SWITCH(term, M_CURSORKEY, value); + break; + case 3: + /* TODO: 80/132 columns mode switch */ + fprintf(stderr, "NYI: DEC mode 3\n"); + break; + case 5: + SWITCH(term, M_REVERSE, value); + break; + case 6: + SWITCH(term, M_ORIGIN, value); + term->row = term->top; + term->col = 0; + break; + case 7: + SWITCH(term, M_AUTOWRAP, value); + break; + case 8: + SWITCH(term, M_AUTORPT, value); + break; + case 9: + SWITCH(term, M_MOUSEX10, value); + break; + case 25: + SWITCH(term, M_CURSORVIS, value); + break; + case 1000: + SWITCH(term, M_MOUSEX11, value); + break; + default: + fprintf(stderr, "UNS: DEC mode %d\n", number); + } + } else { + /* ANSI modes */ + switch (number) { + case 3: + SWITCH(term, M_DISPCTRL, value); + break; + case 4: + SWITCH(term, M_INSERT, value); + break; + case 20: + SWITCH(term, M_NEWLINE, value); + break; + default: + fprintf(stderr, "UNS: ANSI mode %d\n", number); + } + } +} + +void +sgr(Term *term, int number) +{ + switch (number) { + case 0: + term->attr = def_attr; + break; + case 1: + term->attr |= A_BOLD; + break; + case 2: + term->attr |= A_DIM; + break; + case 4: + term->attr |= A_UNDERLINE; + break; + case 5: + term->attr |= A_BLINK; + break; + case 7: + term->attr |= A_INVERSE; + break; + case 10: + /* TODO: reset toggle meta flag */ + term->cs_array[term->cs_index = 0] = CS_BMP; + term->mode &= ~M_DISPCTRL; + break; + case 11: + /* TODO: reset toggle meta flag */ + term->cs_array[term->cs_index] = CS_437; + term->mode |= M_DISPCTRL; + break; + case 12: + /* TODO: set toggle meta flag */ + term->cs_array[term->cs_index] = CS_437; + term->mode |= M_DISPCTRL; + break; + case 21: + term->attr &= ~A_BOLD; + break; + case 22: + term->attr &= ~A_DIM; + break; + case 24: + term->attr &= ~A_UNDERLINE; + break; + case 25: + term->attr &= ~A_BLINK; + break; + case 27: + term->attr &= ~A_INVERSE; + break; + case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: + term->pair = ((number - 30) << 4) | (term->pair & 0x0F); + break; + case 38: + term->attr |= A_UNDERLINE; + term->pair = (DEF_FORE << 4) | (term->pair & 0x0F); + break; + case 39: + term->attr &= ~A_UNDERLINE; + term->pair = (DEF_FORE << 4) | (term->pair & 0x0F); + break; + case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: + term->pair = (term->pair & 0xF0) | (number - 40); + break; + case 49: + term->pair = (term->pair & 0xF0) | DEF_BACK; + break; + default: + fprintf(stderr, "UNS: SGR %d\n", number); + } +} + +void +ctrlseq(Term *term, uint8_t byte) +{ + int private; + int n, k, k1; + int params[MAX_PARAMS]; + char *str; + int ra, rb, ca, cb; + int i, j; + Cell cell; + + term->partial[term->parlen] = '\0'; + if (*term->partial == '?') { + private = 1; + str = (char *) term->partial + 1; + } else { + private = 0; + str = (char *) term->partial; + } + n = getparams(str, params, MAX_PARAMS); + k = n ? *params : 0; + k1 = k ? k : 1; + switch (byte) { + case '@': + /* TODO: insert the indicated # of blank characters */ + fprintf(stderr, "BYI: Control Sequence @ (ICH)\n"); + break; + case 'A': + term->row -= k1; + break; + case 'B': case 'e': + term->row += k1; + break; + case 'C': case 'a': + term->col += k1; + break; + case 'D': + term->col -= k1; + break; + case 'E': + term->row += k1; + term->col = 0; + break; + case 'F': + term->row -= k1; + term->col = 0; + break; + case 'G': case '`': + term->col = k1 - 1; + break; + case 'H': case 'f': + if (n == 2) { + term->row = MAX(params[0], 1) - 1; + term->col = MAX(params[1], 1) - 1; + } else { + term->row = term->col = 0; + } + if (term->mode & M_ORIGIN) + term->row += term->top; + break; + case 'J': + ra = 0; rb = term->rows - 1; + ca = 0; cb = term->cols - 1; + if (k == 0) { + ra = term->row; + ca = term->col; + } else if (k == 1) { + rb = term->row; + cb = term->col; + } + for (j = ca; j < term->cols; j++) + term->addr[ra][j] = BLANK; + for (i = ra+1; i < rb; i++) { + for (j = 0; j < term->cols; j++) { + term->addr[i][j] = BLANK; + } + } + for (j = 0; j <= cb; j++) + term->addr[rb][j] = BLANK; + break; + case 'K': + ca = 0; cb = term->cols - 1; + if (k == 0) + ca = term->col; + else if (k == 1) + cb = term->col; + for (j = ca; j <= cb; j++) + term->addr[term->row][j] = BLANK; + break; + case 'L': + if (term->row < term->top || term->row > term->bot) + break; + /* This is implemented naively: + 1. temporarily change the top margin to current row; + 2. scroll up as many times as requested; + 3. restore top margin to previous value. */ + i = term->top; + term->top = term->row; + for (j = 0; j < k1; j++) + scroll_up(term); + term->top = i; + break; + case 'M': + if (term->row < term->top || term->row > term->bot) + break; + /* This is implemented naively: + 1. temporarily change the top margin to current row; + 2. scroll down as many times as requested; + 3. restore top margin to previous value. */ + /* TODO: + vt102-ug says: + "Lines added to bottom of screen have spaces with same character + attributes as last line moved up." + we need a more flexible scroll_down() to fix this. */ + i = term->top; + term->top = term->row; + for (j = 0; j < k1; j++) + scroll_down(term); + term->top = i; + break; + case 'P': + cell = term->addr[term->row][term->cols-1]; + cell.code = EMPTY; + for (j = term->col; j < term->cols-k1; j++) + term->addr[term->row][j] = term->addr[term->row][j+k1]; + for (j = term->cols-k1; j < term->cols; j++) + term->addr[term->row][j] = cell; + break; + case 'X': + for (j = 0; j < k1; j++) + term->addr[term->row][term->col+j] = BLANK; + break; + case 'c': + /* TODO: answer ESC [ ? 6 c */ + fprintf(stderr, "NYI: Control Sequence c (DA)\n"); + break; + case 'd': + term->row = k1 - 1; + break; + case 'g': + /* TODO: clear tab stop */ + fprintf(stderr, "NYI: Control Sequence g (TBC)\n"); + break; + case 'h': + for (i = 0; i < n; i++) + modeswitch(term, private, params[i], 1); + break; + case 'l': + for (i = 0; i < n; i++) + modeswitch(term, private, params[i], 0); + break; + case 'm': + for (i = 0; i < n; i++) + sgr(term, params[i]); + break; + case 'n': + /* TODO: status report */ + fprintf(stderr, "NYI: Control Sequence n (DSR)\n"); + break; + case 'q': + /* TODO: set keyboard LEDs */ + fprintf(stderr, "NYI: Control Sequence q (DECLL)\n"); + break; + case 'r': + if (n == 2) { + term->top = MAX(params[0], 1) - 1; + term->bot = MAX(params[1], 1) - 1; + } else { + term->top = 0; + term->bot = term->rows - 1; + } + term->row = term->mode & M_ORIGIN ? term->top : 0; + term->col = 0; + break; + case 's': + save_cursor(term); + break; + case 'u': + load_cursor(term); + break; + default: + fprintf(stderr, "UNS: Control Sequence %c\n", byte); + } +} + +#define CHARSET(T) ((T)->cs_array[(T)->cs_index]) +#define PARCAT(T, B) ((T)->partial[(T)->parlen++] = (B)) +#define RESET_STATE(T) do { (T)->state = S_ANY; (T)->parlen = 0; } while(0) +#define CHARLEN(B) ((B) < 0xE0 ? 2 : ((B) < 0xF0 ? 3 : 4)) + +void +parse(Term *term, uint8_t byte) +{ + int es; + + if (byte != 0x1B && byte < 0x20 && !(term->mode & M_DISPCTRL)) { + ctrlchar(term, byte); + } else { + switch (term->state) { + case S_ANY: + switch (byte) { + case 0x1B: + term->state = S_ESC; + break; + case 0x9B: + term->state = S_CSI; + break; + default: + switch (CHARSET(term)) { + case CS_BMP: + if (byte < 0x80) { + /* single-byte UTF-8, i.e. ASCII */ + addchar(term, byte); + } else { + term->unilen = CHARLEN(byte); + PARCAT(term, byte); + term->state = S_UNI; + } + break; + case CS_VTG: + addchar(term, cs_vtg[byte]); + break; + case CS_437: + addchar(term, cs_437[byte]); + break; + } + } + break; + case S_ESC: + es = 1; + if (!term->parlen) { + if (byte == 0x5B) { + term->state = S_CSI; + es = 0; + } else if (byte == 0x5D) { + term->state = S_OSC; + es = 0; + } + } + if (es) { + if (byte >= 0x20 && byte < 0x30) { + PARCAT(term, byte); + } else { + escseq(term, byte); + RESET_STATE(term); + } + } + break; + case S_CSI: + if (byte < 0x40 || byte >= 0x7F) { + PARCAT(term, byte); + } else { + ctrlseq(term, byte); + RESET_STATE(term); + } + break; + case S_OSC: + /* TODO: set/reset palette entries */ + fprintf(stderr, "NYI: Operating System Sequence\n"); + RESET_STATE(term); + break; + case S_UNI: + PARCAT(term, byte); + if (term->parlen == term->unilen) { + addchar(term, char_code(term)); + RESET_STATE(term); + } + break; + } + } +} diff --git a/term.h b/term.h new file mode 100644 index 0000000..3785f53 --- /dev/null +++ b/term.h @@ -0,0 +1,78 @@ +#define A_NORMAL 0x00 +#define A_BOLD 0x01 +#define A_DIM 0x02 +#define A_ITALIC 0x04 +#define A_UNDERLINE 0x08 +#define A_BLINK 0x10 +#define A_INVERSE 0x20 +#define A_INVISIBLE 0x40 +#define A_CROSSED 0x80 + +#define M_DISPCTRL 0x0001 +#define M_INSERT 0x0002 +#define M_NEWLINE 0x0004 +#define M_CURSORKEY 0x0008 +#define M_WIDETERM 0x0010 +#define M_REVERSE 0x0020 +#define M_ORIGIN 0x0040 +#define M_AUTOWRAP 0x0080 +#define M_AUTORPT 0x0100 +#define M_MOUSEX10 0x0200 +#define M_CURSORVIS 0x0400 +#define M_MOUSEX11 0x0800 + +#define EMPTY 0x0020 +#define BCE 1 +#if BCE + #define BLANK (Cell) {EMPTY, def_attr, def_pair} +#else + #define BLANK (Cell) {EMPTY, term->attr, term->pair} +#endif + +#define MAX_PARTIAL 0x100 +#define MAX_PARAMS 0x10 + +typedef struct Cell { + uint16_t code; + uint8_t attr; + uint8_t pair; +} Cell; + +typedef enum CharSet {CS_BMP, CS_VTG, CS_437} CharSet; +typedef enum State {S_ANY, S_ESC, S_CSI, S_OSC, S_UNI} State; + +typedef struct SaveCursor { + int row, col; +} SaveCursor; + +typedef struct SaveMisc { + int row, col; + int origin_on; + uint8_t attr; + uint8_t pair; + CharSet cs_array[2]; + int cs_index; +} SaveMisc; + +typedef struct Term { + int rows, cols; + int row, col; + int top, bot; + uint16_t mode; + uint8_t attr; + uint8_t pair; + Cell **addr; + Cell *cells; + CharSet cs_array[2]; + int cs_index; + SaveCursor save_cursor; + SaveMisc save_misc; + State state; + int parlen; + int unilen; + uint8_t partial[MAX_PARTIAL]; + uint8_t plt[0x30]; +} Term; + +Term *new_term(int rows, int cols); +void parse(Term *term, uint8_t byte);