feat(pty): add terminal_path property to pty

Property can be set to the NodePath of a Terminal node. Doing so will
conveniently set up the neccessary signals between Terminal and PTY for
them to function together.
This commit is contained in:
Leroy Hopson 2024-04-07 12:25:16 +12:00
parent fa49834347
commit e816396d60
No known key found for this signature in database
GPG key ID: D2747312A6DB51AA
3 changed files with 73 additions and 8 deletions

View file

@ -67,6 +67,10 @@ void PTY::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_using_threads"), &PTY::is_using_threads); ClassDB::bind_method(D_METHOD("is_using_threads"), &PTY::is_using_threads);
ClassDB::add_property("PTY", PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads"); ClassDB::add_property("PTY", PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
ClassDB::bind_method(D_METHOD("set_terminal_path", "path"), &PTY::set_terminal_path);
ClassDB::bind_method(D_METHOD("get_terminal_path"), &PTY::get_terminal_path);
ClassDB::add_property("PTY", PropertyInfo(Variant::NODE_PATH, "terminal_path", PropertyHint::PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Terminal"), "set_terminal_path", "get_terminal_path");
ClassDB::bind_method(D_METHOD("get_pts_name"), &PTY::get_pts_name); ClassDB::bind_method(D_METHOD("get_pts_name"), &PTY::get_pts_name);
ClassDB::bind_method(D_METHOD("fork", "file", "args", "cwd", "cols", "rows"), &PTY::fork, DEFVAL(""), DEFVAL(PackedStringArray()), DEFVAL("."), DEFVAL(80), DEFVAL(24)); ClassDB::bind_method(D_METHOD("fork", "file", "args", "cwd", "cols", "rows"), &PTY::fork, DEFVAL(""), DEFVAL(PackedStringArray()), DEFVAL("."), DEFVAL(80), DEFVAL(24));
@ -145,6 +149,36 @@ bool PTY::is_using_threads() const {
return use_threads; return use_threads;
} }
void PTY::set_terminal_path(NodePath p_terminal_path) {
terminal_path = p_terminal_path;
Callable write = Callable(this, "write");
Callable resizev = Callable(this, "resizev");
// Disconnect the current terminal, if any.
if (terminal != nullptr) {
disconnect("data_received", Callable(terminal, "write"));
terminal->disconnect("data_sent", write);
terminal->disconnect("size_changed", resizev);
}
terminal = Object::cast_to<Terminal>(get_node_or_null(terminal_path));
if (terminal == nullptr) return;
// Connect the new terminal.
resize(terminal->get_cols(), terminal->get_rows());
if (!terminal->is_connected("size_changed", resizev))
terminal->connect("size_changed", resizev, CONNECT_PERSIST);
if (!terminal->is_connected("data_sent", write))
terminal->connect("data_sent", write, CONNECT_PERSIST);
if (!is_connected("data_received", Callable(terminal, "write")))
connect("data_received", Callable(terminal, "write"), CONNECT_PERSIST);
}
NodePath PTY::get_terminal_path() const {
return terminal_path;
}
String PTY::get_pts_name() const { String PTY::get_pts_name() const {
return pts_name; return pts_name;
} }
@ -213,8 +247,6 @@ void PTY::resize(const int p_cols, const int p_rows) {
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
if (fd > -1) { if (fd > -1) {
PTYUnix::resize(fd, cols, rows); PTYUnix::resize(fd, cols, rows);
} else {
ERR_PRINT("fd <= -1");
} }
#endif #endif
} }
@ -304,8 +336,10 @@ void PTY::_close() {
uv_close((uv_handle_t *)&async_handle, _close_cb); uv_close((uv_handle_t *)&async_handle, _close_cb);
} }
uv_run(&loop, UV_RUN_NOWAIT); if (status == STATUS_OPEN) {
uv_loop_close(&loop); uv_run(&loop, UV_RUN_NOWAIT);
uv_loop_close(&loop);
}
if (fd > 0) close(fd); if (fd > 0) close(fd);
if (pid > 0) kill(SIGNAL_SIGHUP); if (pid > 0) kill(SIGNAL_SIGHUP);

View file

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "terminal.h"
#include <godot_cpp/classes/mutex.hpp> #include <godot_cpp/classes/mutex.hpp>
#include <godot_cpp/classes/node.hpp> #include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/os.hpp> #include <godot_cpp/classes/os.hpp>
@ -57,6 +59,9 @@ namespace godot
void set_use_threads(bool p_use); void set_use_threads(bool p_use);
bool is_using_threads() const; bool is_using_threads() const;
void set_terminal_path(NodePath p_terminal_path);
NodePath get_terminal_path() const;
String get_pts_name() const; String get_pts_name() const;
Error fork(const String &file = "", const PackedStringArray &args = PackedStringArray(), const String &cwd = ".", const int cols = 80, const int rows = 24); Error fork(const String &file = "", const PackedStringArray &args = PackedStringArray(), const String &cwd = ".", const int cols = 80, const int rows = 24);
@ -83,6 +88,9 @@ namespace godot
String pts_name = ""; String pts_name = "";
NodePath terminal_path;
Terminal *terminal = nullptr;
String _get_fork_file(const String &file) const; String _get_fork_file(const String &file) const;
Dictionary _get_fork_env() const; Dictionary _get_fork_env() const;
PackedStringArray _parse_env(const Dictionary &env) const; PackedStringArray _parse_env(const Dictionary &env) const;

View file

@ -24,9 +24,8 @@ class TestInterface:
func test_has_property_rows() -> void: func test_has_property_rows() -> void:
assert_has_property_with_default_value("rows", 24) assert_has_property_with_default_value("rows", 24)
# TODO: Implement terminal_path property. func test_has_property_terminal_path() -> void:
func xtest_has_property_terminal_path() -> void: assert_has_property_with_default_value("terminal_path", NodePath())
assert_has_property("terminal_path")
func test_has_proprty_use_os_env() -> void: func test_has_proprty_use_os_env() -> void:
assert_has_property_with_default_value("use_os_env", true) assert_has_property_with_default_value("use_os_env", true)
@ -82,3 +81,27 @@ class TestInterface:
func test_has_no_visible_children(): func test_has_no_visible_children():
assert_eq(subject.get_child_count(), 0) assert_eq(subject.get_child_count(), 0)
class TestSetTerminalPath:
extends PTYTest
var terminal: Terminal
func before_each():
super.before_each()
terminal = Terminal.new()
add_child_autofree(terminal)
subject.terminal_path = terminal.get_path()
func test_terminal_path_is_set():
assert_eq(subject.terminal_path, terminal.get_path())
func test_data_sent_is_connected():
assert_connected(terminal, subject, "data_sent", "write")
func test_size_changed_is_connected():
assert_connected(terminal, subject, "size_changed", "resizev")
func test_data_received_is_connected():
assert_connected(subject, terminal, "data_received", "write")