diff --git a/README.md b/README.md index 0392706..ad46f27 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,10 @@ There are three example scenes included in this project which you can study to l - terminal.tscn - asciicast.tscn +## Credits + +[bell.wav](/examples/bell.wav) by InspectorJ (), downloaded from [freesound.org](https://freesound.org/people/InspectorJ/sounds/484344/), released under [CC-BY-3.0](https://creativecommons.org/licenses/by/3.0/). + ## License If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index e2da0a0..6a5b7f2 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -268,6 +268,11 @@ static int text_draw_cb(struct tsm_screen *con, uint64_t id, const uint32_t *ch, return 0; } +static void bell_cb(tsm_vte *_vte, void *data) { + Terminal *terminal = static_cast(data); + terminal->emit_signal("bell"); +} + void Terminal::_register_methods() { register_method("_init", &Terminal::_init); register_method("_ready", &Terminal::_ready); @@ -291,6 +296,7 @@ void Terminal::_register_methods() { "event", GODOT_VARIANT_TYPE_OBJECT); register_signal("size_changed", "new_size", GODOT_VARIANT_TYPE_VECTOR2); + register_signal("bell", Dictionary()); } Terminal::Terminal() {} @@ -310,6 +316,8 @@ void Terminal::_init() { if (tsm_vte_new(&vte, screen, write_cb, this, NULL, NULL)) { ERR_PRINT("Error creating new tsm vte"); } + + tsm_vte_set_bell_cb(vte, bell_cb, this); } void Terminal::_ready() { update_theme(); } diff --git a/addons/godot_xterm/nodes/terminal/terminal.gd b/addons/godot_xterm/nodes/terminal/terminal.gd index 8d0dee0..2b69edf 100644 --- a/addons/godot_xterm/nodes/terminal/terminal.gd +++ b/addons/godot_xterm/nodes/terminal/terminal.gd @@ -6,6 +6,7 @@ const DefaultTheme = preload("../../themes/default.tres") signal data_sent(data) signal key_pressed(data, event) signal size_changed(new_size) +signal bell enum UpdateMode { DISABLED, @@ -62,6 +63,7 @@ func _ready(): _native_terminal.connect("data_sent", self, "_on_data_sent") _native_terminal.connect("key_pressed", self, "_on_key_pressed") _native_terminal.connect("size_changed", self, "_on_size_changed") + _native_terminal.connect("bell", self, "_on_bell") _viewport.size = rect_size _viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS @@ -112,6 +114,10 @@ func _on_size_changed(new_size: Vector2): emit_signal("size_changed", new_size) +func _on_bell(): + emit_signal("bell") + + func _set_size_warning(value): if value: push_warning( diff --git a/examples/bell.wav b/examples/bell.wav new file mode 100644 index 0000000..f14e2ad Binary files /dev/null and b/examples/bell.wav differ diff --git a/examples/bell.wav.import b/examples/bell.wav.import new file mode 100644 index 0000000..edd02d3 --- /dev/null +++ b/examples/bell.wav.import @@ -0,0 +1,21 @@ +[remap] + +importer="wav" +type="AudioStreamSample" +path="res://.import/bell.wav-90c86fbe33880ba47c154bebbc865497.sample" + +[deps] + +source_file="res://examples/bell.wav" +dest_files=[ "res://.import/bell.wav-90c86fbe33880ba47c154bebbc865497.sample" ] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop=false +compress/mode=0 diff --git a/examples/terminal/terminal.gd b/examples/terminal/terminal.gd index 851ac19..5e6209b 100644 --- a/examples/terminal/terminal.gd +++ b/examples/terminal/terminal.gd @@ -2,6 +2,31 @@ extends GDXterm.Terminal onready var pty = $PTY +var _next_bell := true + func _ready(): pty.fork(OS.get_environment("SHELL")) + + +func _on_Terminal_bell(): + # Limit the rate at which bells can be rung in case the user does something crazy such as + # `while true; do echo -e "\a"; done` + # which causes a real mess if we keep adding AudioStreamPlayers. + if not _next_bell: + return + + var player := AudioStreamPlayer.new() + player.stream = preload("../bell.wav") + player.autoplay = true + player.connect("finished", self, "_on_player_finished", [player]) + add_child(player) + _next_bell = false + + +func _on_player_finished(player: AudioStreamPlayer): + player.queue_free() + + +func _on_Timer_timeout(): + _next_bell = true diff --git a/examples/terminal/terminal.tscn b/examples/terminal/terminal.tscn index 0302b84..1bcaa11 100644 --- a/examples/terminal/terminal.tscn +++ b/examples/terminal/terminal.tscn @@ -19,3 +19,10 @@ env = { "COLORTERM": "truecolor", "TERM": "xterm-256color" } + +[node name="Timer" type="Timer" parent="."] +wait_time = 0.1 +autostart = true + +[connection signal="bell" from="." to="." method="_on_Terminal_bell"] +[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] diff --git a/test/unit/terminal.test.gd b/test/unit/terminal.test.gd new file mode 100644 index 0000000..39e3c6b --- /dev/null +++ b/test/unit/terminal.test.gd @@ -0,0 +1,27 @@ +extends WAT.Test + +var term: GDXterm.Terminal + + +func pre(): + term = GDXterm.Terminal.new() + term.rect_size = Vector2(400, 200) + add_child(term) + + +func post(): + term.free() + + +func after(): + term.free() + + +func test_bell() -> void: + term.write(char(7)) + term.write(char(0x07)) + term.write("\a") + term.write("\u0007") + term.write("'Ask not for whom the \a tolls; it tolls for thee' - John Donne") + yield(until_signal(term, "bell", 1), YIELD) + asserts.signal_was_emitted_x_times(term, "bell", 5)