mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
Merge pull request #421 from mitchellh/mode12
Cursor state (style, blinking, visiblity, focus) refactor, implement mode 12 support
This commit is contained in:
@ -418,10 +418,6 @@ pub fn init(
|
|||||||
.renderer_thread = render_thread,
|
.renderer_thread = render_thread,
|
||||||
.renderer_state = .{
|
.renderer_state = .{
|
||||||
.mutex = mutex,
|
.mutex = mutex,
|
||||||
.cursor = .{
|
|
||||||
.style = .default,
|
|
||||||
.visible = true,
|
|
||||||
},
|
|
||||||
.terminal = &self.io.terminal,
|
.terminal = &self.io.terminal,
|
||||||
},
|
},
|
||||||
.renderer_thr = undefined,
|
.renderer_thr = undefined,
|
||||||
|
@ -106,7 +106,7 @@ pub const Config = struct {
|
|||||||
/// In order to fix it, we probably would want to add something similar to Kitty's
|
/// In order to fix it, we probably would want to add something similar to Kitty's
|
||||||
/// shell integration options (no-cursor). For more information see:
|
/// shell integration options (no-cursor). For more information see:
|
||||||
/// https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.shell_integration
|
/// https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.shell_integration
|
||||||
@"cursor-style": CursorStyle = .bar,
|
@"cursor-style": terminal.Cursor.Style = .bar,
|
||||||
|
|
||||||
/// Whether the cursor shall blink
|
/// Whether the cursor shall blink
|
||||||
@"cursor-style-blink": bool = true,
|
@"cursor-style-blink": bool = true,
|
||||||
@ -1475,22 +1475,6 @@ pub const ShellIntegration = enum {
|
|||||||
zsh,
|
zsh,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Available options for `cursor-style`. Blinking is configured with
|
|
||||||
/// the `cursor-style-blink` option.
|
|
||||||
pub const CursorStyle = enum {
|
|
||||||
bar,
|
|
||||||
block,
|
|
||||||
underline,
|
|
||||||
|
|
||||||
pub fn toTerminalCursorStyle(self: CursorStyle, blinks: bool) terminal.CursorStyle {
|
|
||||||
return switch (self) {
|
|
||||||
.bar => if (blinks) .blinking_bar else .steady_bar,
|
|
||||||
.block => if (blinks) .blinking_block else .steady_block,
|
|
||||||
.underline => if (blinks) .blinking_underline else .steady_underline,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wasm API.
|
// Wasm API.
|
||||||
pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
|
pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct {
|
||||||
const wasm = @import("os/wasm.zig");
|
const wasm = @import("os/wasm.zig");
|
||||||
|
@ -64,11 +64,6 @@ padding: renderer.Options.Padding,
|
|||||||
/// True if the window is focused
|
/// True if the window is focused
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
||||||
/// Whether the cursor is visible or not. This is used to control cursor
|
|
||||||
/// blinking.
|
|
||||||
cursor_visible: bool,
|
|
||||||
cursor_style: renderer.CursorStyle,
|
|
||||||
|
|
||||||
/// The current set of cells to render. This is rebuilt on every frame
|
/// The current set of cells to render. This is rebuilt on every frame
|
||||||
/// but we keep this around so that we don't reallocate. Each set of
|
/// but we keep this around so that we don't reallocate. Each set of
|
||||||
/// cells goes into a separate shader.
|
/// cells goes into a separate shader.
|
||||||
@ -108,7 +103,6 @@ pub const DerivedConfig = struct {
|
|||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
font_features: std.ArrayList([]const u8),
|
font_features: std.ArrayList([]const u8),
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
cursor_style: terminal.CursorStyle,
|
|
||||||
cursor_text: ?terminal.color.RGB,
|
cursor_text: ?terminal.color.RGB,
|
||||||
background: terminal.color.RGB,
|
background: terminal.color.RGB,
|
||||||
background_opacity: f64,
|
background_opacity: f64,
|
||||||
@ -137,7 +131,6 @@ pub const DerivedConfig = struct {
|
|||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
|
|
||||||
.cursor_style = config.@"cursor-style".toTerminalCursorStyle(config.@"cursor-style-blink"),
|
|
||||||
.cursor_text = if (config.@"cursor-text") |txt|
|
.cursor_text = if (config.@"cursor-text") |txt|
|
||||||
txt.toTerminalRGB()
|
txt.toTerminalRGB()
|
||||||
else
|
else
|
||||||
@ -252,8 +245,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.screen_size = null,
|
.screen_size = null,
|
||||||
.padding = options.padding,
|
.padding = options.padding,
|
||||||
.focused = true,
|
.focused = true,
|
||||||
.cursor_visible = true,
|
|
||||||
.cursor_style = .box,
|
|
||||||
|
|
||||||
// Render state
|
// Render state
|
||||||
.cells_bg = .{},
|
.cells_bg = .{},
|
||||||
@ -385,13 +376,6 @@ pub fn setFocus(self: *Metal, focus: bool) !void {
|
|||||||
self.focused = focus;
|
self.focused = focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called to toggle the blink state of the cursor
|
|
||||||
///
|
|
||||||
/// Must be called on the render thread.
|
|
||||||
pub fn blinkCursor(self: *Metal, reset: bool) void {
|
|
||||||
self.cursor_visible = reset or !self.cursor_visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the new font size.
|
/// Set the new font size.
|
||||||
///
|
///
|
||||||
/// Must be called on the render thread.
|
/// Must be called on the render thread.
|
||||||
@ -452,6 +436,7 @@ pub fn render(
|
|||||||
self: *Metal,
|
self: *Metal,
|
||||||
surface: *apprt.Surface,
|
surface: *apprt.Surface,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
|
cursor_blink_visible: bool,
|
||||||
) !void {
|
) !void {
|
||||||
_ = surface;
|
_ = surface;
|
||||||
|
|
||||||
@ -460,8 +445,8 @@ pub fn render(
|
|||||||
bg: terminal.color.RGB,
|
bg: terminal.color.RGB,
|
||||||
selection: ?terminal.Selection,
|
selection: ?terminal.Selection,
|
||||||
screen: terminal.Screen,
|
screen: terminal.Screen,
|
||||||
draw_cursor: bool,
|
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
|
cursor_style: ?renderer.CursorStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update all our data as tightly as possible within the mutex.
|
// Update all our data as tightly as possible within the mutex.
|
||||||
@ -475,46 +460,6 @@ pub fn render(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the terminal state isn't requesting any particular style,
|
|
||||||
// then use the configured style.
|
|
||||||
const selected_cursor_style = style: {
|
|
||||||
if (state.cursor.style != .default) break :style state.cursor.style;
|
|
||||||
if (self.config.cursor_style != .default) break :style self.config.cursor_style;
|
|
||||||
break :style .blinking_block;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.cursor_visible = visible: {
|
|
||||||
// If the cursor is explicitly not visible in the state,
|
|
||||||
// then it is not visible.
|
|
||||||
if (!state.cursor.visible) break :visible false;
|
|
||||||
|
|
||||||
// If we are in preedit, then we always show the cursor
|
|
||||||
if (state.preedit != null) break :visible true;
|
|
||||||
|
|
||||||
// If the cursor isn't a blinking style, then never blink.
|
|
||||||
if (!selected_cursor_style.blinking()) break :visible true;
|
|
||||||
|
|
||||||
// If we're not focused, our cursor is always visible so that
|
|
||||||
// we can show the hollow box.
|
|
||||||
if (!self.focused) break :visible true;
|
|
||||||
|
|
||||||
// Otherwise, adhere to our current state.
|
|
||||||
break :visible self.cursor_visible;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The cursor style only needs to be set if its visible.
|
|
||||||
if (self.cursor_visible) {
|
|
||||||
self.cursor_style = cursor_style: {
|
|
||||||
// If we have a dead key preedit then we always use a box style
|
|
||||||
if (state.preedit != null) break :cursor_style .box;
|
|
||||||
|
|
||||||
// If we aren't focused, we use a hollow box
|
|
||||||
if (!self.focused) break :cursor_style .box_hollow;
|
|
||||||
|
|
||||||
break :cursor_style renderer.CursorStyle.fromTerminal(selected_cursor_style) orelse .box;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap bg/fg if the terminal is reversed
|
// Swap bg/fg if the terminal is reversed
|
||||||
const bg = self.config.background;
|
const bg = self.config.background;
|
||||||
const fg = self.config.foreground;
|
const fg = self.config.foreground;
|
||||||
@ -550,7 +495,11 @@ pub fn render(
|
|||||||
null;
|
null;
|
||||||
|
|
||||||
// Whether to draw our cursor or not.
|
// Whether to draw our cursor or not.
|
||||||
const draw_cursor = self.cursor_visible and state.terminal.screen.viewportIsBottom();
|
const cursor_style = renderer.cursorStyle(
|
||||||
|
state,
|
||||||
|
self.focused,
|
||||||
|
cursor_blink_visible,
|
||||||
|
);
|
||||||
|
|
||||||
// If we have Kitty graphics data, we enter a SLOW SLOW SLOW path.
|
// 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
|
// We only do this if the Kitty image state is dirty meaning only if
|
||||||
@ -563,8 +512,8 @@ pub fn render(
|
|||||||
.bg = self.config.background,
|
.bg = self.config.background,
|
||||||
.selection = selection,
|
.selection = selection,
|
||||||
.screen = screen_copy,
|
.screen = screen_copy,
|
||||||
.draw_cursor = draw_cursor,
|
.preedit = if (cursor_style != null) state.preedit else null,
|
||||||
.preedit = if (draw_cursor) state.preedit else null,
|
.cursor_style = cursor_style,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
defer critical.screen.deinit();
|
defer critical.screen.deinit();
|
||||||
@ -577,8 +526,8 @@ pub fn render(
|
|||||||
try self.rebuildCells(
|
try self.rebuildCells(
|
||||||
critical.selection,
|
critical.selection,
|
||||||
&critical.screen,
|
&critical.screen,
|
||||||
critical.draw_cursor,
|
|
||||||
critical.preedit,
|
critical.preedit,
|
||||||
|
critical.cursor_style,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get our drawable (CAMetalDrawable)
|
// Get our drawable (CAMetalDrawable)
|
||||||
@ -1114,8 +1063,8 @@ fn rebuildCells(
|
|||||||
self: *Metal,
|
self: *Metal,
|
||||||
term_selection: ?terminal.Selection,
|
term_selection: ?terminal.Selection,
|
||||||
screen: *terminal.Screen,
|
screen: *terminal.Screen,
|
||||||
draw_cursor: bool,
|
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
|
cursor_style_: ?renderer.CursorStyle,
|
||||||
) !void {
|
) !void {
|
||||||
// Bg cells at most will need space for the visible screen size
|
// Bg cells at most will need space for the visible screen size
|
||||||
self.cells_bg.clearRetainingCapacity();
|
self.cells_bg.clearRetainingCapacity();
|
||||||
@ -1145,8 +1094,7 @@ fn rebuildCells(
|
|||||||
// True if this is the row with our cursor. There are a lot of conditions
|
// True if this is the row with our cursor. There are a lot of conditions
|
||||||
// here because the reasons we need to know this are primarily to invert.
|
// here because the reasons we need to know this are primarily to invert.
|
||||||
//
|
//
|
||||||
// - If we aren't drawing the cursor (draw_cursor), then we don't need
|
// - If we aren't drawing the cursor then we don't need to change our rendering.
|
||||||
// to change our rendering.
|
|
||||||
// - If the cursor is not visible, then we don't need to change rendering.
|
// - If the cursor is not visible, then we don't need to change rendering.
|
||||||
// - If the cursor style is not a box, then we don't need to change
|
// - If the cursor style is not a box, then we don't need to change
|
||||||
// rendering because it'll never fully overlap a glyph.
|
// rendering because it'll never fully overlap a glyph.
|
||||||
@ -1157,11 +1105,12 @@ fn rebuildCells(
|
|||||||
// - If this y doesn't match our cursor y then we don't need to
|
// - If this y doesn't match our cursor y then we don't need to
|
||||||
// change rendering.
|
// change rendering.
|
||||||
//
|
//
|
||||||
const cursor_row = draw_cursor and
|
const cursor_row = if (cursor_style_) |cursor_style|
|
||||||
self.cursor_visible and
|
cursor_style == .block and
|
||||||
self.cursor_style == .box and
|
|
||||||
screen.viewportIsBottom() and
|
screen.viewportIsBottom() and
|
||||||
y == screen.cursor.y;
|
y == screen.cursor.y
|
||||||
|
else
|
||||||
|
false;
|
||||||
|
|
||||||
// True if we want to do font shaping around the cursor. We want to
|
// True if we want to do font shaping around the cursor. We want to
|
||||||
// do font shaping as long as the cursor is enabled.
|
// do font shaping as long as the cursor is enabled.
|
||||||
@ -1234,8 +1183,8 @@ fn rebuildCells(
|
|||||||
// Add the cursor at the end so that it overlays everything. If we have
|
// Add the cursor at the end so that it overlays everything. If we have
|
||||||
// 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 (draw_cursor) {
|
if (cursor_style_) |cursor_style| {
|
||||||
const real_cursor_cell = self.addCursor(screen);
|
const real_cursor_cell = self.addCursor(screen, cursor_style);
|
||||||
|
|
||||||
// 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.
|
||||||
@ -1452,7 +1401,11 @@ pub fn updateCell(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addCursor(self: *Metal, screen: *terminal.Screen) ?*const mtl_shaders.Cell {
|
fn addCursor(
|
||||||
|
self: *Metal,
|
||||||
|
screen: *terminal.Screen,
|
||||||
|
cursor_style: renderer.CursorStyle,
|
||||||
|
) ?*const mtl_shaders.Cell {
|
||||||
// Add the cursor
|
// Add the cursor
|
||||||
const cell = screen.getCell(
|
const cell = screen.getCell(
|
||||||
.active,
|
.active,
|
||||||
@ -1466,9 +1419,9 @@ fn addCursor(self: *Metal, screen: *terminal.Screen) ?*const mtl_shaders.Cell {
|
|||||||
.b = 0xFF,
|
.b = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const sprite: font.Sprite = switch (self.cursor_style) {
|
const sprite: font.Sprite = switch (cursor_style) {
|
||||||
.box => .cursor_rect,
|
.block => .cursor_rect,
|
||||||
.box_hollow => .cursor_hollow_rect,
|
.block_hollow => .cursor_hollow_rect,
|
||||||
.bar => .cursor_bar,
|
.bar => .cursor_bar,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,11 +83,6 @@ texture_color: gl.Texture,
|
|||||||
font_group: *font.GroupCache,
|
font_group: *font.GroupCache,
|
||||||
font_shaper: font.Shaper,
|
font_shaper: font.Shaper,
|
||||||
|
|
||||||
/// Whether the cursor is visible or not. This is used to control cursor
|
|
||||||
/// blinking.
|
|
||||||
cursor_visible: bool,
|
|
||||||
cursor_style: renderer.CursorStyle,
|
|
||||||
|
|
||||||
/// True if the window is focused
|
/// True if the window is focused
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
||||||
@ -237,7 +232,6 @@ pub const DerivedConfig = struct {
|
|||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
font_features: std.ArrayList([]const u8),
|
font_features: std.ArrayList([]const u8),
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
cursor_style: terminal.CursorStyle,
|
|
||||||
cursor_text: ?terminal.color.RGB,
|
cursor_text: ?terminal.color.RGB,
|
||||||
background: terminal.color.RGB,
|
background: terminal.color.RGB,
|
||||||
background_opacity: f64,
|
background_opacity: f64,
|
||||||
@ -266,7 +260,6 @@ pub const DerivedConfig = struct {
|
|||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
|
|
||||||
.cursor_style = config.@"cursor-style".toTerminalCursorStyle(config.@"cursor-style-blink"),
|
|
||||||
.cursor_text = if (config.@"cursor-text") |txt|
|
.cursor_text = if (config.@"cursor-text") |txt|
|
||||||
txt.toTerminalRGB()
|
txt.toTerminalRGB()
|
||||||
else
|
else
|
||||||
@ -435,8 +428,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL {
|
|||||||
.texture_color = tex_color,
|
.texture_color = tex_color,
|
||||||
.font_group = options.font_group,
|
.font_group = options.font_group,
|
||||||
.font_shaper = shaper,
|
.font_shaper = shaper,
|
||||||
.cursor_visible = true,
|
|
||||||
.cursor_style = .box,
|
|
||||||
.draw_background = options.config.background,
|
.draw_background = options.config.background,
|
||||||
.focused = true,
|
.focused = true,
|
||||||
.padding = options.padding,
|
.padding = options.padding,
|
||||||
@ -609,13 +600,6 @@ pub fn setFocus(self: *OpenGL, focus: bool) !void {
|
|||||||
self.focused = focus;
|
self.focused = focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called to toggle the blink state of the cursor
|
|
||||||
///
|
|
||||||
/// Must be called on the render thread.
|
|
||||||
pub fn blinkCursor(self: *OpenGL, reset: bool) void {
|
|
||||||
self.cursor_visible = reset or !self.cursor_visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the new font size.
|
/// Set the new font size.
|
||||||
///
|
///
|
||||||
/// Must be called on the render thread.
|
/// Must be called on the render thread.
|
||||||
@ -695,6 +679,7 @@ pub fn render(
|
|||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
surface: *apprt.Surface,
|
surface: *apprt.Surface,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
|
cursor_blink_visible: bool,
|
||||||
) !void {
|
) !void {
|
||||||
// Data we extract out of the critical area.
|
// Data we extract out of the critical area.
|
||||||
const Critical = struct {
|
const Critical = struct {
|
||||||
@ -702,8 +687,8 @@ pub fn render(
|
|||||||
active_screen: terminal.Terminal.ScreenType,
|
active_screen: terminal.Terminal.ScreenType,
|
||||||
selection: ?terminal.Selection,
|
selection: ?terminal.Selection,
|
||||||
screen: terminal.Screen,
|
screen: terminal.Screen,
|
||||||
draw_cursor: bool,
|
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
|
cursor_style: ?renderer.CursorStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update all our data as tightly as possible within the mutex.
|
// Update all our data as tightly as possible within the mutex.
|
||||||
@ -717,46 +702,6 @@ pub fn render(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the terminal state isn't requesting any particular style,
|
|
||||||
// then use the configured style.
|
|
||||||
const selected_cursor_style = style: {
|
|
||||||
if (state.cursor.style != .default) break :style state.cursor.style;
|
|
||||||
if (self.config.cursor_style != .default) break :style self.config.cursor_style;
|
|
||||||
break :style .blinking_block;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.cursor_visible = visible: {
|
|
||||||
// If the cursor is explicitly not visible in the state,
|
|
||||||
// then it is not visible.
|
|
||||||
if (!state.cursor.visible) break :visible false;
|
|
||||||
|
|
||||||
// If we are in preedit, then we always show the cursor
|
|
||||||
if (state.preedit != null) break :visible true;
|
|
||||||
|
|
||||||
// If the cursor isn't a blinking style, then never blink.
|
|
||||||
if (!selected_cursor_style.blinking()) break :visible true;
|
|
||||||
|
|
||||||
// If we're not focused, our cursor is always visible so that
|
|
||||||
// we can show the hollow box.
|
|
||||||
if (!self.focused) break :visible true;
|
|
||||||
|
|
||||||
// Otherwise, adhere to our current state.
|
|
||||||
break :visible self.cursor_visible;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The cursor style only needs to be set if its visible.
|
|
||||||
if (self.cursor_visible) {
|
|
||||||
self.cursor_style = cursor_style: {
|
|
||||||
// If we have a dead key preedit then we always use a box style
|
|
||||||
if (state.preedit != null) break :cursor_style .box;
|
|
||||||
|
|
||||||
// If we aren't focused, we use a hollow box
|
|
||||||
if (!self.focused) break :cursor_style .box_hollow;
|
|
||||||
|
|
||||||
break :cursor_style renderer.CursorStyle.fromTerminal(selected_cursor_style) orelse .box;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap bg/fg if the terminal is reversed
|
// Swap bg/fg if the terminal is reversed
|
||||||
const bg = self.config.background;
|
const bg = self.config.background;
|
||||||
const fg = self.config.foreground;
|
const fg = self.config.foreground;
|
||||||
@ -792,15 +737,19 @@ pub fn render(
|
|||||||
null;
|
null;
|
||||||
|
|
||||||
// Whether to draw our cursor or not.
|
// Whether to draw our cursor or not.
|
||||||
const draw_cursor = self.cursor_visible and state.terminal.screen.viewportIsBottom();
|
const cursor_style = renderer.cursorStyle(
|
||||||
|
state,
|
||||||
|
self.focused,
|
||||||
|
cursor_blink_visible,
|
||||||
|
);
|
||||||
|
|
||||||
break :critical .{
|
break :critical .{
|
||||||
.gl_bg = self.config.background,
|
.gl_bg = self.config.background,
|
||||||
.active_screen = state.terminal.active_screen,
|
.active_screen = state.terminal.active_screen,
|
||||||
.selection = selection,
|
.selection = selection,
|
||||||
.screen = screen_copy,
|
.screen = screen_copy,
|
||||||
.draw_cursor = draw_cursor,
|
.preedit = if (cursor_style != null) state.preedit else null,
|
||||||
.preedit = if (draw_cursor) state.preedit else null,
|
.cursor_style = cursor_style,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
defer critical.screen.deinit();
|
defer critical.screen.deinit();
|
||||||
@ -818,8 +767,8 @@ pub fn render(
|
|||||||
critical.active_screen,
|
critical.active_screen,
|
||||||
critical.selection,
|
critical.selection,
|
||||||
&critical.screen,
|
&critical.screen,
|
||||||
critical.draw_cursor,
|
|
||||||
critical.preedit,
|
critical.preedit,
|
||||||
|
critical.cursor_style,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,8 +798,8 @@ pub fn rebuildCells(
|
|||||||
active_screen: terminal.Terminal.ScreenType,
|
active_screen: terminal.Terminal.ScreenType,
|
||||||
term_selection: ?terminal.Selection,
|
term_selection: ?terminal.Selection,
|
||||||
screen: *terminal.Screen,
|
screen: *terminal.Screen,
|
||||||
draw_cursor: bool,
|
|
||||||
preedit: ?renderer.State.Preedit,
|
preedit: ?renderer.State.Preedit,
|
||||||
|
cursor_style_: ?renderer.CursorStyle,
|
||||||
) !void {
|
) !void {
|
||||||
const t = trace(@src());
|
const t = trace(@src());
|
||||||
defer t.end();
|
defer t.end();
|
||||||
@ -906,11 +855,12 @@ pub fn rebuildCells(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// See Metal.zig
|
// See Metal.zig
|
||||||
const cursor_row = draw_cursor and
|
const cursor_row = if (cursor_style_) |cursor_style|
|
||||||
self.cursor_visible and
|
cursor_style == .block and
|
||||||
self.cursor_style == .box and
|
|
||||||
screen.viewportIsBottom() and
|
screen.viewportIsBottom() and
|
||||||
y == screen.cursor.y;
|
y == screen.cursor.y
|
||||||
|
else
|
||||||
|
false;
|
||||||
|
|
||||||
// True if we want to do font shaping around the cursor. We want to
|
// True if we want to do font shaping around the cursor. We want to
|
||||||
// do font shaping as long as the cursor is enabled.
|
// do font shaping as long as the cursor is enabled.
|
||||||
@ -1003,8 +953,8 @@ pub fn rebuildCells(
|
|||||||
// Add the cursor at the end so that it overlays everything. If we have
|
// Add the cursor at the end so that it overlays everything. If we have
|
||||||
// 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 (draw_cursor) {
|
if (cursor_style_) |cursor_style| {
|
||||||
const real_cursor_cell = self.addCursor(screen);
|
const real_cursor_cell = self.addCursor(screen, cursor_style);
|
||||||
|
|
||||||
// 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.
|
||||||
@ -1052,7 +1002,11 @@ pub fn rebuildCells(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addCursor(self: *OpenGL, screen: *terminal.Screen) ?*const GPUCell {
|
fn addCursor(
|
||||||
|
self: *OpenGL,
|
||||||
|
screen: *terminal.Screen,
|
||||||
|
cursor_style: renderer.CursorStyle,
|
||||||
|
) ?*const GPUCell {
|
||||||
// Add the cursor
|
// Add the cursor
|
||||||
const cell = screen.getCell(
|
const cell = screen.getCell(
|
||||||
.active,
|
.active,
|
||||||
@ -1066,9 +1020,9 @@ fn addCursor(self: *OpenGL, screen: *terminal.Screen) ?*const GPUCell {
|
|||||||
.b = 0xFF,
|
.b = 0xFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const sprite: font.Sprite = switch (self.cursor_style) {
|
const sprite: font.Sprite = switch (cursor_style) {
|
||||||
.box => .cursor_rect,
|
.block => .cursor_rect,
|
||||||
.box_hollow => .cursor_hollow_rect,
|
.block_hollow => .cursor_hollow_rect,
|
||||||
.bar => .cursor_bar,
|
.bar => .cursor_bar,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,9 +11,6 @@ const renderer = @import("../renderer.zig");
|
|||||||
/// state (i.e. the terminal, devmode, etc. values).
|
/// state (i.e. the terminal, devmode, etc. values).
|
||||||
mutex: *std.Thread.Mutex,
|
mutex: *std.Thread.Mutex,
|
||||||
|
|
||||||
/// Cursor configuration for rendering
|
|
||||||
cursor: Cursor,
|
|
||||||
|
|
||||||
/// The terminal data.
|
/// The terminal data.
|
||||||
terminal: *terminal.Terminal,
|
terminal: *terminal.Terminal,
|
||||||
|
|
||||||
@ -23,17 +20,6 @@ terminal: *terminal.Terminal,
|
|||||||
/// a future exercise.
|
/// a future exercise.
|
||||||
preedit: ?Preedit = null,
|
preedit: ?Preedit = null,
|
||||||
|
|
||||||
pub const Cursor = struct {
|
|
||||||
/// Current cursor style. This can be set by escape sequences. To get
|
|
||||||
/// the default style, the config has to be referenced.
|
|
||||||
style: terminal.CursorStyle = .default,
|
|
||||||
|
|
||||||
/// Whether the cursor is visible at all. This should not be used for
|
|
||||||
/// "blink" settings, see "blink" for that. This is used to turn the
|
|
||||||
/// cursor ON or OFF.
|
|
||||||
visible: bool = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The pre-edit state. See Surface.preeditCallback for more information.
|
/// The pre-edit state. See Surface.preeditCallback for more information.
|
||||||
pub const Preedit = struct {
|
pub const Preedit = struct {
|
||||||
/// The codepoint to render as preedit text. We only support single
|
/// The codepoint to render as preedit text. We only support single
|
||||||
|
@ -48,6 +48,11 @@ cursor_h: xev.Timer,
|
|||||||
cursor_c: xev.Completion = .{},
|
cursor_c: xev.Completion = .{},
|
||||||
cursor_c_cancel: xev.Completion = .{},
|
cursor_c_cancel: xev.Completion = .{},
|
||||||
|
|
||||||
|
/// This is true when a blinking cursor should be visible and false
|
||||||
|
/// when it should not be visible. This is toggled on a timer by the
|
||||||
|
/// thread automatically.
|
||||||
|
cursor_blink_visible: bool = false,
|
||||||
|
|
||||||
/// The surface we're rendering to.
|
/// The surface we're rendering to.
|
||||||
surface: *apprt.Surface,
|
surface: *apprt.Surface,
|
||||||
|
|
||||||
@ -220,7 +225,7 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
// If we're focused, we immediately show the cursor again
|
// If we're focused, we immediately show the cursor again
|
||||||
// and then restart the timer.
|
// and then restart the timer.
|
||||||
if (self.cursor_c.state() != .active) {
|
if (self.cursor_c.state() != .active) {
|
||||||
self.renderer.blinkCursor(true);
|
self.cursor_blink_visible = true;
|
||||||
self.cursor_h.run(
|
self.cursor_h.run(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
&self.cursor_c,
|
&self.cursor_c,
|
||||||
@ -234,7 +239,7 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.reset_cursor_blink => {
|
.reset_cursor_blink => {
|
||||||
self.renderer.blinkCursor(true);
|
self.cursor_blink_visible = true;
|
||||||
if (self.cursor_c.state() == .active) {
|
if (self.cursor_c.state() == .active) {
|
||||||
self.cursor_h.reset(
|
self.cursor_h.reset(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
@ -317,7 +322,11 @@ fn renderCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
t.renderer.render(t.surface, t.state) catch |err|
|
t.renderer.render(
|
||||||
|
t.surface,
|
||||||
|
t.state,
|
||||||
|
t.cursor_blink_visible,
|
||||||
|
) catch |err|
|
||||||
log.warn("error rendering err={}", .{err});
|
log.warn("error rendering err={}", .{err});
|
||||||
|
|
||||||
// If we're doing single-threaded GPU calls then we also wake up the
|
// If we're doing single-threaded GPU calls then we also wake up the
|
||||||
@ -356,7 +365,7 @@ fn cursorTimerCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
t.renderer.blinkCursor(false);
|
t.cursor_blink_visible = !t.cursor_blink_visible;
|
||||||
t.wakeup.notify() catch {};
|
t.wakeup.notify() catch {};
|
||||||
|
|
||||||
t.cursor_h.run(&t.loop, &t.cursor_c, CURSOR_BLINK_INTERVAL, Thread, t, cursorTimerCallback);
|
t.cursor_h.run(&t.loop, &t.cursor_c, CURSOR_BLINK_INTERVAL, Thread, t, cursorTimerCallback);
|
||||||
|
@ -1,19 +1,145 @@
|
|||||||
|
const std = @import("std");
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
|
const State = @import("State.zig");
|
||||||
|
|
||||||
/// Available cursor styles for drawing that renderers must support.
|
/// Available cursor styles for drawing that renderers must support.
|
||||||
|
/// This is a superset of terminal cursor styles since the renderer supports
|
||||||
|
/// some additional cursor states such as the hollow block.
|
||||||
pub const CursorStyle = enum {
|
pub const CursorStyle = enum {
|
||||||
box,
|
block,
|
||||||
box_hollow,
|
block_hollow,
|
||||||
bar,
|
bar,
|
||||||
|
|
||||||
/// Create a cursor style from the terminal style request.
|
/// Create a cursor style from the terminal style request.
|
||||||
pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle {
|
pub fn fromTerminal(style: terminal.Cursor.Style) ?CursorStyle {
|
||||||
return switch (style) {
|
return switch (style) {
|
||||||
.blinking_block, .steady_block => .box,
|
.bar => .bar,
|
||||||
.blinking_bar, .steady_bar => .bar,
|
.block => .block,
|
||||||
.blinking_underline, .steady_underline => null, // TODO
|
.underline => null, // TODO
|
||||||
.default => .box,
|
|
||||||
else => null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns the cursor style to use for the current render state or null
|
||||||
|
/// if a cursor should not be rendered at all.
|
||||||
|
pub fn cursorStyle(
|
||||||
|
state: *State,
|
||||||
|
focused: bool,
|
||||||
|
blink_visible: bool,
|
||||||
|
) ?CursorStyle {
|
||||||
|
// The cursor is only at the bottom of the viewport. If we aren't
|
||||||
|
// at the bottom, we never render the cursor.
|
||||||
|
if (!state.terminal.screen.viewportIsBottom()) return null;
|
||||||
|
|
||||||
|
// If we are in preedit, then we always show the cursor
|
||||||
|
if (state.preedit != null) return .block;
|
||||||
|
|
||||||
|
// If the cursor is explicitly not visible by terminal mode, then false.
|
||||||
|
if (!state.terminal.modes.get(.cursor_visible)) return null;
|
||||||
|
|
||||||
|
// If we're not focused, our cursor is always visible so that
|
||||||
|
// we can show the hollow box.
|
||||||
|
if (!focused) return .block_hollow;
|
||||||
|
|
||||||
|
// If the cursor is blinking and our blink state is not visible,
|
||||||
|
// then we don't show the cursor.
|
||||||
|
if (state.terminal.modes.get(.cursor_blinking) and !blink_visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we use whatever the terminal wants.
|
||||||
|
return CursorStyle.fromTerminal(state.terminal.screen.cursor.style);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "cursor: default uses configured style" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||||
|
defer term.deinit(alloc);
|
||||||
|
|
||||||
|
term.screen.cursor.style = .bar;
|
||||||
|
term.modes.set(.cursor_blinking, true);
|
||||||
|
|
||||||
|
var state: State = .{
|
||||||
|
.mutex = undefined,
|
||||||
|
.terminal = &term,
|
||||||
|
.preedit = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try testing.expect(cursorStyle(&state, true, true) == .bar);
|
||||||
|
try testing.expect(cursorStyle(&state, false, true) == .block_hollow);
|
||||||
|
try testing.expect(cursorStyle(&state, false, false) == .block_hollow);
|
||||||
|
try testing.expect(cursorStyle(&state, true, false) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "cursor: blinking disabled" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||||
|
defer term.deinit(alloc);
|
||||||
|
|
||||||
|
term.screen.cursor.style = .bar;
|
||||||
|
term.modes.set(.cursor_blinking, false);
|
||||||
|
|
||||||
|
var state: State = .{
|
||||||
|
.mutex = undefined,
|
||||||
|
.terminal = &term,
|
||||||
|
.preedit = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try testing.expect(cursorStyle(&state, true, true) == .bar);
|
||||||
|
try testing.expect(cursorStyle(&state, true, false) == .bar);
|
||||||
|
try testing.expect(cursorStyle(&state, false, true) == .block_hollow);
|
||||||
|
try testing.expect(cursorStyle(&state, false, false) == .block_hollow);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "cursor: explictly not visible" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||||
|
defer term.deinit(alloc);
|
||||||
|
|
||||||
|
term.screen.cursor.style = .bar;
|
||||||
|
term.modes.set(.cursor_visible, false);
|
||||||
|
term.modes.set(.cursor_blinking, false);
|
||||||
|
|
||||||
|
var state: State = .{
|
||||||
|
.mutex = undefined,
|
||||||
|
.terminal = &term,
|
||||||
|
.preedit = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try testing.expect(cursorStyle(&state, true, true) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, true, false) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, false, true) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, false, false) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "cursor: always block with preedit" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||||
|
defer term.deinit(alloc);
|
||||||
|
|
||||||
|
var state: State = .{
|
||||||
|
.mutex = undefined,
|
||||||
|
.terminal = &term,
|
||||||
|
.preedit = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
// In any bool state
|
||||||
|
try testing.expect(cursorStyle(&state, false, false) == .block);
|
||||||
|
try testing.expect(cursorStyle(&state, true, false) == .block);
|
||||||
|
try testing.expect(cursorStyle(&state, true, true) == .block);
|
||||||
|
try testing.expect(cursorStyle(&state, false, true) == .block);
|
||||||
|
|
||||||
|
// If we're scrolled though, then we don't show the cursor.
|
||||||
|
for (0..100) |_| try term.index();
|
||||||
|
try term.scrollViewport(.{ .top = {} });
|
||||||
|
|
||||||
|
// In any bool state
|
||||||
|
try testing.expect(cursorStyle(&state, false, false) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, true, false) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, true, true) == null);
|
||||||
|
try testing.expect(cursorStyle(&state, false, true) == null);
|
||||||
|
}
|
||||||
|
@ -68,16 +68,26 @@ const log = std.log.scoped(.screen);
|
|||||||
|
|
||||||
/// Cursor represents the cursor state.
|
/// Cursor represents the cursor state.
|
||||||
pub const Cursor = struct {
|
pub const Cursor = struct {
|
||||||
// x, y where the cursor currently exists (0-indexed). This x/y is
|
/// x, y where the cursor currently exists (0-indexed). This x/y is
|
||||||
// always the offset in the active area.
|
/// always the offset in the active area.
|
||||||
x: usize = 0,
|
x: usize = 0,
|
||||||
y: usize = 0,
|
y: usize = 0,
|
||||||
|
|
||||||
// pen is the current cell styling to apply to new cells.
|
/// The visual style of the cursor. This defaults to block because
|
||||||
|
/// it has to default to something, but users of this struct are
|
||||||
|
/// encouraged to set their own default.
|
||||||
|
style: Style = .block,
|
||||||
|
|
||||||
|
/// pen is the current cell styling to apply to new cells.
|
||||||
pen: Cell = .{ .char = 0 },
|
pen: Cell = .{ .char = 0 },
|
||||||
|
|
||||||
// The last column flag (LCF) used to do soft wrapping.
|
/// The last column flag (LCF) used to do soft wrapping.
|
||||||
pending_wrap: bool = false,
|
pending_wrap: bool = false,
|
||||||
|
|
||||||
|
/// The visual style of the cursor. Whether or not it blinks
|
||||||
|
/// is determined by mode 12 (modes.zig). This mode is synchronized
|
||||||
|
/// with CSI q, the same as xterm.
|
||||||
|
pub const Style = enum { bar, block, underline };
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This is a single item within the storage buffer. We use a union to
|
/// This is a single item within the storage buffer. We use a union to
|
||||||
|
@ -20,7 +20,8 @@ pub const Parser = @import("Parser.zig");
|
|||||||
pub const Selection = @import("Selection.zig");
|
pub const Selection = @import("Selection.zig");
|
||||||
pub const Screen = @import("Screen.zig");
|
pub const Screen = @import("Screen.zig");
|
||||||
pub const Stream = stream.Stream;
|
pub const Stream = stream.Stream;
|
||||||
pub const CursorStyle = ansi.CursorStyle;
|
pub const Cursor = Screen.Cursor;
|
||||||
|
pub const CursorStyleReq = ansi.CursorStyle;
|
||||||
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
||||||
pub const DeviceStatusReq = ansi.DeviceStatusReq;
|
pub const DeviceStatusReq = ansi.DeviceStatusReq;
|
||||||
pub const Mode = modes.Mode;
|
pub const Mode = modes.Mode;
|
||||||
|
@ -154,7 +154,8 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "origin", .value = 6 },
|
.{ .name = "origin", .value = 6 },
|
||||||
.{ .name = "autowrap", .value = 7, .default = true },
|
.{ .name = "autowrap", .value = 7, .default = true },
|
||||||
.{ .name = "mouse_event_x10", .value = 9 },
|
.{ .name = "mouse_event_x10", .value = 9 },
|
||||||
.{ .name = "cursor_visible", .value = 25 },
|
.{ .name = "cursor_blinking", .value = 12 },
|
||||||
|
.{ .name = "cursor_visible", .value = 25, .default = true },
|
||||||
.{ .name = "enable_mode_3", .value = 40 },
|
.{ .name = "enable_mode_3", .value = 40 },
|
||||||
.{ .name = "keypad_keys", .value = 66 },
|
.{ .name = "keypad_keys", .value = 66 },
|
||||||
.{ .name = "mouse_event_normal", .value = 1000 },
|
.{ .name = "mouse_event_normal", .value = 1000 },
|
||||||
|
@ -63,6 +63,11 @@ surface_mailbox: apprt.surface.Mailbox,
|
|||||||
/// The cached grid size whenever a resize is called.
|
/// The cached grid size whenever a resize is called.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
|
/// The default cursor style. We need to know this so that we can set
|
||||||
|
/// it when a CSI q with default is called.
|
||||||
|
default_cursor_style: terminal.Cursor.Style,
|
||||||
|
default_cursor_blink: bool,
|
||||||
|
|
||||||
/// The data associated with the currently running thread.
|
/// The data associated with the currently running thread.
|
||||||
data: ?*EventData,
|
data: ?*EventData,
|
||||||
|
|
||||||
@ -72,6 +77,8 @@ data: ?*EventData,
|
|||||||
pub const DerivedConfig = struct {
|
pub const DerivedConfig = struct {
|
||||||
palette: terminal.color.Palette,
|
palette: terminal.color.Palette,
|
||||||
image_storage_limit: usize,
|
image_storage_limit: usize,
|
||||||
|
cursor_style: terminal.Cursor.Style,
|
||||||
|
cursor_blink: bool,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc_gpa: Allocator,
|
alloc_gpa: Allocator,
|
||||||
@ -82,6 +89,8 @@ pub const DerivedConfig = struct {
|
|||||||
return .{
|
return .{
|
||||||
.palette = config.palette.value,
|
.palette = config.palette.value,
|
||||||
.image_storage_limit = config.@"image-storage-limit",
|
.image_storage_limit = config.@"image-storage-limit",
|
||||||
|
.cursor_style = config.@"cursor-style",
|
||||||
|
.cursor_blink = config.@"cursor-style-blink",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +119,9 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
|||||||
try term.screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
try term.screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
||||||
try term.secondary_screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
try term.secondary_screen.kitty_images.setLimit(alloc, opts.config.image_storage_limit);
|
||||||
|
|
||||||
|
// Set default cursor blink settings
|
||||||
|
term.modes.set(.cursor_blinking, opts.config.cursor_blink);
|
||||||
|
|
||||||
var subprocess = try Subprocess.init(alloc, opts);
|
var subprocess = try Subprocess.init(alloc, opts);
|
||||||
errdefer subprocess.deinit();
|
errdefer subprocess.deinit();
|
||||||
|
|
||||||
@ -126,6 +138,8 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
|||||||
.renderer_mailbox = opts.renderer_mailbox,
|
.renderer_mailbox = opts.renderer_mailbox,
|
||||||
.surface_mailbox = opts.surface_mailbox,
|
.surface_mailbox = opts.surface_mailbox,
|
||||||
.grid_size = opts.grid_size,
|
.grid_size = opts.grid_size,
|
||||||
|
.default_cursor_style = opts.config.cursor_style,
|
||||||
|
.default_cursor_blink = opts.config.cursor_blink,
|
||||||
.data = null,
|
.data = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -188,6 +202,8 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
|||||||
.ev = ev_data_ptr,
|
.ev = ev_data_ptr,
|
||||||
.terminal = &self.terminal,
|
.terminal = &self.terminal,
|
||||||
.grid_size = &self.grid_size,
|
.grid_size = &self.grid_size,
|
||||||
|
.default_cursor_style = self.default_cursor_style,
|
||||||
|
.default_cursor_blink = self.default_cursor_blink,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -253,6 +269,18 @@ pub fn changeConfig(self: *Exec, config: *DerivedConfig) !void {
|
|||||||
// since we decode all palette colors to RGB on usage.
|
// since we decode all palette colors to RGB on usage.
|
||||||
self.terminal.color_palette = config.palette;
|
self.terminal.color_palette = config.palette;
|
||||||
|
|
||||||
|
// Update our default cursor style
|
||||||
|
self.default_cursor_style = config.cursor_style;
|
||||||
|
self.default_cursor_blink = config.cursor_blink;
|
||||||
|
|
||||||
|
// If we have event data, then update our active stream too
|
||||||
|
if (self.data) |data| {
|
||||||
|
data.terminal_stream.handler.changeDefaultCursor(
|
||||||
|
config.cursor_style,
|
||||||
|
config.cursor_blink,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the image size limits
|
// Set the image size limits
|
||||||
try self.terminal.screen.kitty_images.setLimit(
|
try self.terminal.screen.kitty_images.setLimit(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
@ -1108,6 +1136,12 @@ const StreamHandler = struct {
|
|||||||
/// to wake up the writer.
|
/// to wake up the writer.
|
||||||
writer_messaged: bool = false,
|
writer_messaged: bool = false,
|
||||||
|
|
||||||
|
/// The default cursor state. This is used with CSI q. This is
|
||||||
|
/// set to true when we're currently in the default cursor state.
|
||||||
|
default_cursor: bool = true,
|
||||||
|
default_cursor_style: terminal.Cursor.Style,
|
||||||
|
default_cursor_blink: bool,
|
||||||
|
|
||||||
pub fn deinit(self: *StreamHandler) void {
|
pub fn deinit(self: *StreamHandler) void {
|
||||||
self.apc.deinit();
|
self.apc.deinit();
|
||||||
}
|
}
|
||||||
@ -1121,6 +1155,21 @@ const StreamHandler = struct {
|
|||||||
self.writer_messaged = true;
|
self.writer_messaged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn changeDefaultCursor(
|
||||||
|
self: *StreamHandler,
|
||||||
|
style: terminal.Cursor.Style,
|
||||||
|
blink: bool,
|
||||||
|
) void {
|
||||||
|
self.default_cursor_style = style;
|
||||||
|
self.default_cursor_blink = blink;
|
||||||
|
|
||||||
|
// If our cursor is the default, then we update it immediately.
|
||||||
|
if (self.default_cursor) self.setCursorStyle(.default) catch |err| {
|
||||||
|
log.warn("failed to set default cursor style: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apcStart(self: *StreamHandler) !void {
|
pub fn apcStart(self: *StreamHandler) !void {
|
||||||
self.apc.start();
|
self.apc.start();
|
||||||
}
|
}
|
||||||
@ -1339,9 +1388,6 @@ const StreamHandler = struct {
|
|||||||
// Origin resets cursor pos
|
// Origin resets cursor pos
|
||||||
.origin => self.terminal.setCursorPos(1, 1),
|
.origin => self.terminal.setCursorPos(1, 1),
|
||||||
|
|
||||||
// We need to update our renderer state for this mode
|
|
||||||
.cursor_visible => self.ev.renderer_state.cursor.visible = enabled,
|
|
||||||
|
|
||||||
.alt_screen_save_cursor_clear_enter => {
|
.alt_screen_save_cursor_clear_enter => {
|
||||||
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
const opts: terminal.Terminal.AlternateScreenOptions = .{
|
||||||
.cursor_save = true,
|
.cursor_save = true,
|
||||||
@ -1462,9 +1508,50 @@ const StreamHandler = struct {
|
|||||||
|
|
||||||
pub fn setCursorStyle(
|
pub fn setCursorStyle(
|
||||||
self: *StreamHandler,
|
self: *StreamHandler,
|
||||||
style: terminal.CursorStyle,
|
style: terminal.CursorStyleReq,
|
||||||
) !void {
|
) !void {
|
||||||
self.ev.renderer_state.cursor.style = style;
|
// Assume we're setting to a non-default.
|
||||||
|
self.default_cursor = false;
|
||||||
|
|
||||||
|
switch (style) {
|
||||||
|
.default => {
|
||||||
|
self.default_cursor = true;
|
||||||
|
self.terminal.screen.cursor.style = self.default_cursor_style;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, self.default_cursor_blink);
|
||||||
|
},
|
||||||
|
|
||||||
|
.blinking_block => {
|
||||||
|
self.terminal.screen.cursor.style = .block;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
.steady_block => {
|
||||||
|
self.terminal.screen.cursor.style = .block;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
.blinking_underline => {
|
||||||
|
self.terminal.screen.cursor.style = .underline;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
.steady_underline => {
|
||||||
|
self.terminal.screen.cursor.style = .underline;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
.blinking_bar => {
|
||||||
|
self.terminal.screen.cursor.style = .bar;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
.steady_bar => {
|
||||||
|
self.terminal.screen.cursor.style = .bar;
|
||||||
|
self.terminal.modes.set(.cursor_blinking, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => log.warn("unimplemented cursor style: {}", .{style}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decaln(self: *StreamHandler) !void {
|
pub fn decaln(self: *StreamHandler) !void {
|
||||||
|
Reference in New Issue
Block a user