diff --git a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd index df944bf..558a1f3 100644 --- a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd +++ b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd @@ -4,7 +4,6 @@ extends "../../terminal.gd" signal exited(exit_code, signum) var editor_settings: EditorSettings -var timer := Timer.new() onready var pty = $PTY @@ -51,20 +50,6 @@ func _ready(): ) _native_terminal._update_theme() - # In editor _process is not called continuously unless the "Update Continuously" - # editor setting is enabled. This setting is disabled by default and uses 100% - # of one core when enabled, so best to leave it off and use a timer instead. - add_child(timer) - timer.wait_time = 0.025 - timer.connect("timeout", self, "_poll") - timer.start() - - -func _poll(): - if pty and pty.has_method("get_master"): - pty.get_master().poll() - update() - func _input(event): if has_focus() and event is InputEventKey and event.is_pressed(): diff --git a/addons/godot_xterm/nodes/pty/pty_native.gd b/addons/godot_xterm/nodes/pty/pty_native.gd index 8c1e27a..e3f1330 100644 --- a/addons/godot_xterm/nodes/pty/pty_native.gd +++ b/addons/godot_xterm/nodes/pty/pty_native.gd @@ -9,6 +9,10 @@ func open(cols: int, rows: int): return _not_implemented() +func run_process(delta: float): + return _not_implemented() + + func resize(cols: int, rows: int): return _not_implemented() diff --git a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd index 163c5e4..0d496b1 100644 --- a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd +++ b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd @@ -91,6 +91,11 @@ func write(data) -> void: _pipe.write(data) +func run_process(_delta): + if _pipe: + _pipe.poll() + + func resize(cols: int, rows: int) -> void: if cols <= 0 or rows <= 0 or cols == NAN or rows == NAN or cols == INF or rows == INF: push_error("Resizing must be done using positive cols and rows.") @@ -126,11 +131,6 @@ func _parse_env(env: Dictionary = {}) -> PoolStringArray: return pairs -func _process(_delta): - if _pipe: - _pipe.poll() - - func fork( file: String = OS.get_environment("SHELL"), args: PoolStringArray = PoolStringArray(), diff --git a/addons/godot_xterm/pty.gd b/addons/godot_xterm/pty.gd index ae3defe..1676551 100644 --- a/addons/godot_xterm/pty.gd +++ b/addons/godot_xterm/pty.gd @@ -23,6 +23,13 @@ const Signal = _PTYUnix.Signal signal data_received(data) signal exited(exit_code, signum) +enum ProcessMode { + IDLE, + PHYSICS, +} + +export(ProcessMode) var process_mode := ProcessMode.IDLE + export(NodePath) var terminal_path := NodePath() setget set_terminal_path var _terminal: _Terminal = null setget _set_terminal @@ -150,6 +157,16 @@ func get_master(): return _pty_native.get_master() +func _process(delta: float): + if process_mode == ProcessMode.IDLE: + _pty_native.run_process(delta) + + +func _physics_process(delta: float): + if process_mode == ProcessMode.PHYSICS: + _pty_native.run_process(delta) + + func _on_pty_native_data_received(data): emit_signal("data_received", data) diff --git a/addons/godot_xterm/terminal.gd b/addons/godot_xterm/terminal.gd index f5dd9dd..22d89c0 100644 --- a/addons/godot_xterm/terminal.gd +++ b/addons/godot_xterm/terminal.gd @@ -82,6 +82,7 @@ func write(data) -> void: # Will be cleared when _flush() is called after VisualServer emits the "frame_pre_draw" signal. _buffer.push_back(data) + update() # Queue the CanvasItem for updates. Ensures VisualServer will draw a frame, otherwise it might not. func _flush(): diff --git a/test/mocks/mock_pty_native.gd b/test/mocks/mock_pty_native.gd new file mode 100644 index 0000000..708d407 --- /dev/null +++ b/test/mocks/mock_pty_native.gd @@ -0,0 +1,9 @@ +extends "res://addons/godot_xterm/nodes/pty/pty_native.gd" + + +func write(data): + emit_signal("data_received", data) + + +func run_process(_delta: float): + pass diff --git a/test/unit/pty.test.gd b/test/unit/pty.test.gd index f50be5c..19d4155 100644 --- a/test/unit/pty.test.gd +++ b/test/unit/pty.test.gd @@ -1,27 +1,38 @@ extends "res://addons/gut/test.gd" -class MockPTY: - extends "res://addons/godot_xterm/nodes/pty/pty_native.gd" - - func write(data): - emit_signal("data_received", data) - - class BaseTest: extends "res://addons/gut/test.gd" const PTY := preload("res://addons/godot_xterm/pty.gd") + const MockPTYPath := "res://test/mocks/mock_pty_native.gd" + const MockPTY := preload(MockPTYPath) var pty: PTY var mock_pty_native: MockPTY func before_each(): pty = add_child_autofree(PTY.new()) - mock_pty_native = autofree(MockPTY.new()) + mock_pty_native = autofree(double(MockPTYPath).new()) pty._pty_native = mock_pty_native watch_signals(mock_pty_native) +class TestProcessMode: + extends BaseTest + + func test_updates_only_during_idle_time(): + pty.process_mode = PTY.ProcessMode.IDLE + for _i in range(4): + yield(get_tree(), "idle_frame") + assert_between(get_call_count(mock_pty_native, "run_process"), 3, 4) + + func test_updates_only_during_physics_step(): + pty.process_mode = PTY.ProcessMode.PHYSICS + for _i in range(4): + yield(get_tree(), "physics_frame") + assert_between(get_call_count(mock_pty_native, "run_process"), 3, 4) + + class TestPTYInterfaceGodotXterm2_0_0: extends BaseTest # Test that PTY class conforms to the GodotXterm 2.0.0 specification published at: