mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge f0db0c850b58d375515620a3cd5b1d695e41ac10 into fade8825749b8f6ef43bd57a82bd5477ae393913
This commit is contained in:
@ -346,6 +346,41 @@ foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
|
|||||||
/// This value does not apply to Emoji or images.
|
/// This value does not apply to Emoji or images.
|
||||||
@"minimum-contrast": f64 = 1,
|
@"minimum-contrast": f64 = 1,
|
||||||
|
|
||||||
|
/// The amount of time it takes for blinking text and cursors to toggle between
|
||||||
|
/// being invisible and visible.
|
||||||
|
/// Any cell on the screen could be set to blink by setting SGR attribute 5,
|
||||||
|
/// while cursor blinking is controlled by the `cursor-style-blink` setting.
|
||||||
|
///
|
||||||
|
/// The interval is specified as a series of numbers followed by time units.
|
||||||
|
/// Whitespace is allowed between numbers and units. Each number and unit will
|
||||||
|
/// be added together to form the total interval.
|
||||||
|
///
|
||||||
|
/// Blinking is disabled when the interval is set to zero.
|
||||||
|
///
|
||||||
|
/// The allowed time units are as follows:
|
||||||
|
///
|
||||||
|
/// * `y` - 365 SI days, or 8760 hours, or 31536000 seconds. No adjustments
|
||||||
|
/// are made for leap years or leap seconds.
|
||||||
|
/// * `d` - one SI day, or 86400 seconds.
|
||||||
|
/// * `h` - one hour, or 3600 seconds.
|
||||||
|
/// * `m` - one minute, or 60 seconds.
|
||||||
|
/// * `s` - one second.
|
||||||
|
/// * `ms` - one millisecond, or 0.001 second.
|
||||||
|
/// * `us` or `µs` - one microsecond, or 0.000001 second.
|
||||||
|
/// * `ns` - one nanosecond, or 0.000000001 second.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// * `1h30m`
|
||||||
|
/// * `45s`
|
||||||
|
///
|
||||||
|
/// Units can be repeated and will be added together. This means that
|
||||||
|
/// `1h1h` is equivalent to `2h`. This is confusing and should be avoided.
|
||||||
|
/// A future update may disallow this.
|
||||||
|
///
|
||||||
|
/// The maximum value is `584y 49w 23h 34m 33s 709ms 551µs 615ns`. Any
|
||||||
|
/// value larger than this will be clamped to the maximum value.
|
||||||
|
@"blink-interval": Duration = .{ .duration = 600 * std.time.ns_per_ms },
|
||||||
|
|
||||||
/// Color palette for the 256 color form that many terminal applications use.
|
/// Color palette for the 256 color form that many terminal applications use.
|
||||||
/// The syntax of this configuration is `N=HEXCODE` where `N` is 0 to 255 (for
|
/// The syntax of this configuration is `N=HEXCODE` where `N` is 0 to 255 (for
|
||||||
/// the 256 colors in the terminal color table) and `HEXCODE` is a typical RGB
|
/// the 256 colors in the terminal color table) and `HEXCODE` is a typical RGB
|
||||||
|
@ -97,6 +97,9 @@ cursor_color: ?terminal.color.RGB,
|
|||||||
/// foreground color as the cursor color.
|
/// foreground color as the cursor color.
|
||||||
cursor_invert: bool,
|
cursor_invert: bool,
|
||||||
|
|
||||||
|
/// Whether blinking cells are currently visible. Synchronized with cursor blinking.
|
||||||
|
blink_visible: bool = true,
|
||||||
|
|
||||||
/// The current frame background color. This is only updated during
|
/// The current frame background color. This is only updated during
|
||||||
/// the updateFrame method.
|
/// the updateFrame method.
|
||||||
current_background_color: terminal.color.RGB,
|
current_background_color: terminal.color.RGB,
|
||||||
@ -618,6 +621,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.background_color = options.config.background,
|
.background_color = options.config.background,
|
||||||
.cursor_color = options.config.cursor_color,
|
.cursor_color = options.config.cursor_color,
|
||||||
.cursor_invert = options.config.cursor_invert,
|
.cursor_invert = options.config.cursor_invert,
|
||||||
|
.blink_visible = true,
|
||||||
.current_background_color = options.config.background,
|
.current_background_color = options.config.background,
|
||||||
|
|
||||||
// Render state
|
// Render state
|
||||||
@ -631,6 +635,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.min_contrast = options.config.min_contrast,
|
.min_contrast = options.config.min_contrast,
|
||||||
.cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) },
|
.cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) },
|
||||||
.cursor_color = undefined,
|
.cursor_color = undefined,
|
||||||
|
.blink_visible = true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
@ -867,6 +872,7 @@ pub fn updateFrame(
|
|||||||
self: *Metal,
|
self: *Metal,
|
||||||
surface: *apprt.Surface,
|
surface: *apprt.Surface,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
|
blink_visible: bool,
|
||||||
cursor_blink_visible: bool,
|
cursor_blink_visible: bool,
|
||||||
) !void {
|
) !void {
|
||||||
_ = surface;
|
_ = surface;
|
||||||
@ -1030,6 +1036,9 @@ pub fn updateFrame(
|
|||||||
.full_rebuild = full_rebuild,
|
.full_rebuild = full_rebuild,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.blink_visible = blink_visible;
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
critical.screen.deinit();
|
critical.screen.deinit();
|
||||||
if (critical.preedit) |p| p.deinit(self.alloc);
|
if (critical.preedit) |p| p.deinit(self.alloc);
|
||||||
@ -1104,6 +1113,8 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
|||||||
errdefer self.gpu_state.releaseFrame();
|
errdefer self.gpu_state.releaseFrame();
|
||||||
// log.debug("drawing frame index={}", .{self.gpu_state.frame_index});
|
// log.debug("drawing frame index={}", .{self.gpu_state.frame_index});
|
||||||
|
|
||||||
|
self.uniforms.blink_visible = self.blink_visible;
|
||||||
|
|
||||||
// Setup our frame data
|
// Setup our frame data
|
||||||
try frame.uniforms.sync(self.gpu_state.device, &.{self.uniforms});
|
try frame.uniforms.sync(self.gpu_state.device, &.{self.uniforms});
|
||||||
try frame.cells_bg.sync(self.gpu_state.device, self.cells.bg_cells);
|
try frame.cells_bg.sync(self.gpu_state.device, self.cells.bg_cells);
|
||||||
@ -1606,6 +1617,11 @@ fn drawCellFgs(
|
|||||||
@as(c_ulong, 1),
|
@as(c_ulong, 1),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
encoder.msgSend(
|
||||||
|
void,
|
||||||
|
objc.sel("setFragmentBuffer:offset:atIndex:"),
|
||||||
|
.{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 1) },
|
||||||
|
);
|
||||||
|
|
||||||
encoder.msgSend(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
@ -2034,6 +2050,7 @@ pub fn setScreenSize(
|
|||||||
.min_contrast = old.min_contrast,
|
.min_contrast = old.min_contrast,
|
||||||
.cursor_pos = old.cursor_pos,
|
.cursor_pos = old.cursor_pos,
|
||||||
.cursor_color = old.cursor_color,
|
.cursor_color = old.cursor_color,
|
||||||
|
.blink_visible = old.blink_visible,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reset our cell contents if our grid size has changed.
|
// Reset our cell contents if our grid size has changed.
|
||||||
@ -2542,15 +2559,19 @@ fn updateCell(
|
|||||||
break :glyph;
|
break :glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mode: mtl_shaders.CellText.Mode = switch (try fgMode(
|
var mode: mtl_shaders.CellText.Mode = .{ .fg = true };
|
||||||
|
|
||||||
|
switch (try fgMode(
|
||||||
render.presentation,
|
render.presentation,
|
||||||
cell_pin,
|
cell_pin,
|
||||||
)) {
|
)) {
|
||||||
.normal => .fg,
|
.normal => {},
|
||||||
.color => .fg_color,
|
.color => mode.fg_color = true,
|
||||||
.constrained => .fg_constrained,
|
.constrained => mode.fg_constrained = true,
|
||||||
.powerline => .fg_powerline,
|
.powerline => mode.fg_powerline = true,
|
||||||
};
|
}
|
||||||
|
|
||||||
|
mode.fg_blink = style.flags.blink;
|
||||||
|
|
||||||
try self.cells.add(self.alloc, .text, .{
|
try self.cells.add(self.alloc, .text, .{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
@ -2589,7 +2610,7 @@ fn updateCell(
|
|||||||
const color = style.underlineColor(palette) orelse colors.fg;
|
const color = style.underlineColor(palette) orelse colors.fg;
|
||||||
|
|
||||||
try self.cells.add(self.alloc, .underline, .{
|
try self.cells.add(self.alloc, .underline, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
||||||
.constraint_width = cell.gridWidth(),
|
.constraint_width = cell.gridWidth(),
|
||||||
.color = .{ color.r, color.g, color.b, alpha },
|
.color = .{ color.r, color.g, color.b, alpha },
|
||||||
@ -2614,7 +2635,7 @@ fn updateCell(
|
|||||||
);
|
);
|
||||||
|
|
||||||
try self.cells.add(self.alloc, .strikethrough, .{
|
try self.cells.add(self.alloc, .strikethrough, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
||||||
.constraint_width = cell.gridWidth(),
|
.constraint_width = cell.gridWidth(),
|
||||||
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
|
||||||
@ -2704,7 +2725,7 @@ fn addCursor(
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.cells.setCursor(.{
|
self.cells.setCursor(.{
|
||||||
.mode = .cursor,
|
.mode = .{ .fg = false, .cursor = true },
|
||||||
.grid_pos = .{ x, screen.cursor.y },
|
.grid_pos = .{ x, screen.cursor.y },
|
||||||
.color = .{ cursor_color.r, cursor_color.g, cursor_color.b, alpha },
|
.color = .{ cursor_color.r, cursor_color.g, cursor_color.b, alpha },
|
||||||
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
|
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
|
||||||
@ -2753,7 +2774,7 @@ fn addPreeditCell(
|
|||||||
|
|
||||||
// Add our text
|
// Add our text
|
||||||
try self.cells.add(self.alloc, .text, .{
|
try self.cells.add(self.alloc, .text, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
.grid_pos = .{ @intCast(coord.x), @intCast(coord.y) },
|
||||||
.color = .{ fg.r, fg.g, fg.b, 255 },
|
.color = .{ fg.r, fg.g, fg.b, 255 },
|
||||||
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
|
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
|
||||||
|
@ -108,6 +108,9 @@ cursor_color: ?terminal.color.RGB,
|
|||||||
/// foreground color as the cursor color.
|
/// foreground color as the cursor color.
|
||||||
cursor_invert: bool,
|
cursor_invert: bool,
|
||||||
|
|
||||||
|
/// Whether blinking cells are currently visible. Synchronized with cursor blinking.
|
||||||
|
blink_visible: bool = true,
|
||||||
|
|
||||||
/// Padding options
|
/// Padding options
|
||||||
padding: renderer.Options.Padding,
|
padding: renderer.Options.Padding,
|
||||||
|
|
||||||
@ -701,6 +704,7 @@ pub fn updateFrame(
|
|||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
surface: *apprt.Surface,
|
surface: *apprt.Surface,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
|
blink_visible: bool,
|
||||||
cursor_blink_visible: bool,
|
cursor_blink_visible: bool,
|
||||||
) !void {
|
) !void {
|
||||||
_ = surface;
|
_ = surface;
|
||||||
@ -857,6 +861,9 @@ pub fn updateFrame(
|
|||||||
.color_palette = state.terminal.color_palette.colors,
|
.color_palette = state.terminal.color_palette.colors,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.blink_visible = blink_visible;
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
critical.screen.deinit();
|
critical.screen.deinit();
|
||||||
if (critical.preedit) |p| p.deinit(self.alloc);
|
if (critical.preedit) |p| p.deinit(self.alloc);
|
||||||
@ -1282,7 +1289,7 @@ pub fn rebuildCells(
|
|||||||
const screen_cell = row.cells(.all)[screen.cursor.x];
|
const screen_cell = row.cells(.all)[screen.cursor.x];
|
||||||
const x = screen.cursor.x - @intFromBool(screen_cell.wide == .spacer_tail);
|
const x = screen.cursor.x - @intFromBool(screen_cell.wide == .spacer_tail);
|
||||||
for (self.cells.items[start_i..]) |cell| {
|
for (self.cells.items[start_i..]) |cell| {
|
||||||
if (cell.grid_col == x and cell.mode.isFg()) {
|
if (cell.grid_col == x and cell.mode.fg) {
|
||||||
cursor_cell = cell;
|
cursor_cell = cell;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1419,7 +1426,7 @@ pub fn rebuildCells(
|
|||||||
|
|
||||||
_ = try self.addCursor(screen, cursor_style, cursor_color);
|
_ = try self.addCursor(screen, cursor_style, cursor_color);
|
||||||
if (cursor_cell) |*cell| {
|
if (cursor_cell) |*cell| {
|
||||||
if (cell.mode.isFg() and cell.mode != .fg_color) {
|
if (cell.mode.fg and !cell.mode.fg_color) {
|
||||||
const cell_color = if (self.cursor_invert) blk: {
|
const cell_color = if (self.cursor_invert) blk: {
|
||||||
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
||||||
break :blk sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color;
|
break :blk sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color;
|
||||||
@ -1439,8 +1446,8 @@ pub fn rebuildCells(
|
|||||||
|
|
||||||
// Some debug mode safety checks
|
// Some debug mode safety checks
|
||||||
if (std.debug.runtime_safety) {
|
if (std.debug.runtime_safety) {
|
||||||
for (self.cells_bg.items) |cell| assert(cell.mode == .bg);
|
for (self.cells_bg.items) |cell| assert(!cell.mode.fg);
|
||||||
for (self.cells.items) |cell| assert(cell.mode != .bg);
|
for (self.cells.items) |cell| assert(cell.mode.fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,7 +1479,7 @@ fn addPreeditCell(
|
|||||||
|
|
||||||
// Add our opaque background cell
|
// Add our opaque background cell
|
||||||
try self.cells_bg.append(self.alloc, .{
|
try self.cells_bg.append(self.alloc, .{
|
||||||
.mode = .bg,
|
.mode = .{ .fg = false },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = if (cp.wide) 2 else 1,
|
.grid_width = if (cp.wide) 2 else 1,
|
||||||
@ -1494,7 +1501,7 @@ fn addPreeditCell(
|
|||||||
|
|
||||||
// Add our text
|
// Add our text
|
||||||
try self.cells.append(self.alloc, .{
|
try self.cells.append(self.alloc, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = if (cp.wide) 2 else 1,
|
.grid_width = if (cp.wide) 2 else 1,
|
||||||
@ -1589,7 +1596,7 @@ fn addCursor(
|
|||||||
};
|
};
|
||||||
|
|
||||||
try self.cells.append(self.alloc, .{
|
try self.cells.append(self.alloc, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(screen.cursor.y),
|
.grid_row = @intCast(screen.cursor.y),
|
||||||
.grid_width = if (wide) 2 else 1,
|
.grid_width = if (wide) 2 else 1,
|
||||||
@ -1733,7 +1740,7 @@ fn updateCell(
|
|||||||
};
|
};
|
||||||
|
|
||||||
try self.cells_bg.append(self.alloc, .{
|
try self.cells_bg.append(self.alloc, .{
|
||||||
.mode = .bg,
|
.mode = .{ .fg = false },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = cell.gridWidth(),
|
.grid_width = cell.gridWidth(),
|
||||||
@ -1774,16 +1781,20 @@ fn updateCell(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var mode: CellProgram.CellMode = .{ .fg = true };
|
||||||
|
|
||||||
// If we're rendering a color font, we use the color atlas
|
// If we're rendering a color font, we use the color atlas
|
||||||
const mode: CellProgram.CellMode = switch (try fgMode(
|
switch (try fgMode(
|
||||||
render.presentation,
|
render.presentation,
|
||||||
cell_pin,
|
cell_pin,
|
||||||
)) {
|
)) {
|
||||||
.normal => .fg,
|
.normal => {},
|
||||||
.color => .fg_color,
|
.color => mode.fg_color = true,
|
||||||
.constrained => .fg_constrained,
|
.constrained => mode.fg_constrained = true,
|
||||||
.powerline => .fg_powerline,
|
.powerline => mode.fg_powerline = true,
|
||||||
};
|
}
|
||||||
|
|
||||||
|
mode.fg_blink = style.flags.blink;
|
||||||
|
|
||||||
try self.cells.append(self.alloc, .{
|
try self.cells.append(self.alloc, .{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
@ -1830,7 +1841,7 @@ fn updateCell(
|
|||||||
const color = style.underlineColor(palette) orelse colors.fg;
|
const color = style.underlineColor(palette) orelse colors.fg;
|
||||||
|
|
||||||
try self.cells.append(self.alloc, .{
|
try self.cells.append(self.alloc, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = cell.gridWidth(),
|
.grid_width = cell.gridWidth(),
|
||||||
@ -1863,7 +1874,7 @@ fn updateCell(
|
|||||||
);
|
);
|
||||||
|
|
||||||
try self.cells.append(self.alloc, .{
|
try self.cells.append(self.alloc, .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = cell.gridWidth(),
|
.grid_width = cell.gridWidth(),
|
||||||
@ -2187,6 +2198,10 @@ fn drawCellProgram(
|
|||||||
"padding_vertical_bottom",
|
"padding_vertical_bottom",
|
||||||
self.padding_extend_bottom,
|
self.padding_extend_bottom,
|
||||||
);
|
);
|
||||||
|
try program.program.setUniform(
|
||||||
|
"blink_visible",
|
||||||
|
self.blink_visible,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw background images first
|
// Draw background images first
|
||||||
|
@ -16,7 +16,6 @@ const Allocator = std.mem.Allocator;
|
|||||||
const log = std.log.scoped(.renderer_thread);
|
const log = std.log.scoped(.renderer_thread);
|
||||||
|
|
||||||
const DRAW_INTERVAL = 8; // 120 FPS
|
const DRAW_INTERVAL = 8; // 120 FPS
|
||||||
const CURSOR_BLINK_INTERVAL = 600;
|
|
||||||
|
|
||||||
/// The type used for sending messages to the IO thread. For now this is
|
/// The type used for sending messages to the IO thread. For now this is
|
||||||
/// hardcoded with a capacity. We can make this a comptime parameter in
|
/// hardcoded with a capacity. We can make this a comptime parameter in
|
||||||
@ -56,7 +55,16 @@ draw_active: bool = false,
|
|||||||
draw_now: xev.Async,
|
draw_now: xev.Async,
|
||||||
draw_now_c: xev.Completion = .{},
|
draw_now_c: xev.Completion = .{},
|
||||||
|
|
||||||
/// The timer used for cursor blinking
|
/// The timer used for text blinking. This timer will always run, uninterrupted by
|
||||||
|
/// user text input.
|
||||||
|
blink_h: xev.Timer,
|
||||||
|
blink_c: xev.Completion = .{},
|
||||||
|
blink_c_cancel: xev.Completion = .{},
|
||||||
|
|
||||||
|
/// The timer used to reset cursor blinking. When the cursor is set to always visible
|
||||||
|
/// (for example, while typing), this timer introduces a delay that synchronizes with
|
||||||
|
/// the main blink timer, to unset the always visible flag and allow the cursor to blink
|
||||||
|
/// in sync with the rest of the screen again.
|
||||||
cursor_h: xev.Timer,
|
cursor_h: xev.Timer,
|
||||||
cursor_c: xev.Completion = .{},
|
cursor_c: xev.Completion = .{},
|
||||||
cursor_c_cancel: xev.Completion = .{},
|
cursor_c_cancel: xev.Completion = .{},
|
||||||
@ -81,10 +89,14 @@ app_mailbox: App.Mailbox,
|
|||||||
config: DerivedConfig,
|
config: DerivedConfig,
|
||||||
|
|
||||||
flags: packed struct {
|
flags: packed struct {
|
||||||
/// This is true when a blinking cursor should be visible and false
|
/// This is true when blinking text should be visible and false
|
||||||
/// when it should not be visible. This is toggled on a timer by the
|
/// when it should not be visible. This is toggled on a timer by the
|
||||||
/// thread automatically.
|
/// thread automatically.
|
||||||
cursor_blink_visible: bool = false,
|
blink_visible: bool = true,
|
||||||
|
|
||||||
|
/// Whether the cursor should be forced into staying visible regardless
|
||||||
|
/// of blinking. Used when the user is typing.
|
||||||
|
cursor_always_visible: bool = false,
|
||||||
|
|
||||||
/// This is true when the inspector is active.
|
/// This is true when the inspector is active.
|
||||||
has_inspector: bool = false,
|
has_inspector: bool = false,
|
||||||
@ -96,10 +108,18 @@ flags: packed struct {
|
|||||||
|
|
||||||
pub const DerivedConfig = struct {
|
pub const DerivedConfig = struct {
|
||||||
custom_shader_animation: configpkg.CustomShaderAnimation,
|
custom_shader_animation: configpkg.CustomShaderAnimation,
|
||||||
|
blink_interval: u64,
|
||||||
|
|
||||||
pub fn init(config: *const configpkg.Config) DerivedConfig {
|
pub fn init(config: *const configpkg.Config) DerivedConfig {
|
||||||
|
const blink_interval = std.math.divTrunc(
|
||||||
|
u64,
|
||||||
|
config.@"blink-interval".duration,
|
||||||
|
std.time.ns_per_ms,
|
||||||
|
) catch std.math.maxInt(u64);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.custom_shader_animation = config.@"custom-shader-animation",
|
.custom_shader_animation = config.@"custom-shader-animation",
|
||||||
|
.blink_interval = blink_interval,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -139,6 +159,10 @@ pub fn init(
|
|||||||
var draw_now = try xev.Async.init();
|
var draw_now = try xev.Async.init();
|
||||||
errdefer draw_now.deinit();
|
errdefer draw_now.deinit();
|
||||||
|
|
||||||
|
// Setup a timer for blinking the text
|
||||||
|
var blink_timer = try xev.Timer.init();
|
||||||
|
errdefer blink_timer.deinit();
|
||||||
|
|
||||||
// Setup a timer for blinking the cursor
|
// Setup a timer for blinking the cursor
|
||||||
var cursor_timer = try xev.Timer.init();
|
var cursor_timer = try xev.Timer.init();
|
||||||
errdefer cursor_timer.deinit();
|
errdefer cursor_timer.deinit();
|
||||||
@ -156,6 +180,7 @@ pub fn init(
|
|||||||
.render_h = render_h,
|
.render_h = render_h,
|
||||||
.draw_h = draw_h,
|
.draw_h = draw_h,
|
||||||
.draw_now = draw_now,
|
.draw_now = draw_now,
|
||||||
|
.blink_h = blink_timer,
|
||||||
.cursor_h = cursor_timer,
|
.cursor_h = cursor_timer,
|
||||||
.surface = surface,
|
.surface = surface,
|
||||||
.renderer = renderer_impl,
|
.renderer = renderer_impl,
|
||||||
@ -173,6 +198,7 @@ pub fn deinit(self: *Thread) void {
|
|||||||
self.render_h.deinit();
|
self.render_h.deinit();
|
||||||
self.draw_h.deinit();
|
self.draw_h.deinit();
|
||||||
self.draw_now.deinit();
|
self.draw_now.deinit();
|
||||||
|
self.blink_h.deinit();
|
||||||
self.cursor_h.deinit();
|
self.cursor_h.deinit();
|
||||||
self.loop.deinit();
|
self.loop.deinit();
|
||||||
|
|
||||||
@ -218,15 +244,17 @@ fn threadMain_(self: *Thread) !void {
|
|||||||
// Send an initial wakeup message so that we render right away.
|
// Send an initial wakeup message so that we render right away.
|
||||||
try self.wakeup.notify();
|
try self.wakeup.notify();
|
||||||
|
|
||||||
// Start blinking the cursor.
|
// Start blinking the cursor and the text on screen.
|
||||||
self.cursor_h.run(
|
if (self.config.blink_interval > 0) {
|
||||||
|
self.blink_h.run(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
&self.cursor_c,
|
&self.blink_c,
|
||||||
CURSOR_BLINK_INTERVAL,
|
self.config.blink_interval,
|
||||||
Thread,
|
Thread,
|
||||||
self,
|
self,
|
||||||
cursorTimerCallback,
|
blinkTimerCallback,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Start the draw timer
|
// Start the draw timer
|
||||||
self.startDrawTimer();
|
self.startDrawTimer();
|
||||||
@ -303,50 +331,51 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
self.stopDrawTimer();
|
self.stopDrawTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not focused, then we stop the cursor blink
|
// If we're not focused, then we stop the blink
|
||||||
if (self.cursor_c.state() == .active and
|
if (self.blink_c.state() == .active and
|
||||||
self.cursor_c_cancel.state() == .dead)
|
self.blink_c_cancel.state() == .dead)
|
||||||
{
|
{
|
||||||
self.cursor_h.cancel(
|
self.blink_h.cancel(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
&self.cursor_c,
|
&self.blink_c,
|
||||||
&self.cursor_c_cancel,
|
&self.blink_c_cancel,
|
||||||
void,
|
void,
|
||||||
null,
|
null,
|
||||||
cursorCancelCallback,
|
blinkCancelCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Start the draw timer
|
// Start the draw timer
|
||||||
self.startDrawTimer();
|
self.startDrawTimer();
|
||||||
|
|
||||||
// If we're focused, we immediately show the cursor again
|
// If we're focused, we immediately make blinking cells visible
|
||||||
// and then restart the timer.
|
// again and then restart the timer.
|
||||||
if (self.cursor_c.state() != .active) {
|
if (self.blink_c.state() != .active and self.config.blink_interval > 0) {
|
||||||
self.flags.cursor_blink_visible = true;
|
self.flags.blink_visible = true;
|
||||||
self.cursor_h.run(
|
self.blink_h.run(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
&self.cursor_c,
|
&self.blink_c,
|
||||||
CURSOR_BLINK_INTERVAL,
|
self.config.blink_interval,
|
||||||
Thread,
|
Thread,
|
||||||
self,
|
self,
|
||||||
cursorTimerCallback,
|
blinkTimerCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.reset_cursor_blink => {
|
.reset_cursor_blink => {
|
||||||
self.flags.cursor_blink_visible = true;
|
self.flags.cursor_always_visible = true;
|
||||||
if (self.cursor_c.state() == .active) {
|
|
||||||
|
if (self.config.blink_interval > 0) {
|
||||||
self.cursor_h.reset(
|
self.cursor_h.reset(
|
||||||
&self.loop,
|
&self.loop,
|
||||||
&self.cursor_c,
|
&self.cursor_c,
|
||||||
&self.cursor_c_cancel,
|
&self.cursor_c_cancel,
|
||||||
CURSOR_BLINK_INTERVAL,
|
self.config.blink_interval,
|
||||||
Thread,
|
Thread,
|
||||||
self,
|
self,
|
||||||
cursorTimerCallback,
|
resetCursorBlinkCallback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -529,7 +558,8 @@ fn renderCallback(
|
|||||||
t.renderer.updateFrame(
|
t.renderer.updateFrame(
|
||||||
t.surface,
|
t.surface,
|
||||||
t.state,
|
t.state,
|
||||||
t.flags.cursor_blink_visible,
|
t.flags.blink_visible,
|
||||||
|
t.flags.cursor_always_visible or t.flags.blink_visible,
|
||||||
) catch |err|
|
) catch |err|
|
||||||
log.warn("error rendering err={}", .{err});
|
log.warn("error rendering err={}", .{err});
|
||||||
|
|
||||||
@ -539,7 +569,7 @@ fn renderCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursorTimerCallback(
|
fn blinkTimerCallback(
|
||||||
self_: ?*Thread,
|
self_: ?*Thread,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
_: *xev.Completion,
|
_: *xev.Completion,
|
||||||
@ -561,14 +591,14 @@ fn cursorTimerCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
t.flags.cursor_blink_visible = !t.flags.cursor_blink_visible;
|
t.flags.blink_visible = !t.flags.blink_visible;
|
||||||
t.wakeup.notify() catch {};
|
t.wakeup.notify() catch {};
|
||||||
|
t.blink_h.run(&t.loop, &t.blink_c, t.config.blink_interval, Thread, t, blinkTimerCallback);
|
||||||
|
|
||||||
t.cursor_h.run(&t.loop, &t.cursor_c, CURSOR_BLINK_INTERVAL, Thread, t, cursorTimerCallback);
|
|
||||||
return .disarm;
|
return .disarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursorCancelCallback(
|
fn blinkCancelCallback(
|
||||||
_: ?*void,
|
_: ?*void,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
_: *xev.Completion,
|
_: *xev.Completion,
|
||||||
@ -586,7 +616,7 @@ fn cursorCancelCallback(
|
|||||||
error.Canceled => {}, // success
|
error.Canceled => {}, // success
|
||||||
error.NotFound => {}, // completed before it could cancel
|
error.NotFound => {}, // completed before it could cancel
|
||||||
else => {
|
else => {
|
||||||
log.warn("error in cursor cancel callback err={}", .{err});
|
log.warn("error in blink cancel callback err={}", .{err});
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -594,6 +624,24 @@ fn cursorCancelCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resetCursorBlinkCallback(
|
||||||
|
self_: ?*Thread,
|
||||||
|
_: *xev.Loop,
|
||||||
|
_: *xev.Completion,
|
||||||
|
r: xev.Timer.RunError!void,
|
||||||
|
) xev.CallbackAction {
|
||||||
|
_ = r catch unreachable;
|
||||||
|
|
||||||
|
const t: *Thread = self_ orelse {
|
||||||
|
// This shouldn't happen so we log it.
|
||||||
|
log.warn("render callback fired without data set", .{});
|
||||||
|
return .disarm;
|
||||||
|
};
|
||||||
|
|
||||||
|
t.flags.cursor_always_visible = false;
|
||||||
|
return .disarm;
|
||||||
|
}
|
||||||
|
|
||||||
// fn prepFrameCallback(h: *libuv.Prepare) void {
|
// fn prepFrameCallback(h: *libuv.Prepare) void {
|
||||||
// _ = h;
|
// _ = h;
|
||||||
//
|
//
|
||||||
|
@ -241,7 +241,7 @@ test Contents {
|
|||||||
// Add some contents.
|
// Add some contents.
|
||||||
const bg_cell: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
const bg_cell: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
||||||
const fg_cell: mtl_shaders.CellText = .{
|
const fg_cell: mtl_shaders.CellText = .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ 4, 1 },
|
.grid_pos = .{ 4, 1 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
@ -262,7 +262,10 @@ test Contents {
|
|||||||
|
|
||||||
// Add a cursor.
|
// Add a cursor.
|
||||||
const cursor_cell: mtl_shaders.CellText = .{
|
const cursor_cell: mtl_shaders.CellText = .{
|
||||||
.mode = .cursor,
|
.mode = .{
|
||||||
|
.fg = false,
|
||||||
|
.cursor = true,
|
||||||
|
},
|
||||||
.grid_pos = .{ 2, 3 },
|
.grid_pos = .{ 2, 3 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
@ -289,7 +292,7 @@ test "Contents clear retains other content" {
|
|||||||
// bg and fg cells in row 1
|
// bg and fg cells in row 1
|
||||||
const bg_cell_1: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
const bg_cell_1: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
||||||
const fg_cell_1: mtl_shaders.CellText = .{
|
const fg_cell_1: mtl_shaders.CellText = .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ 4, 1 },
|
.grid_pos = .{ 4, 1 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
@ -298,7 +301,7 @@ test "Contents clear retains other content" {
|
|||||||
// bg and fg cells in row 2
|
// bg and fg cells in row 2
|
||||||
const bg_cell_2: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
const bg_cell_2: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
||||||
const fg_cell_2: mtl_shaders.CellText = .{
|
const fg_cell_2: mtl_shaders.CellText = .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ 4, 2 },
|
.grid_pos = .{ 4, 2 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
@ -329,7 +332,7 @@ test "Contents clear last added content" {
|
|||||||
// bg and fg cells in row 1
|
// bg and fg cells in row 1
|
||||||
const bg_cell_1: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
const bg_cell_1: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
||||||
const fg_cell_1: mtl_shaders.CellText = .{
|
const fg_cell_1: mtl_shaders.CellText = .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ 4, 1 },
|
.grid_pos = .{ 4, 1 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
@ -338,7 +341,7 @@ test "Contents clear last added content" {
|
|||||||
// bg and fg cells in row 2
|
// bg and fg cells in row 2
|
||||||
const bg_cell_2: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
const bg_cell_2: mtl_shaders.CellBg = .{ 0, 0, 0, 1 };
|
||||||
const fg_cell_2: mtl_shaders.CellText = .{
|
const fg_cell_2: mtl_shaders.CellText = .{
|
||||||
.mode = .fg,
|
.mode = .{ .fg = true },
|
||||||
.grid_pos = .{ 4, 2 },
|
.grid_pos = .{ 4, 2 },
|
||||||
.color = .{ 0, 0, 0, 1 },
|
.color = .{ 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
|
@ -137,6 +137,9 @@ pub const Uniforms = extern struct {
|
|||||||
cursor_pos: [2]u16 align(4),
|
cursor_pos: [2]u16 align(4),
|
||||||
cursor_color: [4]u8 align(4),
|
cursor_color: [4]u8 align(4),
|
||||||
|
|
||||||
|
/// Whether blinking cells and cursors are visible on this frame.
|
||||||
|
blink_visible: bool align(1),
|
||||||
|
|
||||||
const PaddingExtend = packed struct(u8) {
|
const PaddingExtend = packed struct(u8) {
|
||||||
left: bool = false,
|
left: bool = false,
|
||||||
right: bool = false,
|
right: bool = false,
|
||||||
@ -324,12 +327,15 @@ pub const CellText = extern struct {
|
|||||||
mode: Mode align(1),
|
mode: Mode align(1),
|
||||||
constraint_width: u8 align(1) = 0,
|
constraint_width: u8 align(1) = 0,
|
||||||
|
|
||||||
pub const Mode = enum(u8) {
|
pub const Mode = packed struct(u8) {
|
||||||
fg = 1,
|
fg: bool,
|
||||||
fg_constrained = 2,
|
fg_constrained: bool = false,
|
||||||
fg_color = 3,
|
fg_color: bool = false,
|
||||||
cursor = 4,
|
cursor: bool = false,
|
||||||
fg_powerline = 5,
|
fg_powerline: bool = false,
|
||||||
|
fg_blink: bool = false,
|
||||||
|
|
||||||
|
_padding: u2 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@ -662,6 +668,7 @@ fn autoAttribute(T: type, attrs: objc.Object) void {
|
|||||||
[2]u32 => mtl.MTLVertexFormat.uint2,
|
[2]u32 => mtl.MTLVertexFormat.uint2,
|
||||||
[4]u32 => mtl.MTLVertexFormat.uint4,
|
[4]u32 => mtl.MTLVertexFormat.uint4,
|
||||||
u8 => mtl.MTLVertexFormat.uchar,
|
u8 => mtl.MTLVertexFormat.uchar,
|
||||||
|
CellText.Mode => mtl.MTLVertexFormat.uchar,
|
||||||
else => comptime unreachable,
|
else => comptime unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,31 +48,14 @@ pub const Cell = extern struct {
|
|||||||
grid_width: u8,
|
grid_width: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CellMode = enum(u8) {
|
pub const CellMode = packed struct(u8) {
|
||||||
bg = 1,
|
fg: bool,
|
||||||
fg = 2,
|
fg_constrained: bool = false,
|
||||||
fg_constrained = 3,
|
fg_color: bool = false,
|
||||||
fg_color = 7,
|
fg_powerline: bool = false,
|
||||||
fg_powerline = 15,
|
fg_blink: bool = false,
|
||||||
|
|
||||||
// Non-exhaustive because masks change it
|
_padding: u3 = 0,
|
||||||
_,
|
|
||||||
|
|
||||||
/// Apply a mask to the mode.
|
|
||||||
pub fn mask(self: CellMode, m: CellMode) CellMode {
|
|
||||||
return @enumFromInt(@intFromEnum(self) | @intFromEnum(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn isFg(self: CellMode) bool {
|
|
||||||
// Since we use bit tricks below, we want to ensure the enum
|
|
||||||
// doesn't change without us looking at this logic again.
|
|
||||||
comptime {
|
|
||||||
const info = @typeInfo(CellMode).Enum;
|
|
||||||
std.debug.assert(info.fields.len == 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return @intFromEnum(self) & @intFromEnum(@as(CellMode, .fg)) != 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init() !CellProgram {
|
pub fn init() !CellProgram {
|
||||||
|
@ -22,32 +22,29 @@ uniform sampler2D text_color;
|
|||||||
|
|
||||||
// Dimensions of the cell
|
// Dimensions of the cell
|
||||||
uniform vec2 cell_size;
|
uniform vec2 cell_size;
|
||||||
|
uniform bool blink_visible;
|
||||||
|
|
||||||
// See vertex shader
|
// See vertex shader
|
||||||
const uint MODE_BG = 1u;
|
const uint MODE_FG = 1u;
|
||||||
const uint MODE_FG = 2u;
|
const uint MODE_FG_CONSTRAINED = 2u;
|
||||||
const uint MODE_FG_CONSTRAINED = 3u;
|
const uint MODE_FG_COLOR = 4u;
|
||||||
const uint MODE_FG_COLOR = 7u;
|
const uint MODE_FG_POWERLINE = 8u;
|
||||||
const uint MODE_FG_POWERLINE = 15u;
|
const uint MODE_FG_BLINK = 16u;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float a;
|
if ((mode & MODE_FG) == 0u) {
|
||||||
|
// Background
|
||||||
switch (mode) {
|
|
||||||
case MODE_BG:
|
|
||||||
out_FragColor = color;
|
out_FragColor = color;
|
||||||
break;
|
}
|
||||||
|
if ((mode & MODE_FG_BLINK) != 0u && !blink_visible) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
if ((mode & MODE_FG_COLOR) != 0u) {
|
||||||
|
out_FragColor = texture(text_color, glyph_tex_coords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case MODE_FG:
|
float a = texture(text, glyph_tex_coords).r;
|
||||||
case MODE_FG_CONSTRAINED:
|
|
||||||
case MODE_FG_POWERLINE:
|
|
||||||
a = texture(text, glyph_tex_coords).r;
|
|
||||||
vec3 premult = color.rgb * color.a;
|
vec3 premult = color.rgb * color.a;
|
||||||
out_FragColor = vec4(premult.rgb*a, a);
|
out_FragColor = vec4(premult.rgb*a, a);
|
||||||
break;
|
|
||||||
|
|
||||||
case MODE_FG_COLOR:
|
|
||||||
out_FragColor = texture(text_color, glyph_tex_coords);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ struct Uniforms {
|
|||||||
float min_contrast;
|
float min_contrast;
|
||||||
ushort2 cursor_pos;
|
ushort2 cursor_pos;
|
||||||
uchar4 cursor_color;
|
uchar4 cursor_color;
|
||||||
|
bool blink_visible;
|
||||||
};
|
};
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
@ -161,9 +162,10 @@ fragment float4 cell_bg_fragment(
|
|||||||
enum CellTextMode : uint8_t {
|
enum CellTextMode : uint8_t {
|
||||||
MODE_TEXT = 1u,
|
MODE_TEXT = 1u,
|
||||||
MODE_TEXT_CONSTRAINED = 2u,
|
MODE_TEXT_CONSTRAINED = 2u,
|
||||||
MODE_TEXT_COLOR = 3u,
|
MODE_TEXT_COLOR = 4u,
|
||||||
MODE_TEXT_CURSOR = 4u,
|
MODE_TEXT_CURSOR = 8u,
|
||||||
MODE_TEXT_POWERLINE = 5u,
|
MODE_TEXT_POWERLINE = 16u,
|
||||||
|
MODE_TEXT_BLINK = 32u,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CellTextVertexIn {
|
struct CellTextVertexIn {
|
||||||
@ -257,7 +259,7 @@ vertex CellTextVertexOut cell_text_vertex(
|
|||||||
// If we're constrained then we need to scale the glyph.
|
// If we're constrained then we need to scale the glyph.
|
||||||
// We also always constrain colored glyphs since we should have
|
// We also always constrain colored glyphs since we should have
|
||||||
// their scaled cell size exactly correct.
|
// their scaled cell size exactly correct.
|
||||||
if (in.mode == MODE_TEXT_CONSTRAINED || in.mode == MODE_TEXT_COLOR) {
|
if (in.mode & (MODE_TEXT_CONSTRAINED | MODE_TEXT_COLOR)) {
|
||||||
float max_width = uniforms.cell_size.x * in.constraint_width;
|
float max_width = uniforms.cell_size.x * in.constraint_width;
|
||||||
if (size.x > max_width) {
|
if (size.x > max_width) {
|
||||||
float new_y = size.y * (max_width / size.x);
|
float new_y = size.y * (max_width / size.x);
|
||||||
@ -285,14 +287,14 @@ vertex CellTextVertexOut cell_text_vertex(
|
|||||||
// since we want color glyphs to appear in their original color
|
// since we want color glyphs to appear in their original color
|
||||||
// and Powerline glyphs to be unaffected (else parts of the line would
|
// and Powerline glyphs to be unaffected (else parts of the line would
|
||||||
// have different colors as some parts are displayed via background colors).
|
// have different colors as some parts are displayed via background colors).
|
||||||
if (uniforms.min_contrast > 1.0f && in.mode == MODE_TEXT) {
|
if (uniforms.min_contrast > 1.0f && !(in.mode & ~(MODE_TEXT | MODE_TEXT_BLINK))) {
|
||||||
float4 bg_color = float4(bg_colors[in.grid_pos.y * uniforms.grid_size.x + in.grid_pos.x]) / 255.0f;
|
float4 bg_color = float4(bg_colors[in.grid_pos.y * uniforms.grid_size.x + in.grid_pos.x]) / 255.0f;
|
||||||
out.color = contrasted_color(uniforms.min_contrast, out.color, bg_color);
|
out.color = contrasted_color(uniforms.min_contrast, out.color, bg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this cell is the cursor cell, then we need to change the color.
|
// If this cell is the cursor cell, then we need to change the color.
|
||||||
if (
|
if (
|
||||||
in.mode != MODE_TEXT_CURSOR &&
|
!(in.mode & MODE_TEXT_CURSOR) &&
|
||||||
in.grid_pos.x == uniforms.cursor_pos.x &&
|
in.grid_pos.x == uniforms.cursor_pos.x &&
|
||||||
in.grid_pos.y == uniforms.cursor_pos.y
|
in.grid_pos.y == uniforms.cursor_pos.y
|
||||||
) {
|
) {
|
||||||
@ -305,7 +307,8 @@ vertex CellTextVertexOut cell_text_vertex(
|
|||||||
fragment float4 cell_text_fragment(
|
fragment float4 cell_text_fragment(
|
||||||
CellTextVertexOut in [[stage_in]],
|
CellTextVertexOut in [[stage_in]],
|
||||||
texture2d<float> textureGrayscale [[texture(0)]],
|
texture2d<float> textureGrayscale [[texture(0)]],
|
||||||
texture2d<float> textureColor [[texture(1)]]
|
texture2d<float> textureColor [[texture(1)]],
|
||||||
|
constant Uniforms& uniforms [[buffer(1)]]
|
||||||
) {
|
) {
|
||||||
constexpr sampler textureSampler(
|
constexpr sampler textureSampler(
|
||||||
coord::pixel,
|
coord::pixel,
|
||||||
@ -313,12 +316,13 @@ fragment float4 cell_text_fragment(
|
|||||||
filter::nearest
|
filter::nearest
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (in.mode) {
|
if (in.mode & MODE_TEXT_COLOR) {
|
||||||
default:
|
return textureColor.sample(textureSampler, in.tex_coord);
|
||||||
case MODE_TEXT_CURSOR:
|
}
|
||||||
case MODE_TEXT_CONSTRAINED:
|
if (in.mode & MODE_TEXT_BLINK && !uniforms.blink_visible) {
|
||||||
case MODE_TEXT_POWERLINE:
|
discard_fragment();
|
||||||
case MODE_TEXT: {
|
}
|
||||||
|
|
||||||
// We premult the alpha to our whole color since our blend function
|
// We premult the alpha to our whole color since our blend function
|
||||||
// uses One/OneMinusSourceAlpha to avoid blurry edges.
|
// uses One/OneMinusSourceAlpha to avoid blurry edges.
|
||||||
// We first premult our given color.
|
// We first premult our given color.
|
||||||
@ -330,12 +334,6 @@ fragment float4 cell_text_fragment(
|
|||||||
|
|
||||||
return premult;
|
return premult;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MODE_TEXT_COLOR: {
|
|
||||||
return textureColor.sample(textureSampler, in.tex_coord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
// Image Shader
|
// Image Shader
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
// used to multiplex multiple render modes into a single shader.
|
// used to multiplex multiple render modes into a single shader.
|
||||||
//
|
//
|
||||||
// NOTE: this must be kept in sync with the fragment shader
|
// NOTE: this must be kept in sync with the fragment shader
|
||||||
const uint MODE_BG = 1u;
|
const uint MODE_FG = 1u;
|
||||||
const uint MODE_FG = 2u;
|
const uint MODE_FG_CONSTRAINED = 2u;
|
||||||
const uint MODE_FG_CONSTRAINED = 3u;
|
const uint MODE_FG_COLOR = 4u;
|
||||||
const uint MODE_FG_COLOR = 7u;
|
const uint MODE_FG_POWERLINE = 8u;
|
||||||
const uint MODE_FG_POWERLINE = 15u;
|
const uint MODE_FG_BLINK = 16u;
|
||||||
|
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
layout (location = 0) in vec2 grid_coord;
|
layout (location = 0) in vec2 grid_coord;
|
||||||
@ -170,8 +170,8 @@ void main() {
|
|||||||
vec2 cell_size_scaled = cell_size;
|
vec2 cell_size_scaled = cell_size;
|
||||||
cell_size_scaled.x = cell_size_scaled.x * grid_width;
|
cell_size_scaled.x = cell_size_scaled.x * grid_width;
|
||||||
|
|
||||||
switch (mode) {
|
if ((mode & MODE_FG) == 0u) {
|
||||||
case MODE_BG:
|
// Draw background
|
||||||
// If we're at the edge of the grid, we add our padding to the background
|
// If we're at the edge of the grid, we add our padding to the background
|
||||||
// to extend it. Note: grid_padding is top/right/bottom/left.
|
// to extend it. Note: grid_padding is top/right/bottom/left.
|
||||||
if (grid_coord.y == 0 && padding_vertical_top) {
|
if (grid_coord.y == 0 && padding_vertical_top) {
|
||||||
@ -194,12 +194,7 @@ void main() {
|
|||||||
|
|
||||||
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
||||||
color = color_in / 255.0;
|
color = color_in / 255.0;
|
||||||
break;
|
} else {
|
||||||
|
|
||||||
case MODE_FG:
|
|
||||||
case MODE_FG_CONSTRAINED:
|
|
||||||
case MODE_FG_COLOR:
|
|
||||||
case MODE_FG_POWERLINE:
|
|
||||||
vec2 glyph_offset_calc = glyph_offset;
|
vec2 glyph_offset_calc = glyph_offset;
|
||||||
|
|
||||||
// The glyph_offset.y is the y bearing, a y value that when added
|
// The glyph_offset.y is the y bearing, a y value that when added
|
||||||
@ -211,7 +206,7 @@ void main() {
|
|||||||
// We also always constrain colored glyphs since we should have
|
// We also always constrain colored glyphs since we should have
|
||||||
// their scaled cell size exactly correct.
|
// their scaled cell size exactly correct.
|
||||||
vec2 glyph_size_calc = glyph_size;
|
vec2 glyph_size_calc = glyph_size;
|
||||||
if (mode == MODE_FG_CONSTRAINED || mode == MODE_FG_COLOR) {
|
if ((mode & (MODE_FG_CONSTRAINED | MODE_FG_COLOR)) != 0u) {
|
||||||
if (glyph_size.x > cell_size_scaled.x) {
|
if (glyph_size.x > cell_size_scaled.x) {
|
||||||
float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x);
|
float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x);
|
||||||
glyph_offset_calc.y = glyph_offset_calc.y + ((glyph_size.y - new_y) / 2);
|
glyph_offset_calc.y = glyph_offset_calc.y + ((glyph_size.y - new_y) / 2);
|
||||||
@ -227,16 +222,10 @@ void main() {
|
|||||||
// We need to convert our texture position and size to normalized
|
// We need to convert our texture position and size to normalized
|
||||||
// device coordinates (0 to 1.0) by dividing by the size of the texture.
|
// device coordinates (0 to 1.0) by dividing by the size of the texture.
|
||||||
ivec2 text_size;
|
ivec2 text_size;
|
||||||
switch(mode) {
|
if ((mode & MODE_FG_COLOR) != 0u) {
|
||||||
case MODE_FG_CONSTRAINED:
|
|
||||||
case MODE_FG_POWERLINE:
|
|
||||||
case MODE_FG:
|
|
||||||
text_size = textureSize(text, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MODE_FG_COLOR:
|
|
||||||
text_size = textureSize(text_color, 0);
|
text_size = textureSize(text_color, 0);
|
||||||
break;
|
} else {
|
||||||
|
text_size = textureSize(text, 0);
|
||||||
}
|
}
|
||||||
vec2 glyph_tex_pos = glyph_pos / text_size;
|
vec2 glyph_tex_pos = glyph_pos / text_size;
|
||||||
vec2 glyph_tex_size = glyph_size / text_size;
|
vec2 glyph_tex_size = glyph_size / text_size;
|
||||||
@ -250,11 +239,10 @@ void main() {
|
|||||||
// and Powerline glyphs to be unaffected (else parts of the line would
|
// and Powerline glyphs to be unaffected (else parts of the line would
|
||||||
// have different colors as some parts are displayed via background colors).
|
// have different colors as some parts are displayed via background colors).
|
||||||
vec4 color_final = color_in / 255.0;
|
vec4 color_final = color_in / 255.0;
|
||||||
if (min_contrast > 1.0 && mode == MODE_FG) {
|
if (min_contrast > 1.0 && (mode & ~(MODE_FG | MODE_FG_BLINK)) != 0u) {
|
||||||
vec4 bg_color = bg_color_in / 255.0;
|
vec4 bg_color = bg_color_in / 255.0;
|
||||||
color_final = contrasted_color(min_contrast, color_final, bg_color);
|
color_final = contrasted_color(min_contrast, color_final, bg_color);
|
||||||
}
|
}
|
||||||
color = color_final;
|
color = color_final;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user