feat: switch text to UTF-8; add Unicode key events, dead keys & Czech layout; fix numpad navigation; add NumLock support

This commit is contained in:
daniel committed 2026-10-03 10:11:49 +02:00
1 parent 32660ddd1a
commit 2aa6360b3d
101 files changed
+3733 -1639

No files matched your search

+69 -13
View File
@@ -9,13 +9,14 @@
#include "gui/font.hpp"
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/utf8.h>
#include <Api/Syscall.hpp>
#include <montauk/capabilities.h>
namespace gui {
struct TermCell {
char ch;
uint32_t ch; // Unicode codepoint (every cell is one column wide)
Color fg;
Color bg;
};
@@ -58,6 +59,11 @@ struct TerminalState {
int csi_params[8];
int csi_param_count;
int csi_current_param;
// Partial UTF-8 sequence carried across terminal_feed calls (child output
// arrives in arbitrary chunks, so a character can straddle two reads).
char utf8_pending[4];
int utf8_pending_len;
};
// ==== Palette ====
@@ -244,6 +250,7 @@ static inline void terminal_init_cells(TerminalState* t, int cols, int rows, int
t->csi_private = false;
t->csi_param_count = 0;
t->csi_current_param = 0;
t->utf8_pending_len = 0;
t->child_pid = 0;
int total_cells = (rows + max_sb) * cols;
@@ -275,7 +282,7 @@ static inline void terminal_init(TerminalState* t, int cols, int rows) {
montauk::childio_settermsz(t->child_pid, cols, rows);
}
static inline void terminal_put_char(TerminalState* t, char ch) {
static inline void terminal_put_char(TerminalState* t, uint32_t ch) {
if (t->cursor_x >= t->cols) {
t->cursor_x = 0;
t->cursor_y++;
@@ -549,11 +556,61 @@ static inline void terminal_process_csi(TerminalState* t, char cmd) {
}
}
// Emit pending bytes that did not form a valid UTF-8 sequence: each one is a
// Windows-1252 character, matching how the GUI renders legacy text.
static inline void terminal_flush_utf8(TerminalState* t) {
for (int i = 0; i < t->utf8_pending_len; i++)
terminal_put_char(t, montauk::utf8::from_cp1252((uint8_t)t->utf8_pending[i]));
t->utf8_pending_len = 0;
}
static inline int terminal_utf8_expected(uint8_t lead) {
if (lead >= 0xC2 && lead <= 0xDF) return 2;
if (lead >= 0xE0 && lead <= 0xEF) return 3;
if (lead >= 0xF0 && lead <= 0xF4) return 4;
return 0;
}
// Feed one byte >= 0x80 of printable text through the UTF-8 assembler.
static inline void terminal_feed_high(TerminalState* t, uint8_t b) {
if (t->utf8_pending_len > 0) {
if ((b & 0xC0) == 0x80) {
t->utf8_pending[t->utf8_pending_len++] = (char)b;
int want = terminal_utf8_expected((uint8_t)t->utf8_pending[0]);
uint32_t cp = 0;
int n = montauk::utf8::sequence_length(t->utf8_pending,
t->utf8_pending_len, &cp);
if (n == want) {
terminal_put_char(t, cp);
t->utf8_pending_len = 0;
} else if (t->utf8_pending_len >= want) {
terminal_flush_utf8(t); // overlong / surrogate / out of range
}
return;
}
terminal_flush_utf8(t);
}
if (terminal_utf8_expected(b)) {
t->utf8_pending[0] = (char)b;
t->utf8_pending_len = 1;
} else {
terminal_put_char(t, montauk::utf8::from_cp1252(b));
}
}
static inline void terminal_feed(TerminalState* t, const char* data, int len) {
if (len > 0) t->dirty = true;
for (int i = 0; i < len; i++) {
char ch = data[i];
if (t->parse_state == TerminalState::STATE_NORMAL) {
if ((uint8_t)ch >= 0x80) {
terminal_feed_high(t, (uint8_t)ch);
continue;
}
if (t->utf8_pending_len > 0) terminal_flush_utf8(t);
}
switch (t->parse_state) {
case TerminalState::STATE_NORMAL:
if (ch == '\033') {
@@ -575,9 +632,8 @@ static inline void terminal_feed(TerminalState* t, const char* data, int len) {
while (t->cursor_x < next) {
terminal_put_char(t, ' ');
}
} else if (ch >= 32 || ch < 0) {
// Printable character (also treat high-bit chars as printable)
terminal_put_char(t, ch);
} else if (ch >= 32) {
terminal_put_char(t, (uint8_t)ch);
}
break;
@@ -641,9 +697,9 @@ static inline void terminal_fill_pixel_rect(uint32_t* pixels, int pw, int ph,
}
static inline void terminal_draw_bitmap_char(uint32_t* pixels, int pw, int ph,
int x, int y, unsigned char ch,
int x, int y, uint32_t ch,
uint32_t color) {
const uint8_t* glyph = &font_data[ch * FONT_HEIGHT];
const uint8_t* glyph = &font_data[cp437_index(ch) * FONT_HEIGHT];
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
int dy = y + fy;
if (dy >= ph) break;
@@ -669,14 +725,14 @@ static inline void terminal_draw_cell_pixels(const TermCell& cell,
cell.bg.to_pixel());
}
if (cell.ch <= 32 && cell.ch >= 0) return;
if (cell.ch <= 32) return;
if (use_ttf) {
int baseline = py + gc->ascent;
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
px, baseline, (unsigned char)cell.ch, cell.fg, gc);
px, baseline, (int)cell.ch, cell.fg, gc);
} else {
terminal_draw_bitmap_char(pixels, pw, ph, px, py, (unsigned char)cell.ch,
terminal_draw_bitmap_char(pixels, pw, ph, px, py, cell.ch,
cell.fg.to_pixel());
}
}
@@ -691,14 +747,14 @@ static inline void terminal_draw_cursor_pixels(const TermCell& cell,
terminal_fill_pixel_rect(pixels, pw, ph, px, py, cell_w, cell_h,
g_term_palette.cursor.to_pixel());
if (cell.ch <= 32 && cell.ch >= 0) return;
if (cell.ch <= 32) return;
if (use_ttf) {
int baseline = py + gc->ascent;
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
px, baseline, (unsigned char)cell.ch, g_term_palette.bg, gc);
px, baseline, (int)cell.ch, g_term_palette.bg, gc);
} else {
terminal_draw_bitmap_char(pixels, pw, ph, px, py, (unsigned char)cell.ch,
terminal_draw_bitmap_char(pixels, pw, ph, px, py, cell.ch,
g_term_palette.bg.to_pixel());
}
}