diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 29d7381..e616764 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -246,7 +246,7 @@ jobs: path: docs/demo test: - name: Test (${{ matrix.platform }}, ${{ matrix.arch }}, debug) + name: Test (${{ matrix.test-type }}, ${{ matrix.platform }}, ${{ matrix.arch }}, debug) needs: [check-archive, check-pre-commit] runs-on: ${{ matrix.os }} strategy: @@ -254,6 +254,10 @@ jobs: matrix: platform: [linux, windows] # Testing not currently supported on macOS. bits: [64, 32] + test-type: [headless, rendering] + exclude: + - platform: windows + test-type: rendering include: - platform: linux os: ubuntu-22.04 @@ -302,7 +306,13 @@ jobs: name: libgodot-xterm-debug path: addons/godot_xterm/native/bin - name: Test - run: just test + shell: bash + run: | + if [ "${{ matrix.test-type }}" = "headless" ]; then + just test + else + just test-${{ matrix.test-type }} + fi delete-asset-imports: name: Delete Asset Imports diff --git a/Justfile b/Justfile index 4475a94..d168d5c 100644 --- a/Justfile +++ b/Justfile @@ -12,7 +12,10 @@ install: {{godot}} --headless -s plug.gd install test: - {{godot}} --headless -s addons/gut/gut_cmdln.gd -gdir=res://test -gexit + {{godot}} --headless -s addons/gut/gut_cmdln.gd -gtest=res://test/test_terminal.gd -gexit + +test-rendering: + {{godot}} --windowed --resolution 400x200 --position 0,0 -s addons/gut/gut_cmdln.gd -gtest=res://test/test_rendering.gd -gopacity=0 -gexit uninstall: {{godot}} --headless -s plug.gd uninstall diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 8b01291..b7b318a 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -70,6 +70,7 @@ void Terminal::_bind_methods() ClassDB::bind_method(D_METHOD("write", "data"), &Terminal::write); ClassDB::bind_method(D_METHOD("get_cursor_pos"), &Terminal::get_cursor_pos); ClassDB::bind_method(D_METHOD("get_cell_size"), &Terminal::get_cell_size); + ClassDB::bind_method(D_METHOD("_on_frame_post_draw"), &Terminal::_on_frame_post_draw); ClassDB::bind_method(D_METHOD("_on_gui_input", "event"), &Terminal::_gui_input); ClassDB::bind_method(D_METHOD("_on_selection_held"), &Terminal::_on_selection_held); } @@ -173,7 +174,7 @@ String Terminal::write(const Variant data) response.clear(); tsm_vte_input(vte, (char *)bytes.ptr(), bytes.size()); - queue_redraw(); + redraw_requested = true; return response.get_string_from_utf8(); } @@ -489,7 +490,7 @@ void Terminal::initialize_rendering() { rs->viewport_set_disable_3d(viewport, true); rs->viewport_set_transparent_background(viewport, true); rs->viewport_set_clear_mode(viewport, RenderingServer::ViewportClearMode::VIEWPORT_CLEAR_NEVER); - rs->viewport_set_update_mode(viewport, RenderingServer::ViewportUpdateMode::VIEWPORT_UPDATE_WHEN_VISIBLE); + rs->viewport_set_update_mode(viewport, RenderingServer::ViewportUpdateMode::VIEWPORT_UPDATE_ALWAYS); rs->viewport_set_active(viewport, true); fore_shader = rl->load(FOREGROUND_SHADER_PATH); @@ -501,6 +502,8 @@ void Terminal::initialize_rendering() { fore_canvas_item = rs->canvas_item_create(); rs->canvas_item_set_material(fore_canvas_item, fore_material->get_rid()); rs->canvas_item_set_parent(fore_canvas_item, get_canvas_item()); + + rs->connect("frame_post_draw", Callable(this, "_on_frame_post_draw")); } void Terminal::update_theme() { @@ -521,6 +524,13 @@ void Terminal::update_theme() { refresh(); } +void Terminal::_on_frame_post_draw() { + if (redraw_requested) { + queue_redraw(); + redraw_requested = false; + } +} + void Terminal::draw_screen() { if (framebuffer_age == 0) { Rect2 rect = Rect2(Vector2(), size); diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index 42152f9..1ba7f28 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -143,6 +143,8 @@ namespace godot void update_theme(); void update_sizes(bool force = false); void update_shader_parameters(Ref material); + bool redraw_requested = false; + void _on_frame_post_draw(); void draw_screen(); void refresh(); void cleanup_rendering(); diff --git a/test/test_rendering.gd b/test/test_rendering.gd new file mode 100644 index 0000000..a91172d --- /dev/null +++ b/test/test_rendering.gd @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2024 Leroy Hopson +# SPDX-License-Identifier: MIT + +class_name RenderingTest extends GutTest + +var terminal: Terminal + + +# Return the color in the center of the given cell. +func pick_cell_color(cell := Vector2i(0, 0)) -> Color: + var cell_size = terminal.get_cell_size() + var pixelv = Vector2(cell) * cell_size + (cell_size / 2) + return get_viewport().get_texture().get_image().get_pixelv(cell_size / 2) + + +func before_each(): + terminal = Terminal.new() + terminal.add_theme_font_override("normal_font", preload("res://themes/fonts/regular.tres")) + terminal.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + call_deferred("add_child_autofree", terminal) + await wait_for_signal(terminal.ready, 5) + + +class TestRendering: + extends RenderingTest + + func test_update(): + terminal.write("\u001b[38;2;255;0;0m") + terminal.write("█".repeat(terminal.get_cols() * terminal.get_rows())) + await get_tree().physics_frame + terminal.queue_redraw() + await wait_for_signal(terminal.draw, 3) + await wait_frames(10) + var cell_color = pick_cell_color(Vector2i(0, 0)) + assert_eq(cell_color, Color.RED)