Commit: 9bb775b76c7d37fa7ac446de2962846393119d27
Parent: ca530c87fdb664a63b74100be963687d498772eb
Author: Randy Palamar
Date:   Sat, 16 Nov 2024 15:35:21 -0700
add GRAPHIC0 charset support
Diffstat:
| M | terminal.c | | | 64 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- | 
| M | util.c | | | 4 | +--- | 
| M | util.h | | | 14 | ++++++++++++-- | 
3 files changed, 70 insertions(+), 12 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -289,6 +289,10 @@ cursor_reset(Term *t)
 	t->cursor.style.fg   = g_colours.data[g_colours.fgidx];
 	t->cursor.style.bg   = g_colours.data[g_colours.bgidx];
 	t->cursor.style.attr = ATTR_NULL;
+
+	t->cursor.charset_index = 0;
+	for (u32 i = 0; i < ARRAY_COUNT(t->cursor.charsets); i++)
+		t->cursor.charsets[i] = CS_USA;
 }
 
 static void
@@ -1049,13 +1053,29 @@ handle_escape(Term *t, s8 *raw, Arena a)
 			}
 		}
 		break;
-	case '(': /* GZD4 -- set primary charset G0 */
-	case ')': /* G1D4 -- set secondary charset G1 */
-	case '*': /* G2D4 -- set tertiary charset G2 */
-	case '+': /* G3D4 -- set quaternary charset G3 */
-		if (!raw->len) result = 1;
-		else           get_ascii(raw);
-		break;
+	case '(':   /* GZD4 -- set primary charset G0 */
+	case ')':   /* G1D4 -- set secondary charset G1 */
+	case '*':   /* G2D4 -- set tertiary charset G2 */
+	case '+': { /* G3D4 -- set quaternary charset G3 */
+		i32 index = cp - '(';
+		/* TODO: should this really be done here? */
+		if (!raw->len) {
+			result = 1;
+		} else {
+			u32 cs = get_ascii(raw);
+			switch (cs) {
+			case '0': t->cursor.charsets[index] = CS_GRAPHIC0; break;
+			case 'B': t->cursor.charsets[index] = CS_USA;      break;
+			default:
+				stream_push_s8(&t->error_stream, s8("unhandled charset: "));
+				stream_push_byte(&t->error_stream, cs);
+				stream_push_byte(&t->error_stream, '\n');
+				os_write_err_msg(stream_to_s8(&t->error_stream));
+				t->error_stream.widx = 0;
+				break;
+			}
+		}
+	} break;
 	case '=': /* DECPAM -- application keypad */
 	case '>': /* DECPNM -- normal keypad mode */
 		/* TODO: MODE_APPKEYPAD */
@@ -1115,6 +1135,10 @@ push_control(Term *t, s8 *line, u32 cp, Arena a)
 	case '\b':
 		cursor_move_to(t, t->cursor.pos.y, t->cursor.pos.x - 1);
 		break;
+	case 0x0E: /* SO (LS1: Locking Shift 1) */
+	case 0x0F: /* SI (LS0: Locking Shift 0) */
+		t->cursor.charset_index = 1 - (cp - 0x0E);
+		break;
 	default:
 		stream_push_s8(&t->error_stream, s8("unknown control code: 0x"));
 		stream_push_hex_u64(&t->error_stream, cp);
@@ -1146,6 +1170,32 @@ push_normal_cp(Term *t, TermView *tv, u32 cp)
 		width = cg->tile_count;
 	}
 
+	/* NOTE: graphic character rxvt extension */
+	/*                                          | 0x41 - 0x7e
+	 *  "↑", "↓", "→", "←", "█", "▚", "☃",      | A - G
+	 *  0, 0, 0, 0, 0, 0, 0, 0,                 | H - O
+	 *  0, 0, 0, 0, 0, 0, 0, 0,                 | P - W
+	 *  0, 0, 0, 0, 0, 0, 0, " ",               | X - _
+	 *  "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", | ` - g
+	 *  "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", | h - o
+	 *  "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", | p - w
+	 *  "│", "≤", "≥", "π", "≠", "£", "·",      | x - ~
+	 */
+	static u16 graphic_0[62] = {
+		0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259A, 0x2603, 0x0000,
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x25C6,
+		0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x00B0, 0x00B1, 0x2424,
+		0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, 0x23BB,
+		0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, 0x2502,
+		0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00B7,
+	};
+
+	if (t->cursor.charsets[t->cursor.charset_index] == CS_GRAPHIC0 &&
+	    BETWEEN(cp, 0x41, 0x7e) && graphic_0[cp - 0x41])
+		cp = graphic_0[cp - 0x41];
+
 	/* NOTE: make this '>=' for fun in vis */
 	if (t->cursor.pos.x + width > t->size.w) {
 		/* NOTE: make space for character if mode enabled else
diff --git a/util.c b/util.c
@@ -302,10 +302,8 @@ stream_push_hex_u64(Stream *s, u64 n)
 		*--beg = hex[n & 0x0F];
 		n >>= 4;
 	}
-	if (beg == end) {
+	while (end - beg < 2)
 		*--beg = '0';
-		*--beg = '0';
-	}
 	stream_push_s8(s, (s8){.len = end - beg, .data = beg});
 }
 
diff --git a/util.h b/util.h
@@ -167,10 +167,15 @@ typedef struct {
 	u32 attr;
 } CellStyle;
 
-typedef struct {
-	iv2               pos;
+typedef __attribute__((aligned(32))) struct {
 	CellStyle         style;
 	enum cursor_state state;
+	iv2               pos;
+
+	/* NOTE: 4 available charset slots and the selected charset slot to look in.
+	 * xterm stores this as part of the cursor so we do here as well. */
+	u32 charset_index;
+	u8  charsets[4];
 } Cursor;
 
 typedef Cell *Row;
@@ -486,6 +491,11 @@ enum terminal_mode {
 	TM_UTF8      = 1 << 4,
 };
 
+enum charsets {
+	CS_USA,
+	CS_GRAPHIC0,
+};
+
 typedef struct RenderCtx {
 	RenderPushBuffer *rpb;
 	GLCtx            *gl;