diff --git a/src/Surface.zig b/src/Surface.zig index b6a74beed..f2d903a27 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -629,6 +629,8 @@ pub fn deinit(self: *Surface) void { self.alloc.destroy(v); } + // Clean up our render state + if (self.renderer_state.preedit) |p| self.alloc.free(p.codepoints); self.alloc.destroy(self.renderer_state.mutex); self.config.deinit(); @@ -1087,7 +1089,10 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { defer self.renderer_state.mutex.unlock(); // We always clear our prior preedit - self.renderer_state.preedit = null; + if (self.renderer_state.preedit) |p| { + self.alloc.free(p.codepoints); + self.renderer_state.preedit = null; + } // If we have no text, we're done. We queue a render in case we cleared // a prior preedit (likely). @@ -1101,7 +1106,9 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { var it = view.iterator(); // Allocate the codepoints slice - var preedit: renderer.State.Preedit = .{}; + const Codepoint = renderer.State.Preedit.Codepoint; + var codepoints: std.ArrayListUnmanaged(Codepoint) = .{}; + defer codepoints.deinit(self.alloc); while (it.nextCodepoint()) |cp| { const width = ziglyph.display_width.codePointWidth(cp, .half); @@ -1110,21 +1117,18 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { // Let's just ignore it. if (width <= 0) continue; - preedit.codepoints[preedit.len] = .{ .codepoint = cp, .wide = width >= 2 }; - preedit.len += 1; - - // This is a strange edge case. We have a generous buffer for - // preedit text but if we exceed it, we just truncate. - if (preedit.len >= preedit.codepoints.len) { - log.warn("preedit text is longer than our buffer, truncating", .{}); - break; - } + try codepoints.append( + self.alloc, + .{ .codepoint = cp, .wide = width >= 2 }, + ); } // If we have no codepoints, then we're done. - if (preedit.len == 0) return; + if (codepoints.items.len == 0) return; - self.renderer_state.preedit = preedit; + self.renderer_state.preedit = .{ + .codepoints = try codepoints.toOwnedSlice(self.alloc), + }; try self.queueRender(); } diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 9c87149c6..6f1a4d23e 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -632,6 +632,14 @@ pub fn updateFrame( cursor_blink_visible, ); + // Get our preedit state + const preedit: ?renderer.State.Preedit = preedit: { + if (cursor_style == null) break :preedit null; + const p = state.preedit orelse break :preedit null; + break :preedit try p.clone(self.alloc); + }; + errdefer if (preedit) |p| p.deinit(self.alloc); + // If we have Kitty graphics data, we enter a SLOW SLOW SLOW path. // We only do this if the Kitty image state is dirty meaning only if // it changes. @@ -644,11 +652,14 @@ pub fn updateFrame( .selection = selection, .screen = screen_copy, .mouse = state.mouse, - .preedit = if (cursor_style != null) state.preedit else null, + .preedit = preedit, .cursor_style = cursor_style, }; }; - defer critical.screen.deinit(); + defer { + critical.screen.deinit(); + if (critical.preedit) |p| p.deinit(self.alloc); + } // Build our GPU cells try self.rebuildCells( @@ -1572,7 +1583,7 @@ fn rebuildCells( if (preedit) |preedit_v| { const range = preedit_range.?; var x = range.x[0]; - for (preedit_v.codepoints[0..preedit_v.len]) |cp| { + for (preedit_v.codepoints) |cp| { self.addPreeditCell(cp, x, range.y) catch |err| { log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{ x, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index aa7936c03..4909b255e 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -697,6 +697,14 @@ pub fn updateFrame( cursor_blink_visible, ); + // Get our preedit state + const preedit: ?renderer.State.Preedit = preedit: { + if (cursor_style == null) break :preedit null; + const p = state.preedit orelse break :preedit null; + break :preedit try p.clone(self.alloc); + }; + errdefer if (preedit) |p| p.deinit(self.alloc); + // If we have Kitty graphics data, we enter a SLOW SLOW SLOW path. // We only do this if the Kitty image state is dirty meaning only if // it changes. @@ -709,11 +717,14 @@ pub fn updateFrame( .selection = selection, .screen = screen_copy, .mouse = state.mouse, - .preedit = if (cursor_style != null) state.preedit else null, + .preedit = preedit, .cursor_style = cursor_style, }; }; - defer critical.screen.deinit(); + defer { + critical.screen.deinit(); + if (critical.preedit) |p| p.deinit(self.alloc); + } // Grab our draw mutex if we have it and update our data { @@ -1083,7 +1094,7 @@ pub fn rebuildCells( if (preedit) |preedit_v| { const range = preedit_range.?; var x = range.x[0]; - for (preedit_v.codepoints[0..preedit_v.len]) |cp| { + for (preedit_v.codepoints) |cp| { self.addPreeditCell(cp, x, range.y) catch |err| { log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{ x, diff --git a/src/renderer/State.zig b/src/renderer/State.zig index bde0f0f52..ec4c6f971 100644 --- a/src/renderer/State.zig +++ b/src/renderer/State.zig @@ -38,11 +38,8 @@ pub const Mouse = struct { /// The pre-edit state. See Surface.preeditCallback for more information. pub const Preedit = struct { - /// The codepoints to render as preedit text. We allow up to 16 codepoints - /// as a sort of arbitrary limit. If we experience a realisitic use case - /// where we need more please open an issue. - codepoints: [16]Codepoint = undefined, - len: u8 = 0, + /// The codepoints to render as preedit text. + codepoints: []Codepoint, /// A single codepoint to render as preedit text. pub const Codepoint = struct { @@ -50,10 +47,22 @@ pub const Preedit = struct { wide: bool = false, }; + /// Deinit this preedit that was cre + pub fn deinit(self: *const Preedit, alloc: Allocator) void { + alloc.free(self.codepoints); + } + + /// Allocate a copy of this preedit in the given allocator.. + pub fn clone(self: *const Preedit, alloc: Allocator) !Preedit { + return .{ + .codepoints = try alloc.dupe(Codepoint, self.codepoints), + }; + } + /// The width in cells of all codepoints in the preedit. pub fn width(self: *const Preedit) usize { var result: usize = 0; - for (self.codepoints[0..self.len]) |cp| { + for (self.codepoints) |cp| { result += if (cp.wide) 2 else 1; }