From 13cf5ba023e2d0e9d7a04fae321d081e7aeacf93 Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Sun, 3 Mar 2024 19:11:16 +1300 Subject: [PATCH] feat(term): implement copy_selection() Implements the copy_selection() method, which returns the selected text. Adds a copy_on_selection property. When this property is enabled, the selected text will automatically be copied to the primary clipboard (Linux X11/Wayland only). --- addons/godot_xterm/native/src/terminal.cpp | 37 ++++++++++++++++++++++ addons/godot_xterm/native/src/terminal.h | 6 ++++ test/test_terminal.gd | 6 ++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index e59f3e4..e5a0946 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(__linux) +#include +#endif + #define SHADERS_DIR "res://addons/godot_xterm/shaders/" #define FOREGROUND_SHADER_PATH SHADERS_DIR "foreground.gdshader" #define BACKGROUND_SHADER_PATH SHADERS_DIR "background.gdshader" @@ -64,6 +68,12 @@ void Terminal::_bind_methods() ClassDB::bind_method(D_METHOD("set_blink_off_time", "time"), &Terminal::set_blink_off_time); ClassDB::add_property("Terminal", PropertyInfo(Variant::FLOAT, "blink_off_time"), "set_blink_off_time", "get_blink_off_time"); + // Selection copying. + ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection); + ClassDB::bind_method(D_METHOD("set_copy_on_selection", "enabled"), &Terminal::set_copy_on_selection); + ClassDB::bind_method(D_METHOD("get_copy_on_selection"), &Terminal::get_copy_on_selection); + ClassDB::add_property("Terminal", PropertyInfo(Variant::BOOL, "copy_on_selection"), "set_copy_on_selection", "get_copy_on_selection"); + // Methods. ClassDB::bind_method(D_METHOD("write", "data"), &Terminal::write); @@ -89,6 +99,8 @@ Terminal::Terminal() bell_timer->set_one_shot(true); add_child(bell_timer, false, INTERNAL_MODE_FRONT); + copy_on_selection = false; + inverse_mode = InverseMode::INVERSE_MODE_INVERT; if (tsm_screen_new(&screen, NULL, NULL)) @@ -629,6 +641,25 @@ double Terminal::get_blink_off_time() const return blink_off_time; } +String Terminal::copy_selection() { + char *out; + PackedByteArray data; + + data.resize(tsm_screen_selection_copy(screen, &out)); + memcpy(data.ptrw(), out, data.size()); + std::free(out); + + return data.get_string_from_utf8(); +} + +void Terminal::set_copy_on_selection(const bool p_enabled) { + copy_on_selection = p_enabled; +} + +bool Terminal::get_copy_on_selection() const { + return copy_on_selection; +} + void Terminal::set_inverse_mode(const int mode) { inverse_mode = static_cast(mode); @@ -754,6 +785,12 @@ void Terminal::_handle_selection(Ref event) { void Terminal::_on_selection_held() { if (!(Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) || selection_mode == SelectionMode::NONE) { + #if defined(__linux__) + if (copy_on_selection) { + DisplayServer::get_singleton()->clipboard_set_primary(copy_selection()); + } + #endif + selection_timer->stop(); return; } diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index 88610a7..0f46f61 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -71,6 +71,10 @@ namespace godot void set_blink_off_time(const double p_blink_off_time); double get_blink_off_time() const; + String copy_selection(); + void set_copy_on_selection(const bool p_enable); + bool get_copy_on_selection() const; + void set_inverse_mode(const int mode); int get_inverse_mode() const; @@ -89,6 +93,8 @@ namespace godot double blink_on_time; double blink_off_time; + bool copy_on_selection; + InverseMode inverse_mode; RenderingServer *rs; diff --git a/test/test_terminal.gd b/test/test_terminal.gd index 21a0f8b..6b48072 100644 --- a/test/test_terminal.gd +++ b/test/test_terminal.gd @@ -32,8 +32,7 @@ class TestInterface: func test_has_property_blink_off_time(): assert_has_property_with_default_value("blink_off_time", 0.3) - # TODO: Implement copy_on_selection property. - func xtest_has_property_copy_on_selection(): + func test_has_property_copy_on_selection(): assert_has_property_with_default_value("copy_on_selection", false) # TODO: Implement update_mode property. @@ -53,8 +52,7 @@ class TestInterface: func xtest_has_method_copy_all(): assert_has_method_with_return_type("copy_all", TYPE_STRING) - # TODO: Implement copy_selection() method. - func xtest_has_method_copy_selection(): + func test_has_method_copy_selection(): assert_has_method_with_return_type("copy_selection", TYPE_STRING) func test_has_method_get_cols():