From 05b87b1406d10a839a8864e13930f486f647fde4 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Tue, 23 Sep 2025 13:32:13 -0600 Subject: [PATCH] finish win32 gui support for double-wide characters --- src/win32/DwriteRenderer.zig | 6 +++++- src/win32/d3d11.zig | 20 ++++++++++++++++---- src/win32/gui.zig | 6 ++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/win32/DwriteRenderer.zig b/src/win32/DwriteRenderer.zig index 8ad34a0..39abee5 100644 --- a/src/win32/DwriteRenderer.zig +++ b/src/win32/DwriteRenderer.zig @@ -75,6 +75,7 @@ pub fn render( self: *const DwriteRenderer, font: Font, utf8: []const u8, + double_width: bool, ) void { var utf16_buf: [10]u16 = undefined; const utf16_len = std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable; @@ -85,7 +86,10 @@ pub fn render( const rect: win32.D2D_RECT_F = .{ .left = 0, .top = 0, - .right = @floatFromInt(font.cell_size.x), + .right = if (double_width) + @as(f32, @floatFromInt(font.cell_size.x)) * @as(f32, @floatFromInt(font.cell_size.x)) + else + @as(f32, @floatFromInt(font.cell_size.x)), .bottom = @floatFromInt(font.cell_size.y), }; self.render_target.BeginDraw(); diff --git a/src/win32/d3d11.zig b/src/win32/d3d11.zig index b1b88cf..dd1c5a2 100644 --- a/src/win32/d3d11.zig +++ b/src/win32/d3d11.zig @@ -149,7 +149,7 @@ pub const WindowState = struct { } // TODO: this should take a utf8 graphme instead - pub fn generateGlyph(state: *WindowState, font: Font, codepoint: u21, right_half: bool) u32 { + pub fn generateGlyph(state: *WindowState, font: Font, codepoint: u21, kind: enum { single, left, right }) u32 { // for now we'll just use 1 texture and leverage the entire thing const texture_cell_count: XY(u16) = getD3d11TextureMaxCellCount(font.cell_size); const texture_cell_count_total: u32 = @@ -184,6 +184,11 @@ pub const WindowState = struct { break :blk &(state.glyph_index_cache.?); }; + const right_half: bool = switch (kind) { + .single, .left => false, + .right => true, + }; + switch (glyph_index_cache.reserve( global.glyph_cache_arena.allocator(), codepoint, @@ -194,8 +199,11 @@ pub const WindowState = struct { // defer if (!render_success) state.glyph_index_cache.remove(reserved.index); const pos: XY(u16) = cellPosFromIndex(reserved.index, texture_cell_count.x); const coord = coordFromCellPos(font.cell_size, pos); - var staging_size = font.cell_size; - staging_size.x = if (right_half) staging_size.x * 2 else staging_size.x; + const staging_size: XY(u16) = .{ + // twice the width to handle double-wide glyphs + .x = font.cell_size.x * 2, + .y = font.cell_size.y, + }; const staging = global.staging_texture.update(staging_size); var utf8_buf: [7]u8 = undefined; const utf8_len: u3 = std.unicode.utf8Encode(codepoint, &utf8_buf) catch |e| std.debug.panic( @@ -205,6 +213,10 @@ pub const WindowState = struct { staging.text_renderer.render( font, utf8_buf[0..utf8_len], + switch (kind) { + .single => false, + .left, .right => true, + }, ); const box: win32.D3D11_BOX = .{ .left = if (right_half) font.cell_size.x else 0, @@ -292,7 +304,7 @@ pub fn paint( } const copy_col_count: u16 = @min(col_count, shader_col_count); - const blank_space_glyph_index = state.generateGlyph(font, ' ', false); + const blank_space_glyph_index = state.generateGlyph(font, ' ', .single); const cell_count: u32 = @as(u32, shader_col_count) * @as(u32, shader_row_count); state.shader_cells.updateCount(cell_count); diff --git a/src/win32/gui.zig b/src/win32/gui.zig index 485d1b4..bab2196 100644 --- a/src/win32/gui.zig +++ b/src/win32/gui.zig @@ -1083,6 +1083,7 @@ fn WndProc( ) catch |e| oom(e); var prev_width: usize = 1; var prev_cell: render.Cell = undefined; + var prev_codepoint: u21 = undefined; for (global.screen.buf, global.render_cells.items) |*screen_cell, *render_cell| { const width = screen_cell.char.width; const codepoint = if (std.unicode.utf8ValidateSlice(screen_cell.char.grapheme)) @@ -1091,16 +1092,17 @@ fn WndProc( std.unicode.replacement_character; if (prev_width > 1) { render_cell.* = prev_cell; - render_cell.glyph_index = state.render_state.generateGlyph(font, codepoint, true); + render_cell.glyph_index = state.render_state.generateGlyph(font, prev_codepoint, .right); } else { render_cell.* = .{ - .glyph_index = state.render_state.generateGlyph(font, codepoint, false), + .glyph_index = state.render_state.generateGlyph(font, codepoint, if (width == 1) .single else .left), .background = renderColorFromVaxis(screen_cell.style.bg), .foreground = renderColorFromVaxis(screen_cell.style.fg), }; } prev_width = width; prev_cell = render_cell.*; + prev_codepoint = codepoint; } render.paint( &state.render_state,