commit 8a26205

uint  ·  2026-05-14 20:00:30 +0000 UTC
parent dc078d3
add wcwidth-based wider characters support
3 files changed,  +59, -4
+3, -0
 1@@ -1,6 +1,7 @@
 2 #define RGFW_IMPLEMENTATION
 3 #include <errno.h>
 4 #include <fcntl.h>
 5+#include <locale.h>
 6 #include <stdbool.h>
 7 #include <stdint.h>
 8 #include <stdlib.h>
 9@@ -125,6 +126,8 @@ static void handle_key(RGFW_event ev)
10  */
11 static void init(void)
12 {
13+	setlocale(LC_CTYPE, "");
14+
15 	/* font */
16 	/* TODO: paths */
17 	if (font_load(&font, "./Xanh.ttf", 24.0) < 0)
+1, -0
1@@ -23,6 +23,7 @@ typedef struct {
2 	uint32_t       fg;
3 	uint32_t       bg;
4 	uint8_t        attr;
5+	uint8_t        width;
6 	bool           dmg; /* rune damaged */
7 } Rune;
8 
+55, -4
  1@@ -1,6 +1,7 @@
  2 #include <stdbool.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5+#include <wchar.h>
  6 
  7 #include <grapheme.h>
  8 
  9@@ -13,9 +14,11 @@ static void chars_insert(Term* t, Caret* c, int n);
 10 static void chars_delete(Term* t, Caret* c, int n);
 11 static void csi_dispatch(Term* t, Caret* c, unsigned char ch);
 12 static void csi_reset(Term* t);
 13+static int cp_width(uint32_t cp);
 14 static void erase_display(Term* t, Caret* c);
 15 static void erase_line(Term* t, Caret* c);
 16 static void reset_rune(Term* t, int x, int y);
 17+static void rune_prepare(Term* t, int x, int y);
 18 static void scroll_down(Term* t, int top, int bot, int n);
 19 static void scroll_up(Term* t, int top, int bot, int n);
 20 static void sgr(Term* t);
 21@@ -296,6 +299,19 @@ static void csi_reset(Term* t)
 22 	t->csi_idx = 0;
 23 }
 24 
 25+/** TODO
 26+ */
 27+static int cp_width(uint32_t cp)
 28+{
 29+	int w = wcwidth((wchar_t)cp);
 30+
 31+	if (w < 0)
 32+		return 1;
 33+	if (w > 2)
 34+		return 2;
 35+	return w;
 36+}
 37+
 38 /** TODO
 39  */
 40 static void erase_display(Term* t, Caret* c)
 41@@ -352,9 +368,20 @@ static void reset_rune(Term* t, int x, int y)
 42 	r->fg = t->fg;
 43 	r->bg = t->bg;
 44 	r->attr = t->attr;
 45+	r->width = 1;
 46 	r->dmg = true;
 47 }
 48 
 49+/** TODO
 50+ */
 51+static void rune_prepare(Term* t, int x, int y)
 52+{
 53+	if (x > 0 && RUNE(t, x, y).width == 0)
 54+		reset_rune(t, x - 1, y);
 55+	if (RUNE(t, x, y).width == 2 && x + 1 < t->cols)
 56+		reset_rune(t, x + 1, y);
 57+}
 58+
 59 /** TODO
 60  */
 61 static void scroll_down(Term* t, int top, int bot, int n)
 62@@ -492,6 +519,7 @@ void term_clear(Term* t, Caret* c)
 63 			rune->fg = t->fg;
 64 			rune->bg = t->bg;
 65 			rune->attr = t->attr;
 66+			rune->width = 1;
 67 			rune->dmg = true;
 68 		}
 69 	}
 70@@ -544,31 +572,53 @@ void term_putc(Term* t, Caret* c, uint32_t cp)
 71 		if (c->x >= t->cols)
 72 			term_putc(t, c, '\n');
 73 		break;
 74-	default:
 75+	default: {
 76+		int w;
 77+
 78 		if (cp < 32)
 79 			break;
 80+		w = cp_width(cp);
 81+		if (w == 0)
 82+			break;
 83+		if (w == 2 && t->cols < 2)
 84+			w = 1;
 85 
 86 		if (t->wrapnext) {
 87 			term_putc(t, c, '\n');
 88 			t->wrapnext = false;
 89 		}
 90+		if (w == 2 && c->x == t->cols - 1)
 91+			term_putc(t, c, '\n');
 92 
 93+		rune_prepare(t, c->x, c->y);
 94 		Rune* r = &RUNE(t, c->x, c->y);
 95 		if (r->cp != cp || r->fg != t->fg || r->bg != t->bg ||
 96-		    r->attr != t->attr) {
 97+		    r->attr != t->attr || r->width != w) {
 98 			r->cp = cp;
 99 			r->fg = t->fg;
100 			r->bg = t->bg;
101 			r->attr = t->attr;
102+			r->width = w;
103 			r->dmg = true;
104 		}
105-		if (c->x == t->cols - 1)
106+		if (w == 2) {
107+			Rune* spacer = &RUNE(t, c->x + 1, c->y);
108+			spacer->cp = ' ';
109+			spacer->fg = t->fg;
110+			spacer->bg = t->bg;
111+			spacer->attr = t->attr;
112+			spacer->width = 0;
113+			spacer->dmg = true;
114+		}
115+
116+		if (c->x + w >= t->cols)
117 			t->wrapnext = true;
118 		else
119-			c->x++;
120+			c->x += w;
121 
122 		break;
123 	}
124+	}
125 }
126 
127 int term_resize(Term* t, Caret* c, int cols, int rows)
128@@ -602,6 +652,7 @@ int term_resize(Term* t, Caret* c, int cols, int rows)
129 			r->fg = t->fg;
130 			r->bg = t->bg;
131 			r->attr = t->attr;
132+			r->width = 1;
133 			r->dmg = true;
134 		}
135 	}