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 }