Skip to content

Commit

Permalink
feat(term): make cursor hollow when un-focused
Browse files Browse the repository at this point in the history
Closes #29.
  • Loading branch information
lihop committed Apr 28, 2024
1 parent c4c0f9c commit 4c4e61c
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 20 deletions.
3 changes: 0 additions & 3 deletions addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ func _ready():
if not editor_settings:
return

add_theme_stylebox_override("normal", get_theme_stylebox("normal", "Tree"))
add_theme_stylebox_override("focus", get_theme_stylebox("focus", "Tree"))

# Get colors from TextEdit theme. Created using the default (Adaptive) theme
# for reference, but will probably cause strange results if using another theme
# better to use a dedicated terminal theme, rather than relying on this.
Expand Down
28 changes: 18 additions & 10 deletions addons/godot_xterm/native/src/terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void Terminal::_notification(int what)
case NOTIFICATION_FOCUS_ENTER:
case NOTIFICATION_FOCUS_EXIT:
{
set_shader_parameters("has_focus", has_focus());
refresh();
break;
}
Expand Down Expand Up @@ -275,6 +276,9 @@ int Terminal::_draw_cb(struct tsm_screen *con,
attr_flags |= AttrFlag::INVERSE;
if (attr->blink)
attr_flags |= AttrFlag::BLINK;
if (term->cursor_position.x == posx && term->cursor_position.y == posy) {
attr_flags |= AttrFlag::CURSOR;
}

// Collect colors.
Color fgcol = std::min(attr->fccode, (int8_t)TSM_COLOR_FOREGROUND) >= 0
Expand Down Expand Up @@ -471,22 +475,26 @@ void Terminal::update_sizes(bool force)
attr_image = Image::create(std::max(cols, 1u), std::max(rows, 1u), false, Image::FORMAT_L8);
attr_texture->set_image(attr_image);

update_shader_parameters(back_material);
update_shader_parameters(fore_material);
set_shader_parameters();

if (force || prev_cols != cols || prev_rows != rows)
emit_signal("size_changed", Vector2i(cols, rows));

refresh();
}

void Terminal::update_shader_parameters(Ref<ShaderMaterial> material)
void Terminal::set_shader_parameters(const String &param, const Variant &value)
{
material->set_shader_parameter("cols", cols);
material->set_shader_parameter("rows", rows);
material->set_shader_parameter("size", size);
material->set_shader_parameter("cell_size", cell_size);
material->set_shader_parameter("grid_size", Vector2(cols * cell_size.x, rows * cell_size.y));
if (param.is_empty()) {
set_shader_parameters("cols", cols);
set_shader_parameters("rows", rows);
set_shader_parameters("size", size);
set_shader_parameters("cell_size", cell_size);
set_shader_parameters("grid_size", Vector2(cols * cell_size.x, rows * cell_size.y));
} else {
back_material->set_shader_parameter(param, value);
fore_material->set_shader_parameter(param, value);
}
}

void Terminal::initialize_rendering() {
Expand Down Expand Up @@ -614,6 +622,7 @@ void Terminal::draw_screen() {
}

rs->canvas_item_clear(char_canvas_item);
cursor_position = tsm_screen_get_flags(screen) & TSM_SCREEN_HIDE_CURSOR ? Vector2i(-1, -1) : get_cursor_pos();
framebuffer_age = tsm_screen_draw(screen, Terminal::_draw_cb, this);
attr_texture->update(attr_image);
back_texture->update(back_image);
Expand Down Expand Up @@ -752,8 +761,7 @@ void Terminal::set_inverse_mode(const int mode) {
inverse_mode = static_cast<InverseMode>(mode);

bool inverse_enabled = inverse_mode == InverseMode::INVERSE_MODE_INVERT;
back_material->set_shader_parameter("inverse_enabled", inverse_enabled);
fore_material->set_shader_parameter("inverse_enabled", inverse_enabled);
set_shader_parameters("inverse_enabled", inverse_enabled);

refresh();
}
Expand Down
4 changes: 3 additions & 1 deletion addons/godot_xterm/native/src/terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace godot
{
INVERSE = 1 << 0,
BLINK = 1 << 1,
CURSOR = 1 << 2,
};

enum InverseMode {
Expand Down Expand Up @@ -130,6 +131,7 @@ namespace godot
double font_offset;
Vector2 size;
Vector2 cell_size;
Vector2i cursor_position;

Ref<Image> attr_image;
Ref<ImageTexture> attr_texture;
Expand Down Expand Up @@ -157,7 +159,7 @@ namespace godot
void initialize_rendering();
void update_theme();
void update_sizes(bool force = false);
void update_shader_parameters(Ref<ShaderMaterial> material);
void set_shader_parameters(const String &name = "", const Variant &value = nullptr);
bool redraw_requested = false;
void _on_frame_post_draw();
void draw_screen();
Expand Down
27 changes: 21 additions & 6 deletions addons/godot_xterm/shaders/common.gdshaderinc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#define FLAG_INVERSE 1 << 0
#define FLAG_BLINK 1 << 1
#define FLAG_CURSOR 1 << 2

#define transparent vec4(0)

Expand All @@ -14,6 +15,7 @@ uniform vec2 grid_size;

uniform sampler2D attributes;
uniform bool inverse_enabled = true;
uniform bool has_focus = false;

#ifdef BACKGROUND
uniform vec4 background_color;
Expand Down Expand Up @@ -48,17 +50,21 @@ void fragment() {
color = texture(TEXTURE, UV);
#endif

if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) {
bool unfocused_cursor = !has_focus && has_attribute(sample_uv, FLAG_CURSOR);

#ifdef BACKGROUND
if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) {
vec4 bg_color = textureLod(screen_texture, SCREEN_UV, 0.0);
vec4 target_color;
target_color.a = color.a + (1.0 - color.a) * bg_color.a;
target_color.rgb = 1.0 / target_color.a * (color.a * color.rgb + (1.0 - color.a) * bg_color.a * bg_color.rgb);
#else
vec4 target_color = color;
#endif
color = vec4(1.0 - target_color.rgb, target_color.a);
}
#else
if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled && !unfocused_cursor) {
color.rgb = vec3(1.0 - color.rgb);
}
#endif

#ifdef FOREGROUND
if (has_attribute(sample_uv, FLAG_BLINK)) {
Expand All @@ -70,9 +76,18 @@ void fragment() {
}
#endif

#if defined(FOREGROUND) || defined(BACKGROUND)
COLOR = color;
#ifdef BACKGROUND
if (unfocused_cursor) {
// Draw hollow cursor when not focused.
bool isBorderX = (UV.x * size.x - float(cell_x) * cell_size.x) < 1.0 || (float(cell_x + 1) * cell_size.x - UV.x * size.x) < 1.0;
bool isBorderY = (UV.y * size.y - float(cell_y) * cell_size.y) < 1.0 || (float(cell_y + 1) * cell_size.y - UV.y * size.y) < 1.0;
if (!isBorderX && !isBorderY) {
color = transparent;
}
}
#endif

COLOR = color;
} else { // Outside the grid.
COLOR = transparent;
}
Expand Down
Binary file modified test/visual_regression/baseline/default_theme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/emoji.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/visual_regression/baseline/empty_focused.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/visual_regression/baseline/hollow_cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/solid_invert_selection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/solid_swap_selection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/transparency.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual_regression/baseline/transparent_invert_selection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions test/visual_regression/test_visual_regression.gd
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ class TestVisualRegression:
await wait_frames(30)
assert_match("empty")

func test_empty_focused():
subject.grab_focus()
await wait_frames(30)
assert_match("empty_focused")

func test_hollow_cursor():
subject.write("W\b")
await wait_frames(30)
assert_match("hollow_cursor")

func test_default_theme():
# Print every background color.
for i in range(8):
Expand Down

0 comments on commit 4c4e61c

Please sign in to comment.