feat: TrueType (TTF) font rendering, many new desktop applications and DOOM support, among other improvements

This commit is contained in:
2026-02-20 22:46:41 +01:00
parent a0db5899ef
commit 596be25eaf
124 changed files with 9021 additions and 355 deletions
+101 -32
View File
@@ -423,6 +423,11 @@ static inline void terminal_feed(TerminalState* t, const char* data, int len) {
}
static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, int ph) {
int cell_w = mono_cell_width();
int cell_h = mono_cell_height();
bool use_ttf = fonts::mono && fonts::mono->valid;
GlyphCache* gc = use_ttf ? fonts::mono->get_cache(fonts::TERM_SIZE) : nullptr;
// Fill background
uint32_t bg_px = colors::TERM_BG.to_pixel();
int total = pw * ph;
@@ -431,8 +436,8 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
}
// Render each visible cell
int visible_rows = ph / FONT_HEIGHT;
int visible_cols = pw / FONT_WIDTH;
int visible_rows = ph / cell_h;
int visible_cols = pw / cell_w;
if (visible_rows > t->rows) visible_rows = t->rows;
if (visible_cols > t->cols) visible_cols = t->cols;
@@ -441,17 +446,16 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
int idx = r * t->cols + c;
TermCell& cell = t->cells[idx];
int px = c * FONT_WIDTH;
int py = r * FONT_HEIGHT;
int px = c * cell_w;
int py = r * cell_h;
uint32_t cell_bg = cell.bg.to_pixel();
uint32_t cell_fg = cell.fg.to_pixel();
// Draw cell background
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
for (int fy = 0; fy < cell_h; fy++) {
int dy = py + fy;
if (dy >= ph) break;
for (int fx = 0; fx < FONT_WIDTH; fx++) {
for (int fx = 0; fx < cell_w; fx++) {
int dx = px + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = cell_bg;
@@ -460,16 +464,23 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
// Draw character glyph
if (cell.ch > 32 || cell.ch < 0) {
const uint8_t* glyph = &font_data[(unsigned char)cell.ch * FONT_HEIGHT];
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
int dy = py + fy;
if (dy >= ph) break;
uint8_t bits = glyph[fy];
for (int fx = 0; fx < FONT_WIDTH; fx++) {
if (bits & (0x80 >> fx)) {
int dx = px + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = cell_fg;
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);
} else {
uint32_t cell_fg = cell.fg.to_pixel();
const uint8_t* glyph = &font_data[(unsigned char)cell.ch * FONT_HEIGHT];
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
int dy = py + fy;
if (dy >= ph) break;
uint8_t bits = glyph[fy];
for (int fx = 0; fx < FONT_WIDTH; fx++) {
if (bits & (0x80 >> fx)) {
int dx = px + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = cell_fg;
}
}
}
}
@@ -479,13 +490,13 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
// Draw cursor
if (t->cursor_visible && t->cursor_x < visible_cols && t->cursor_y < visible_rows) {
int cx = t->cursor_x * FONT_WIDTH;
int cy = t->cursor_y * FONT_HEIGHT;
int cx = t->cursor_x * cell_w;
int cy = t->cursor_y * cell_h;
uint32_t cursor_px = colors::WHITE.to_pixel();
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
for (int fy = 0; fy < cell_h; fy++) {
int dy = cy + fy;
if (dy >= ph) break;
for (int fx = 0; fx < FONT_WIDTH; fx++) {
for (int fx = 0; fx < cell_w; fx++) {
int dx = cx + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = cursor_px;
@@ -496,17 +507,23 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
int idx = t->cursor_y * t->cols + t->cursor_x;
char ch = t->cells[idx].ch;
if (ch > 32 || ch < 0) {
const uint8_t* glyph = &font_data[(unsigned char)ch * FONT_HEIGHT];
uint32_t black_px = colors::BLACK.to_pixel();
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
int dy = cy + fy;
if (dy >= ph) break;
uint8_t bits = glyph[fy];
for (int fx = 0; fx < FONT_WIDTH; fx++) {
if (bits & (0x80 >> fx)) {
int dx = cx + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = black_px;
if (use_ttf) {
int baseline = cy + gc->ascent;
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
cx, baseline, (unsigned char)ch, colors::BLACK, gc);
} else {
const uint8_t* glyph = &font_data[(unsigned char)ch * FONT_HEIGHT];
uint32_t black_px = colors::BLACK.to_pixel();
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
int dy = cy + fy;
if (dy >= ph) break;
uint8_t bits = glyph[fy];
for (int fx = 0; fx < FONT_WIDTH; fx++) {
if (bits & (0x80 >> fx)) {
int dx = cx + fx;
if (dx >= pw) break;
pixels[dy * pw + dx] = black_px;
}
}
}
}
@@ -515,6 +532,58 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
}
}
static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows) {
if (new_cols == t->cols && new_rows == t->rows) return;
if (new_cols < 1 || new_rows < 1) return;
int new_total = new_cols * new_rows;
TermCell* new_cells = (TermCell*)zenith::alloc(new_total * sizeof(TermCell));
TermCell* new_alt = (TermCell*)zenith::alloc(new_total * sizeof(TermCell));
// Clear new buffers
for (int i = 0; i < new_total; i++) {
new_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
new_alt[i] = {' ', colors::TERM_FG, colors::TERM_BG};
}
// Copy existing content (as much as fits)
int copy_rows = t->rows < new_rows ? t->rows : new_rows;
int copy_cols = t->cols < new_cols ? t->cols : new_cols;
// If cursor is beyond the new grid, scroll content up to keep cursor visible
int row_offset = 0;
if (t->cursor_y >= new_rows) {
row_offset = t->cursor_y - new_rows + 1;
}
for (int r = 0; r < copy_rows && (r + row_offset) < t->rows; r++) {
for (int c = 0; c < copy_cols; c++) {
new_cells[r * new_cols + c] = t->cells[(r + row_offset) * t->cols + c];
}
}
if (t->cells) zenith::mfree(t->cells);
if (t->alt_cells) zenith::mfree(t->alt_cells);
t->cells = new_cells;
t->alt_cells = new_alt;
int old_cols = t->cols;
t->cols = new_cols;
t->rows = new_rows;
// Adjust cursor position
t->cursor_y -= row_offset;
if (t->cursor_x >= new_cols) t->cursor_x = new_cols - 1;
if (t->cursor_y >= new_rows) t->cursor_y = new_rows - 1;
if (t->cursor_y < 0) t->cursor_y = 0;
// Notify child process of new terminal size
if (t->child_pid > 0) {
zenith::childio_settermsz(t->child_pid, new_cols, new_rows);
}
}
static inline void terminal_handle_key(TerminalState* t, const Zenith::KeyEvent& key) {
if (t->child_pid > 0) {
zenith::childio_writekey(t->child_pid, &key);