diff --git a/shaders/cell.f.glsl b/shaders/cell.f.glsl index b7b7f3914..f536d0991 100644 --- a/shaders/cell.f.glsl +++ b/shaders/cell.f.glsl @@ -7,12 +7,23 @@ flat in uint mode; // background color. Otherwise, this is the foreground color. flat in vec4 color; +// The position of the cells top-left corner. +flat in vec2 screen_cell_pos; + +// Position the fragment coordinate to the upper left +layout(origin_upper_left) in vec4 gl_FragCoord; + // Font texture uniform sampler2D text; +// Dimensions of the cell +uniform vec2 cell_size; + // See fragment shader const uint MODE_BG = 1u; const uint MODE_FG = 2u; +const uint MODE_CURSOR_RECT = 3u; +const uint MODE_CURSOR_RECT_HOLLOW = 4u; void main() { switch (mode) { @@ -24,5 +35,45 @@ void main() { float a = texture(text, glyph_tex_coords).r; gl_FragColor = vec4(color.rgb, color.a*a); break; + + case MODE_CURSOR_RECT: + gl_FragColor = color; + break; + + case MODE_CURSOR_RECT_HOLLOW: + // Okay so yeah this is probably horrendously slow and a shader + // should never do this, but we only ever render a cursor for ONE + // rectangle so we take the slowdown for that one. + + // Default to no color. + gl_FragColor = vec4(0., 0., 0, 0.0); + + // We subtracted one from cell size because our coordinates start at 0. + // So a width of 50 means max pixel of 49. + vec2 cell_size_coords = cell_size - 1; + + // Apply padding + vec2 padding = vec2(1.,1.); + cell_size_coords = cell_size_coords - (padding * 2); + vec2 screen_cell_pos_padded = screen_cell_pos + padding; + + // Convert our frag coord to offset of this cell. We have to subtract + // 0.5 because the frag coord is in center pixels. + vec2 cell_frag_coord = gl_FragCoord.xy - screen_cell_pos_padded - 0.5; + + // If the frag coords are in the bounds, then we color it. + const float eps = 0.1; + if (cell_frag_coord.x >= 0 && cell_frag_coord.y >= 0 && + cell_frag_coord.x <= cell_size_coords.x && + cell_frag_coord.y <= cell_size_coords.y) { + if (abs(cell_frag_coord.x) < eps || + abs(cell_frag_coord.x - cell_size_coords.x) < eps || + abs(cell_frag_coord.y) < eps || + abs(cell_frag_coord.y - cell_size_coords.y) < eps) { + gl_FragColor = color; + } + } + + break; } } diff --git a/shaders/cell.v.glsl b/shaders/cell.v.glsl index 59eb02f74..6363b5689 100644 --- a/shaders/cell.v.glsl +++ b/shaders/cell.v.glsl @@ -6,6 +6,8 @@ // NOTE: this must be kept in sync with the fragment shader const uint MODE_BG = 1u; const uint MODE_FG = 2u; +const uint MODE_CURSOR_RECT = 3u; +const uint MODE_CURSOR_RECT_HOLLOW = 4u; // The grid coordinates (x, y) where x < columns and y < rows layout (location = 0) in vec2 grid_coord; @@ -38,6 +40,10 @@ flat out vec4 color; // The x/y coordinate for the glyph representing the font. out vec2 glyph_tex_coords; +// The position of the cell top-left corner in screen cords. z and w +// are width and height. +flat out vec2 screen_cell_pos; + // Pass the mode forward to the fragment shader. flat out uint mode; @@ -121,5 +127,24 @@ void main() { // Set our foreground color output color = fg_color_in / 255.; break; + + case MODE_CURSOR_RECT: + // Same as background since we're taking up the whole cell. + cell_pos = cell_pos + cell_size * position; + + gl_Position = projection * vec4(cell_pos, 0.0, 1.0); + color = bg_color_in / 255.0; + break; + + case MODE_CURSOR_RECT_HOLLOW: + // Top-left position of this cell is needed for the hollow rect. + screen_cell_pos = cell_pos; + + // Same as background since we're taking up the whole cell. + cell_pos = cell_pos + cell_size * position; + + gl_Position = projection * vec4(cell_pos, 0.0, 1.0); + color = bg_color_in / 255.0; + break; } } diff --git a/src/Grid.zig b/src/Grid.zig index 02ea575f9..ab0082376 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -37,6 +37,12 @@ font_atlas: FontAtlas, /// Whether the cursor is visible or not. This is used to control cursor /// blinking. cursor_visible: bool, +cursor_style: CursorStyle, + +const CursorStyle = enum(u8) { + box = 3, + box_hollow = 4, +}; /// The raw structure that maps directly to the buffer sent to the vertex shader. const GPUCell = struct { @@ -211,6 +217,7 @@ pub fn init(alloc: Allocator) !Grid { .texture = tex, .font_atlas = font, .cursor_visible = true, + .cursor_style = .box, }; } @@ -295,7 +302,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void { // Draw the cursor if (self.cursor_visible) { self.cells.appendAssumeCapacity(.{ - .mode = 1, + .mode = @enumToInt(self.cursor_style), .grid_col = @intCast(u16, term.cursor.x), .grid_row = @intCast(u16, term.cursor.y), .fg_r = 0, diff --git a/src/Window.zig b/src/Window.zig index 8dc40c4f8..13a0fadc8 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -221,10 +221,13 @@ fn keyCallback( fn focusCallback(window: glfw.Window, focused: bool) void { const win = window.getUserPointer(Window) orelse return; if (focused) { - win.cursor_timer.start(cursorTimerCallback, 800, 800) catch unreachable; win.wakeup = true; - } else { + win.cursor_timer.start(cursorTimerCallback, 0, 800) catch unreachable; + win.grid.cursor_style = .box; win.grid.cursor_visible = false; + } else { + win.grid.cursor_visible = true; + win.grid.cursor_style = .box_hollow; win.grid.updateCells(win.terminal) catch unreachable; win.cursor_timer.stop() catch unreachable; }