mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-24 02:20:24 +01:00
feat(pty): add cols/rows methods and resize tests
This commit is contained in:
parent
d00a31fb45
commit
0bd0d39f41
4 changed files with 122 additions and 52 deletions
|
@ -47,6 +47,14 @@ void PTY::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
||||||
ADD_SIGNAL(MethodInfo("exited", PropertyInfo(Variant::INT, "exit_code"), PropertyInfo(Variant::INT, "signal_code")));
|
ADD_SIGNAL(MethodInfo("exited", PropertyInfo(Variant::INT, "exit_code"), PropertyInfo(Variant::INT, "signal_code")));
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_cols", "num_cols"), &PTY::set_cols);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_cols"), &PTY::get_cols);
|
||||||
|
ClassDB::add_property("PTY", PropertyInfo(Variant::INT, "cols"), "set_cols", "get_cols");
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_rows", "num_rows"), &PTY::set_rows);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_rows"), &PTY::get_rows);
|
||||||
|
ClassDB::add_property("PTY", PropertyInfo(Variant::INT, "rows"), "set_rows", "get_rows");
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_env"), &PTY::get_env);
|
ClassDB::bind_method(D_METHOD("get_env"), &PTY::get_env);
|
||||||
ClassDB::bind_method(D_METHOD("set_env", "env"), &PTY::set_env);
|
ClassDB::bind_method(D_METHOD("set_env", "env"), &PTY::set_env);
|
||||||
ClassDB::add_property("PTY", PropertyInfo(Variant::DICTIONARY, "env"), "set_env", "get_env");
|
ClassDB::add_property("PTY", PropertyInfo(Variant::DICTIONARY, "env"), "set_env", "get_env");
|
||||||
|
@ -90,10 +98,24 @@ PTY::PTY() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PTY::set_cols(const int num_cols) {
|
||||||
|
if (cols != num_cols) {
|
||||||
|
cols = num_cols;
|
||||||
|
resize(cols, rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PTY::get_cols() const {
|
int PTY::get_cols() const {
|
||||||
return cols;
|
return cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PTY::set_rows(const int num_rows) {
|
||||||
|
if (rows != num_rows) {
|
||||||
|
rows = num_rows;
|
||||||
|
resize(cols, rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int PTY::get_rows() const {
|
int PTY::get_rows() const {
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
@ -127,14 +149,14 @@ String PTY::get_pts_name() const {
|
||||||
return pts_name;
|
return pts_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error PTY::fork(const String &file, const PackedStringArray &args, const String &cwd, const int cols, const int rows) {
|
Error PTY::fork(const String &file, const PackedStringArray &args, const String &cwd, const int p_cols, const int p_rows) {
|
||||||
String fork_file = _get_fork_file(file);
|
String fork_file = _get_fork_file(file);
|
||||||
Dictionary fork_env = _get_fork_env();
|
Dictionary fork_env = _get_fork_env();
|
||||||
Dictionary result;
|
Dictionary result;
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
String helper_path = ProjectSettings::get_singleton()->globalize_path("res://addons/godot_xterm/native/bin/spawn-helper");
|
String helper_path = ProjectSettings::get_singleton()->globalize_path("res://addons/godot_xterm/native/bin/spawn-helper");
|
||||||
result = PTYUnix::fork(fork_file, args, _parse_env(fork_env), cwd, cols, rows, -1, -1, true, helper_path, Callable(this, "_on_exit"));
|
result = PTYUnix::fork(fork_file, args, _parse_env(fork_env), cwd, p_cols, p_rows, -1, -1, true, helper_path, Callable(this, "_on_exit"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Error err = static_cast<Error>((int)result["error"]);
|
Error err = static_cast<Error>((int)result["error"]);
|
||||||
|
@ -178,14 +200,22 @@ Error PTY::open(const int cols, const int rows) {
|
||||||
Error err = static_cast<Error>((int)result["error"]);
|
Error err = static_cast<Error>((int)result["error"]);
|
||||||
ERR_FAIL_COND_V(err != OK, err);
|
ERR_FAIL_COND_V(err != OK, err);
|
||||||
|
|
||||||
|
fd = result["master"];
|
||||||
pts_name = result["pty"];
|
pts_name = result["pty"];
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PTY::resize(const int cols, const int rows) const {
|
void PTY::resize(const int p_cols, const int p_rows) {
|
||||||
|
cols = p_cols;
|
||||||
|
rows = p_rows;
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
if (fd > -1) {
|
||||||
PTYUnix::resize(fd, cols, rows);
|
PTYUnix::resize(fd, cols, rows);
|
||||||
|
} else {
|
||||||
|
ERR_PRINT("fd <= -1");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,9 @@ namespace godot
|
||||||
|
|
||||||
Status status = STATUS_CLOSED;
|
Status status = STATUS_CLOSED;
|
||||||
|
|
||||||
|
void set_cols(const int num_cols);
|
||||||
int get_cols() const;
|
int get_cols() const;
|
||||||
|
void set_rows(const int num_rows);
|
||||||
int get_rows() const;
|
int get_rows() const;
|
||||||
|
|
||||||
Dictionary get_env() const;
|
Dictionary get_env() const;
|
||||||
|
@ -60,8 +62,8 @@ namespace godot
|
||||||
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);
|
||||||
void kill(const int signum = Signal::SIGNAL_SIGHUP);
|
void kill(const int signum = Signal::SIGNAL_SIGHUP);
|
||||||
Error open(const int cols = 80, const int rows = 24);
|
Error open(const int cols = 80, const int rows = 24);
|
||||||
void resize(const int cols, const int rows) const;
|
void resize(const int cols, const int rows);
|
||||||
void resizev(const Vector2i &size) const { resize(size.x, size.y); };
|
void resizev(const Vector2i &size) { resize(size.x, size.y); };
|
||||||
void write(const Variant &data) const;
|
void write(const Variant &data) const;
|
||||||
|
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
@ -73,8 +75,8 @@ namespace godot
|
||||||
int pid = -1;
|
int pid = -1;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
|
||||||
unsigned int cols = 0;
|
unsigned int cols = 80;
|
||||||
unsigned int rows = 0;
|
unsigned int rows = 24;
|
||||||
|
|
||||||
Dictionary env = Dictionary();
|
Dictionary env = Dictionary();
|
||||||
bool use_os_env = true;
|
bool use_os_env = true;
|
||||||
|
|
116
test/test_nix.gd
116
test/test_nix.gd
|
@ -152,55 +152,95 @@ class Helper:
|
||||||
return {rows = int(size.x), cols = int(size.y)}
|
return {rows = int(size.x), cols = int(size.y)}
|
||||||
|
|
||||||
|
|
||||||
class XTestPTYSize:
|
class TestPTYSize:
|
||||||
extends NixTest
|
extends GodotXtermTest
|
||||||
# Tests to check that psuedoterminal size (as reported by the stty command)
|
|
||||||
# matches the size of the Terminal node. Uses various scene tree layouts with
|
|
||||||
# Terminal and PTY nodes in different places.
|
|
||||||
# See: https://github.com/lihop/godot-xterm/issues/56
|
|
||||||
|
|
||||||
var terminal: Terminal
|
|
||||||
var scene: Node
|
|
||||||
var regex := RegEx.new()
|
var regex := RegEx.new()
|
||||||
|
|
||||||
|
func get_described_class():
|
||||||
|
return PTY
|
||||||
|
|
||||||
func before_all():
|
func before_all():
|
||||||
regex.compile(".*rows (?<rows>[0-9]+).*columns (?<columns>[0-9]+).*")
|
# Depending on the implementation, the output of stty -a may vary.
|
||||||
|
# For example, on linux the format is "rows 24; columns 80;", while on
|
||||||
|
# macOS it is "rows 24; columns 80;". This regex should match both.
|
||||||
|
(
|
||||||
|
regex
|
||||||
|
. compile(
|
||||||
|
".*rows (?<rows>[0-9]+).*columns (?<columns>[0-9]+).*|.*; (?<rows>[0-9]+) rows; (?<columns>[0-9]+) columns.*"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
func before_each():
|
# Get the size as reported by stty.
|
||||||
scene = add_child_autofree(preload("res://test/scenes/pty_and_terminal.tscn").instantiate())
|
func get_stty_size() -> Vector2i:
|
||||||
|
await wait_frames(1)
|
||||||
func xtest_correct_stty_reports_correct_size():
|
|
||||||
for s in [
|
|
||||||
"PTYChild",
|
|
||||||
"PTYSiblingAbove",
|
|
||||||
"PTYSiblingBelow",
|
|
||||||
"PTYCousinAbove",
|
|
||||||
"PTYCousinBelow",
|
|
||||||
"PTYCousinAbove2",
|
|
||||||
"PTYCousinBelow2"
|
|
||||||
]:
|
|
||||||
subject = scene.get_node(s).find_child("PTY")
|
|
||||||
terminal = scene.get_node(s).find_child("Terminal")
|
|
||||||
|
|
||||||
subject.call_deferred("fork", OS.get_environment("SHELL"))
|
|
||||||
subject.call_deferred("write", "stty -a | head -n1\n")
|
subject.call_deferred("write", "stty -a | head -n1\n")
|
||||||
var output := ""
|
var output := ""
|
||||||
while not "rows" in output and not "columns" in output:
|
while not "rows" in output or not "columns" in output:
|
||||||
output = (await subject.data_received).get_string_from_utf8()
|
output += (await subject.data_received).get_string_from_utf8()
|
||||||
var regex_match = regex.search(output)
|
var regex_match = regex.search(output)
|
||||||
var stty_rows = int(regex_match.get_string("rows"))
|
var stty_rows = int(regex_match.get_string("rows"))
|
||||||
var stty_cols = int(regex_match.get_string("columns"))
|
var stty_cols = int(regex_match.get_string("columns"))
|
||||||
|
return Vector2i(stty_cols, stty_rows)
|
||||||
|
|
||||||
assert_eq(
|
func before_each():
|
||||||
stty_rows,
|
super.before_each()
|
||||||
terminal.get_rows(),
|
subject.call_deferred("fork", OS.get_environment("SHELL"))
|
||||||
"Expected stty to report correct number of rows for layout '%s'" % s
|
await wait_for_signal(subject.data_received, 1)
|
||||||
)
|
|
||||||
assert_eq(
|
func after_each():
|
||||||
stty_cols,
|
subject.call_deferred("kill", PTY.SIGNAL_SIGHUP)
|
||||||
terminal.get_cols(),
|
await wait_for_signal(subject.exited, 1)
|
||||||
"Expected stty to report correct number of columns for layout '%s'" % s
|
|
||||||
)
|
func test_pty_default_size():
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(80, 24))
|
||||||
|
|
||||||
|
func test_pty_set_cols():
|
||||||
|
subject.set_cols(5768)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(5768, 24))
|
||||||
|
|
||||||
|
func test_pty_set_rows():
|
||||||
|
subject.set_rows(5768)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(80, 5768))
|
||||||
|
|
||||||
|
func test_pty_resize():
|
||||||
|
subject.resize(2778, 8120)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(2778, 8120))
|
||||||
|
|
||||||
|
func test_pty_resizev():
|
||||||
|
subject.resizev(Vector2i(2778, 8120))
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(2778, 8120))
|
||||||
|
|
||||||
|
func test_pty_min_size():
|
||||||
|
subject.resize(0, 0)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i.ZERO)
|
||||||
|
|
||||||
|
func test_pty_max_size():
|
||||||
|
subject.resize(65535, 65535)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(65535, 65535))
|
||||||
|
|
||||||
|
func test_pty_set_size_on_open():
|
||||||
|
subject = described_class.new()
|
||||||
|
add_child_autofree(subject)
|
||||||
|
subject.call_deferred("fork", OS.get_environment("SHELL"), [], ".", 2236, 1998)
|
||||||
|
var stty_size = await get_stty_size()
|
||||||
|
assert_eq(stty_size, Vector2i(2236, 1998))
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: Currently tests fail when threads are disabled.
|
||||||
|
class XTestPTYSizeNoThreads:
|
||||||
|
extends TestPTYSize
|
||||||
|
|
||||||
|
func before_each():
|
||||||
|
super.before_each()
|
||||||
|
subject.use_threads = false
|
||||||
|
|
||||||
|
|
||||||
class LinuxHelper:
|
class LinuxHelper:
|
||||||
|
|
|
@ -13,8 +13,7 @@ class TestInterface:
|
||||||
|
|
||||||
# Properties.
|
# Properties.
|
||||||
|
|
||||||
# TODO: Implement cols property.
|
func test_has_property_cols() -> void:
|
||||||
func xtest_has_property_cols() -> void:
|
|
||||||
assert_has_property_with_default_value("cols", 80)
|
assert_has_property_with_default_value("cols", 80)
|
||||||
|
|
||||||
func test_has_property_env() -> void:
|
func test_has_property_env() -> void:
|
||||||
|
@ -22,8 +21,7 @@ class TestInterface:
|
||||||
"env", {"TERM": "xterm-256color", "COLORTERM": "truecolor"}
|
"env", {"TERM": "xterm-256color", "COLORTERM": "truecolor"}
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: Implement rows property.
|
func test_has_property_rows() -> void:
|
||||||
func xtest_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.
|
# TODO: Implement terminal_path property.
|
||||||
|
|
Loading…
Reference in a new issue