mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #858 from mitchellh/wide-preedit
core, renderer: handle wide preedit chars
This commit is contained in:
@ -20,6 +20,7 @@ const builtin = @import("builtin");
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
const ziglyph = @import("ziglyph");
|
||||||
const renderer = @import("renderer.zig");
|
const renderer = @import("renderer.zig");
|
||||||
const termio = @import("termio.zig");
|
const termio = @import("termio.zig");
|
||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
@ -1020,12 +1021,24 @@ fn resize(self: *Surface, size: renderer.ScreenSize) !void {
|
|||||||
/// The core surface will NOT reset the preedit state on charCallback or
|
/// The core surface will NOT reset the preedit state on charCallback or
|
||||||
/// keyCallback and we rely completely on the apprt implementation to track
|
/// keyCallback and we rely completely on the apprt implementation to track
|
||||||
/// the preedit state correctly.
|
/// the preedit state correctly.
|
||||||
pub fn preeditCallback(self: *Surface, preedit: ?u21) !void {
|
pub fn preeditCallback(self: *Surface, preedit_: ?u21) !void {
|
||||||
|
const preedit: ?renderer.State.Preedit = if (preedit_) |cp| preedit: {
|
||||||
|
const width = ziglyph.display_width.codePointWidth(cp, .half);
|
||||||
|
|
||||||
|
// This shouldn't ever happen in well-behaved programs because
|
||||||
|
// preedit text must be visible, but we want to protect against it
|
||||||
|
// at this point.
|
||||||
|
if (width <= 0) break :preedit null;
|
||||||
|
|
||||||
|
break :preedit .{
|
||||||
|
.codepoint = cp,
|
||||||
|
.wide = width >= 2,
|
||||||
|
};
|
||||||
|
} else null;
|
||||||
|
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
self.renderer_state.preedit = if (preedit) |v| .{
|
self.renderer_state.preedit = preedit;
|
||||||
.codepoint = v,
|
|
||||||
} else null;
|
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1203,7 +1203,7 @@ fn rebuildCells(
|
|||||||
// a cursor cell then we invert the colors on that and add it in so
|
// a cursor cell then we invert the colors on that and add it in so
|
||||||
// that we can always see it.
|
// that we can always see it.
|
||||||
if (cursor_style_) |cursor_style| {
|
if (cursor_style_) |cursor_style| {
|
||||||
const real_cursor_cell = self.addCursor(screen, cursor_style);
|
const real_cursor_cell = self.addCursor(screen, cursor_style, preedit);
|
||||||
|
|
||||||
// If we have a preedit, we try to render the preedit text on top
|
// If we have a preedit, we try to render the preedit text on top
|
||||||
// of the cursor.
|
// of the cursor.
|
||||||
@ -1215,6 +1215,7 @@ fn rebuildCells(
|
|||||||
var cell: mtl_shaders.Cell = cursor_cell orelse
|
var cell: mtl_shaders.Cell = cursor_cell orelse
|
||||||
(real_cursor_cell orelse break :preedit).*;
|
(real_cursor_cell orelse break :preedit).*;
|
||||||
cell.color = .{ 0, 0, 0, 255 };
|
cell.color = .{ 0, 0, 0, 255 };
|
||||||
|
cell.cell_width = if (preedit_v.wide) 2 else 1;
|
||||||
|
|
||||||
// If preedit rendering succeeded then we don't want to
|
// If preedit rendering succeeded then we don't want to
|
||||||
// re-render the underlying cell fg
|
// re-render the underlying cell fg
|
||||||
@ -1427,10 +1428,18 @@ fn addCursor(
|
|||||||
self: *Metal,
|
self: *Metal,
|
||||||
screen: *terminal.Screen,
|
screen: *terminal.Screen,
|
||||||
cursor_style: renderer.CursorStyle,
|
cursor_style: renderer.CursorStyle,
|
||||||
|
preedit: ?renderer.State.Preedit,
|
||||||
) ?*const mtl_shaders.Cell {
|
) ?*const mtl_shaders.Cell {
|
||||||
// Add the cursor. We render the cursor over the wide character if
|
// Add the cursor. We render the cursor over the wide character if
|
||||||
// we're on the wide characer tail.
|
// we're on the wide characer tail.
|
||||||
const cell, const x = cell: {
|
const wide, const x = cell: {
|
||||||
|
// If we have preedit text, our width is based on that.
|
||||||
|
if (preedit) |p| {
|
||||||
|
if (p.codepoint > 0) {
|
||||||
|
break :cell .{ p.wide, screen.cursor.x };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The cursor goes over the screen cursor position.
|
// The cursor goes over the screen cursor position.
|
||||||
const cell = screen.getCell(
|
const cell = screen.getCell(
|
||||||
.active,
|
.active,
|
||||||
@ -1438,7 +1447,7 @@ fn addCursor(
|
|||||||
screen.cursor.x,
|
screen.cursor.x,
|
||||||
);
|
);
|
||||||
if (!cell.attrs.wide_spacer_tail or screen.cursor.x == 0)
|
if (!cell.attrs.wide_spacer_tail or screen.cursor.x == 0)
|
||||||
break :cell .{ cell, screen.cursor.x };
|
break :cell .{ cell.attrs.wide, screen.cursor.x };
|
||||||
|
|
||||||
// If we're part of a wide character, we move the cursor back to
|
// If we're part of a wide character, we move the cursor back to
|
||||||
// the actual character.
|
// the actual character.
|
||||||
@ -1446,7 +1455,7 @@ fn addCursor(
|
|||||||
.active,
|
.active,
|
||||||
screen.cursor.y,
|
screen.cursor.y,
|
||||||
screen.cursor.x - 1,
|
screen.cursor.x - 1,
|
||||||
), screen.cursor.x - 1 };
|
).attrs.wide, screen.cursor.x - 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const color = self.cursor_color orelse self.foreground_color;
|
const color = self.cursor_color orelse self.foreground_color;
|
||||||
@ -1466,7 +1475,7 @@ fn addCursor(
|
|||||||
self.alloc,
|
self.alloc,
|
||||||
font.sprite_index,
|
font.sprite_index,
|
||||||
@intFromEnum(sprite),
|
@intFromEnum(sprite),
|
||||||
.{ .cell_width = if (cell.attrs.wide) 2 else 1 },
|
.{ .cell_width = if (wide) 2 else 1 },
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.warn("error rendering cursor glyph err={}", .{err});
|
log.warn("error rendering cursor glyph err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
@ -1478,7 +1487,7 @@ fn addCursor(
|
|||||||
@as(f32, @floatFromInt(x)),
|
@as(f32, @floatFromInt(x)),
|
||||||
@as(f32, @floatFromInt(screen.cursor.y)),
|
@as(f32, @floatFromInt(screen.cursor.y)),
|
||||||
},
|
},
|
||||||
.cell_width = if (cell.attrs.wide) 2 else 1,
|
.cell_width = if (wide) 2 else 1,
|
||||||
.color = .{ color.r, color.g, color.b, alpha },
|
.color = .{ color.r, color.g, color.b, alpha },
|
||||||
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
||||||
.glyph_size = .{ glyph.width, glyph.height },
|
.glyph_size = .{ glyph.width, glyph.height },
|
||||||
|
@ -817,7 +817,7 @@ pub fn rebuildCells(
|
|||||||
// a cursor cell then we invert the colors on that and add it in so
|
// a cursor cell then we invert the colors on that and add it in so
|
||||||
// that we can always see it.
|
// that we can always see it.
|
||||||
if (cursor_style_) |cursor_style| {
|
if (cursor_style_) |cursor_style| {
|
||||||
const real_cursor_cell = self.addCursor(screen, cursor_style);
|
const real_cursor_cell = self.addCursor(screen, cursor_style, preedit);
|
||||||
|
|
||||||
// If we have a preedit, we try to render the preedit text on top
|
// If we have a preedit, we try to render the preedit text on top
|
||||||
// of the cursor.
|
// of the cursor.
|
||||||
@ -832,6 +832,7 @@ pub fn rebuildCells(
|
|||||||
cell.fg_g = 0;
|
cell.fg_g = 0;
|
||||||
cell.fg_b = 0;
|
cell.fg_b = 0;
|
||||||
cell.fg_a = 255;
|
cell.fg_a = 255;
|
||||||
|
cell.grid_width = if (preedit_v.wide) 2 else 1;
|
||||||
|
|
||||||
// If preedit rendering succeeded then we don't want to
|
// If preedit rendering succeeded then we don't want to
|
||||||
// re-render the underlying cell fg
|
// re-render the underlying cell fg
|
||||||
@ -871,10 +872,18 @@ fn addCursor(
|
|||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
screen: *terminal.Screen,
|
screen: *terminal.Screen,
|
||||||
cursor_style: renderer.CursorStyle,
|
cursor_style: renderer.CursorStyle,
|
||||||
|
preedit: ?renderer.State.Preedit,
|
||||||
) ?*const GPUCell {
|
) ?*const GPUCell {
|
||||||
// Add the cursor. We render the cursor over the wide character if
|
// Add the cursor. We render the cursor over the wide character if
|
||||||
// we're on the wide characer tail.
|
// we're on the wide characer tail.
|
||||||
const cell, const x = cell: {
|
const wide, const x = cell: {
|
||||||
|
// If we have preedit text, our width is based on that.
|
||||||
|
if (preedit) |p| {
|
||||||
|
if (p.codepoint > 0) {
|
||||||
|
break :cell .{ p.wide, screen.cursor.x };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The cursor goes over the screen cursor position.
|
// The cursor goes over the screen cursor position.
|
||||||
const cell = screen.getCell(
|
const cell = screen.getCell(
|
||||||
.active,
|
.active,
|
||||||
@ -882,7 +891,7 @@ fn addCursor(
|
|||||||
screen.cursor.x,
|
screen.cursor.x,
|
||||||
);
|
);
|
||||||
if (!cell.attrs.wide_spacer_tail or screen.cursor.x == 0)
|
if (!cell.attrs.wide_spacer_tail or screen.cursor.x == 0)
|
||||||
break :cell .{ cell, screen.cursor.x };
|
break :cell .{ cell.attrs.wide, screen.cursor.x };
|
||||||
|
|
||||||
// If we're part of a wide character, we move the cursor back to
|
// If we're part of a wide character, we move the cursor back to
|
||||||
// the actual character.
|
// the actual character.
|
||||||
@ -890,7 +899,7 @@ fn addCursor(
|
|||||||
.active,
|
.active,
|
||||||
screen.cursor.y,
|
screen.cursor.y,
|
||||||
screen.cursor.x - 1,
|
screen.cursor.x - 1,
|
||||||
), screen.cursor.x - 1 };
|
).attrs.wide, screen.cursor.x - 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const color = self.cursor_color orelse self.foreground_color;
|
const color = self.cursor_color orelse self.foreground_color;
|
||||||
@ -910,7 +919,7 @@ fn addCursor(
|
|||||||
self.alloc,
|
self.alloc,
|
||||||
font.sprite_index,
|
font.sprite_index,
|
||||||
@intFromEnum(sprite),
|
@intFromEnum(sprite),
|
||||||
.{ .cell_width = if (cell.attrs.wide) 2 else 1 },
|
.{ .cell_width = if (wide) 2 else 1 },
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.warn("error rendering cursor glyph err={}", .{err});
|
log.warn("error rendering cursor glyph err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
@ -920,7 +929,7 @@ fn addCursor(
|
|||||||
.mode = .fg,
|
.mode = .fg,
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(screen.cursor.y),
|
.grid_row = @intCast(screen.cursor.y),
|
||||||
.grid_width = if (cell.attrs.wide) 2 else 1,
|
.grid_width = if (wide) 2 else 1,
|
||||||
.fg_r = color.r,
|
.fg_r = color.r,
|
||||||
.fg_g = color.g,
|
.fg_g = color.g,
|
||||||
.fg_b = color.b,
|
.fg_b = color.b,
|
||||||
|
@ -34,4 +34,7 @@ pub const Preedit = struct {
|
|||||||
/// This can also be "0" in which case we can know we're in a preedit
|
/// This can also be "0" in which case we can know we're in a preedit
|
||||||
/// mode but we don't have any preedit text to render.
|
/// mode but we don't have any preedit text to render.
|
||||||
codepoint: u21 = 0,
|
codepoint: u21 = 0,
|
||||||
|
|
||||||
|
/// True if the preedit text should be rendered "wide" (two cells)
|
||||||
|
wide: bool = false,
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user