From 9bd17ec8dc2b9ef7f4cb2deed92c1d2a23167d58 Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Tue, 29 Sep 2020 16:16:59 +0700 Subject: [PATCH] Multiple changes Former-commit-id: db8e67435861d39207697937dffc81a0d83f07ae --- addons/SIsilicon.3d.text/3d_text_plugin.gd | 64 - addons/SIsilicon.3d.text/LICENSE | 21 - addons/SIsilicon.3d.text/array_utils.gd | 47 - addons/SIsilicon.3d.text/default_font.tres | 8 - addons/SIsilicon.3d.text/default_font.ttf | Bin 96852 -> 0 bytes addons/SIsilicon.3d.text/icon_label_3d.svg | 1 - .../icon_label_3d.svg.import | 34 - addons/SIsilicon.3d.text/label_3d.gd | 148 - addons/SIsilicon.3d.text/label_3d.material | Bin 1599 -> 0 bytes .../SIsilicon.3d.text/label_3d_converter.gd | 461 -- .../SIsilicon.3d.text/label_3d_converter.tscn | 40 - addons/SIsilicon.3d.text/plugin.cfg | 7 - addons/SIsilicon.3d.text/text_viewport.tscn | 74 - .../import_plugins/asciicast_import_plugin.gd | 27 +- .../godot_xterm/native/src/pseudoterminal.cpp | 66 +- .../godot_xterm/native/src/pseudoterminal.h | 6 +- addons/godot_xterm/native/src/terminal.cpp | 30 +- addons/godot_xterm/native/src/terminal.h | 2 + addons/godot_xterm/nodes/terminal/README.md | 131 + .../godot_xterm/nodes/terminal/docs/.gdignore | 0 .../nodes/terminal/docs/flow_diagram.svg | 3777 +++++++++++++++++ .../terminal/docs/important_properties.png | Bin 0 -> 11163 bytes addons/godot_xterm/resources/asciicast.gd | 4 + addons/godot_xterm/util/tput.gd | 66 + examples/asciicast/asciicast.tscn | 27 +- examples/asciicast/example.cast | 840 +++- examples/menu/menu.gd | 149 + examples/menu/menu.tscn | 28 + examples/terminal/Node.gd | 7 + .../terminal/{Terminal.tscn => terminal.tscn} | 9 +- project.godot | 15 +- 31 files changed, 5058 insertions(+), 1031 deletions(-) delete mode 100644 addons/SIsilicon.3d.text/3d_text_plugin.gd delete mode 100644 addons/SIsilicon.3d.text/LICENSE delete mode 100644 addons/SIsilicon.3d.text/array_utils.gd delete mode 100644 addons/SIsilicon.3d.text/default_font.tres delete mode 100644 addons/SIsilicon.3d.text/default_font.ttf delete mode 100644 addons/SIsilicon.3d.text/icon_label_3d.svg delete mode 100644 addons/SIsilicon.3d.text/icon_label_3d.svg.import delete mode 100644 addons/SIsilicon.3d.text/label_3d.gd delete mode 100644 addons/SIsilicon.3d.text/label_3d.material delete mode 100644 addons/SIsilicon.3d.text/label_3d_converter.gd delete mode 100644 addons/SIsilicon.3d.text/label_3d_converter.tscn delete mode 100644 addons/SIsilicon.3d.text/plugin.cfg delete mode 100644 addons/SIsilicon.3d.text/text_viewport.tscn create mode 100644 addons/godot_xterm/nodes/terminal/README.md create mode 100644 addons/godot_xterm/nodes/terminal/docs/.gdignore create mode 100644 addons/godot_xterm/nodes/terminal/docs/flow_diagram.svg create mode 100644 addons/godot_xterm/nodes/terminal/docs/important_properties.png create mode 100644 addons/godot_xterm/util/tput.gd create mode 100644 examples/menu/menu.gd create mode 100644 examples/menu/menu.tscn create mode 100644 examples/terminal/Node.gd rename examples/terminal/{Terminal.tscn => terminal.tscn} (69%) diff --git a/addons/SIsilicon.3d.text/3d_text_plugin.gd b/addons/SIsilicon.3d.text/3d_text_plugin.gd deleted file mode 100644 index 240a584..0000000 --- a/addons/SIsilicon.3d.text/3d_text_plugin.gd +++ /dev/null @@ -1,64 +0,0 @@ -tool -extends EditorPlugin - -const Label3D = preload("label_3d.gd") - -var converter_button : Button -var edited_node : Label3D - -func _enter_tree(): - yield(get_tree(), "idle_frame") - - add_custom_type( - "Label3D", "Spatial", - Label3D, - preload("icon_label_3d.svg") - ) - - if not converter_button: - converter_button = preload("label_3d_converter.tscn").instance() - add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, converter_button) - converter_button.connect("mesh_generated", self, "generate_mesh") - converter_button.hide() - - print("3d text plugin added to project.") - - -func _exit_tree(): - remove_custom_type("Label3D") - remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, converter_button) - - print("3d text plugin removed from project.") - - -func handles(object : Object) -> bool: - var handle = object is Label3D - - if not handle: - converter_button.hide() - - return handle - - -func edit(object): - edited_node = object - if edited_node is Label3D: - converter_button.show() - converter_button.label3d = object - else: - converter_button.hide() - - -func clear(): - edited_node = null - converter_button.hide() - -func generate_mesh(mesh_inst): - var undo_redo = get_undo_redo() - undo_redo.create_action("Convert Text") - - undo_redo.add_do_method(edited_node.get_parent(), "add_child", mesh_inst) - undo_redo.add_undo_method(edited_node.get_parent(), "remove_child", mesh_inst) - undo_redo.commit_action() - - mesh_inst.set_owner(get_editor_interface().get_edited_scene_root()) diff --git a/addons/SIsilicon.3d.text/LICENSE b/addons/SIsilicon.3d.text/LICENSE deleted file mode 100644 index e3f824b..0000000 --- a/addons/SIsilicon.3d.text/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Roujel Williams - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/addons/SIsilicon.3d.text/array_utils.gd b/addons/SIsilicon.3d.text/array_utils.gd deleted file mode 100644 index 257e53a..0000000 --- a/addons/SIsilicon.3d.text/array_utils.gd +++ /dev/null @@ -1,47 +0,0 @@ -class_name ArrayUtils - -# Shifts the array's values by a specified amount. -# Positive amount shifts right while negative shifts left. -static func shift_array(array : Array, amount : int): - var right = bool(sign(amount) * 0.5 + 0.5) - for i in abs(amount): - var value - if right: - value = array.pop_back() - array.push_front(value) - else: - value = array.pop_front() - array.push_back(value) - -# Combines the array with the other array. -static func append_array(array : Array, other : Array): - for i in other: - array.append(i) - -# Splits the array between a and b at index. -static func split_array(array : Array, index : int, a : Array, b : Array): - for i in range(0, index): - a.append(array[i]) - - for i in range(index, array.size()): - b.append(array[i]) - -# Converts array to dictionary -static func to_dictionary(array : Array) -> Dictionary: - var dict = {} - for i in array.size(): - dict[i] = array[i] - return dict - -# Calls a function per object in the array with variable arguments. -static func call_per_element(array : Array, function : String, vars = null): - match typeof(vars): - TYPE_NIL: - for e in array: - e.call(function) - TYPE_ARRAY: - for e in array: - e.callv(function, vars) - _: - for e in array: - e.call(function, vars) diff --git a/addons/SIsilicon.3d.text/default_font.tres b/addons/SIsilicon.3d.text/default_font.tres deleted file mode 100644 index 34c9b2b..0000000 --- a/addons/SIsilicon.3d.text/default_font.tres +++ /dev/null @@ -1,8 +0,0 @@ -[gd_resource type="DynamicFont" load_steps=2 format=2] - -[ext_resource path="res://addons/SIsilicon.3d.text/default_font.ttf" type="DynamicFontData" id=1] - -[resource] -size = 100 -use_filter = true -font_data = ExtResource( 1 ) diff --git a/addons/SIsilicon.3d.text/default_font.ttf b/addons/SIsilicon.3d.text/default_font.ttf deleted file mode 100644 index e5f34b2f342ffcd820a8a0956af3ab36bb1f9e1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96852 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

Zbo?qP=j5|;Ix-7e6n%!HUy>!_NU?V^ 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 Z?>EPL{t*~_Pwh-%lN=@Tekd \ No newline at end of file diff --git a/addons/SIsilicon.3d.text/icon_label_3d.svg.import b/addons/SIsilicon.3d.text/icon_label_3d.svg.import deleted file mode 100644 index 6a1752e..0000000 --- a/addons/SIsilicon.3d.text/icon_label_3d.svg.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/icon_label_3d.svg-66ebb0fa17446da110a5ed153702e3c3.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/SIsilicon.3d.text/icon_label_3d.svg" -dest_files=[ "res://.import/icon_label_3d.svg-66ebb0fa17446da110a5ed153702e3c3.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/SIsilicon.3d.text/label_3d.gd b/addons/SIsilicon.3d.text/label_3d.gd deleted file mode 100644 index 95788b6..0000000 --- a/addons/SIsilicon.3d.text/label_3d.gd +++ /dev/null @@ -1,148 +0,0 @@ -tool -extends Spatial - -export(String, MULTILINE) var text = "Text" setget set_text -export(float) var text_scale = 0.01 setget set_text_scale -export(float) var extrude = 0.0 setget set_extrude -export(Font) var font setget set_font; - -export(int, "Left", "Right", "Center", "Fill") var align setget set_align - -export(Color) var color = Color(0.6, 0.6, 0.6) setget set_color -export(float, 0, 1) var metallic = 0.0 setget set_metallic -export(float, 0, 1) var roughness = 0.5 setget set_roughness - -export(int) var max_steps = 256 setget set_max_steps -export(float) var step_size = 1.0 setget set_step_size - -var label -var viewport -var proxy -var material - -func _ready(): - for i in range(get_child_count()): - remove_child(get_child(0)) - - viewport = preload("text_viewport.tscn").instance() - label = viewport.get_node("Label") - add_child(viewport) - - proxy = MeshInstance.new() - proxy.mesh = CubeMesh.new() - proxy.material_override = preload("label_3d.material").duplicate() - material = proxy.material_override - - var view_texture = viewport.get_texture() - view_texture.flags = Texture.FLAG_FILTER - material.set_shader_param("text", view_texture) - add_child(proxy) - - set_align(align) - set_font(font) - set_text(text) - set_text_scale(text_scale) - set_extrude(extrude) - - set_color(color) - set_metallic(metallic) - set_roughness(roughness) - - set_max_steps(max_steps) - set_step_size(step_size) - - -func set_text(string): - text = string; - if label: - label.text = text - label.rect_size = Vector2() - label.force_update_transform() - - var size = label.rect_size - viewport.size = size - - viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS - yield(get_tree(), "idle_frame") - - label.rect_size = Vector2() - label.force_update_transform() - - size = label.rect_size - viewport.size = size - - yield(get_tree(), "idle_frame") - viewport.render_target_update_mode = Viewport.UPDATE_DISABLED - - proxy.scale.x = size.x * text_scale - proxy.scale.y = size.y * text_scale - -func set_text_scale(scale): - text_scale = scale - if label: - var size = label.rect_size - if proxy: - proxy.scale.x = size.x * text_scale - proxy.scale.y = size.y * text_scale - -func set_extrude(ext): - extrude = ext - - if proxy: - proxy.scale.z = extrude if extrude != 0 else 1 - material.set_shader_param("extrude", extrude != 0) - - if extrude == 0 and proxy.mesh is CubeMesh: - proxy.mesh = QuadMesh.new() - proxy.mesh.size = Vector2(2, 2) - elif proxy.mesh is QuadMesh: - proxy.mesh = CubeMesh.new() - -func set_font(f): - font = f - if label: - if font: - label.add_font_override("font", font) - else: - label.add_font_override("font", preload("default_font.tres")) - set_text(text) - -func set_align(al): - align = al - if label: - match align: - 0: - label.align = Label.ALIGN_LEFT - 1: - label.align = Label.ALIGN_RIGHT - 2: - label.align = Label.ALIGN_CENTER - 3: - label.align = Label.ALIGN_FILL - - set_text(text) - -func set_color(col): - color = col - if material: - material.set_shader_param("albedo", color) - -func set_metallic(metal): - metallic = metal - if material: - material.set_shader_param("metallic", metallic) - -func set_roughness(rough): - roughness = rough - if material: - material.set_shader_param("roughness", roughness) - -func set_max_steps(max_s): - max_steps = max(max_s, 8) - if material: - material.set_shader_param("maxSteps", max_steps) - -func set_step_size(step_s): - step_size = max(step_s, 0) - if material: - material.set_shader_param("stepSize", step_size) diff --git a/addons/SIsilicon.3d.text/label_3d.material b/addons/SIsilicon.3d.text/label_3d.material deleted file mode 100644 index b9c6f1eb79f0d736e42dea17da53df0b6867fe29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1599 zcmV-F2Eh4JQ$s@n000005C8y14FCWq1^@skwJ-f(MhxvR09r>N4nq)eoB#lT5h9RD z7XSbh!7~8Q03bvF7!#SMk(o@5n5CC3Sw6)8<<1+ZR-oW=33LKvvP*W$alYQ>Hn({^ zmHrv~0l)yz0M7vZ;CM6YBlNWx&kuXjBvN^yA7zTf&sh8{Ldj|>jrLCHZ&E7rhZ5R? zKcZ3#<&~lbp)r=Cmr-xoc}4F&SQ+%=X{#xh>fek#F&syt45mQva&d=vbar%^Vx`a#W53|5Xt7_jd((!64&GVr#ij!%1Ytbh5CH|wg703X` ziZBB`Jo+O^wG8ygs{JONxFgbVwIC=p4FqRudvJ7G6QD+CG!3MCz=O`H&kh0_@1|E> z9-Uhr)Ay7LQ=K@+!8`cL31-VQ-f5oU`#Jx}uO+$ZyYjK8uB`Ke2d9!$RTk!5hhtlo zWiVnW>35N4Raqa7)SmoJ*&m!ZkJd@^6Kxi`rd5??ag+>mX-bxM7OyIcLrI}B4BN7d zU#^eJb*ik&Vwui$@af$2T<3Kysj|3=TfFl;TEE;kmL8fn^W@M z4BPy28vSzp9qS$Q-_z)t?@w1oJxTtOWu;i0#WiG`S3HHvOmIj-9Fr29j!7aRQK-8G zT(D3fJ#0v;1x*O|O5z^h=076uBhje1Va6sXX-JPJ5A!Eh})D+bh zLEYV;Q!Y*O&N0vC69SFcwv20j>A+H`Y$OB%iG6!Ij1Uo`1Qe<)PNd`UY)Ckt&Osxb51vh~AC}}L zrC61fl5vc8ozD~U?+9~!R4#6U*Q3nNLgfX^Y!>ev1-74Gu&YjdX&dB1Wd_T0NaLa;zS{@LE-$5b}3xdPGGAqpj;cijo7&(+xppNt1ifq~#c!0D=kb zW`mM>ORJb2j%8t9N`ddCd!=BlhX1}5!^1Ic&|k%BLrf~9-(pbdVMM^vDB~dCw&w||D7)s zhi~JLPsJ3E_h*TXbqYm*L!2sNY9v?Jmi5kQujb5aA6#o21u6%{?^}maqZfj-lQFJm z)|w+KG%R!?w@IPAvbaf&xS&D++Z3jOfaVs|F@Olm?*!ryHHJXNC9aPKUh$E{lp4Ce z%#}_QcoFj-kuR=VL*m1C|0`Ki^-3WQH^3G50D^{r>&mQf6&(6F=)qS9lgN-U)eu2P-Ll)@7>R!& z(_=q{8rD}s{(*{b=`~Mc**SJ|SPSJ$jdTBF-3U+C>jbV1y+w_ovJ!FKAlrT}J6vEA zil`lu1SLLXhvf9846P_y0Rv!xW90yjy+aZC&?jsrcrDbs0QZXkGHi=V!VCAUlHF`z zxqJn_aEUsD{kqI5J_mMS&T3-czFcqt??)=w89oa}KKn#G{bQ-^IM7Bbc3M?n)OV;A xw9c>+6N*$|G(7xZh$2S6q;RNGFl(mFYIZWfI(N{W7b?~AFOR{#025MELqoSO1(pB+ diff --git a/addons/SIsilicon.3d.text/label_3d_converter.gd b/addons/SIsilicon.3d.text/label_3d_converter.gd deleted file mode 100644 index fdbffae..0000000 --- a/addons/SIsilicon.3d.text/label_3d_converter.gd +++ /dev/null @@ -1,461 +0,0 @@ -tool -extends Button - -signal mesh_generated(mesh_inst) - -const Label3D = preload("label_3d.gd") - -var label3d : Label3D - -var image_cache = PoolRealArray() - -# These variables are used to spread the marching_square function -# across multiple frames. -var finished_marching = false -var os_time - -func _on_Button_pressed(): - generate_geometry() - -func generate_geometry(): - $PopupDialog.popup_centered() - var text_scale = label3d.text_scale - var extrude = label3d.extrude - var viewport = label3d.get_node("Viewport") - - viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS - yield(get_tree(), "idle_frame") - yield(get_tree(), "idle_frame") - viewport.render_target_update_mode = Viewport.UPDATE_DISABLED - - var image = viewport.get_texture().get_data() - image.lock() - - var edges = Dictionary() - finished_marching = false - os_time = OS.get_ticks_msec() - do_marching_squares(image, edges) - while not finished_marching: - yield(get_tree(), "idle_frame") - - # Contours are the edges and holes of the text. - # The paths are said edges combined with the holes for easier triangulation. - var contours = [] - collect_contours(edges.duplicate(), contours) - var paths = contours.duplicate(true) - decimate_holes(edges, paths, Rect2(0, 0, image.get_width(), image.get_height())) - - var triangle_data = [] - var vertex_data = [] - for path in paths: - path.points = douglas_peucker(path.points, 0.125) - # Edge data beyond this point will no longer be valid - - var triangles = [] - ear_clipping(path, triangles) - - # points are converted into vector3 here. - for p in path.points.size(): - var point = path.points[p] - point -= Vector2(image.get_width(), image.get_height()) / 2.0 - point *= text_scale * 2.0 - path.points[p] = Vector3(point.x, point.y, extrude) - - ArrayUtils.call_per_element(triangles, "offset_indices", vertex_data.size()) - - ArrayUtils.append_array(vertex_data, path.points) - ArrayUtils.append_array(triangle_data, triangles) - - if extrude != 0: - # Generate the back triangles - var original_vert_size = vertex_data.size() - for v in original_vert_size: - var vertex = vertex_data[v] - vertex_data.append(vertex * Vector3(1, 1, -1)) - - var tri_size = triangle_data.size() - for t in tri_size: - var triangle = triangle_data[t].duplicate() - triangle.offset_indices(original_vert_size) - triangle.reverse_order() - triangle_data.append(triangle) - - # And now the triangles inbetween - for path in paths: - var vertices = path.points.duplicate() - var points_size = path.points.size() - for vert in points_size: - var back_vertex = path.points[vert] * Vector3(1, 1, -1) - vertices.append(back_vertex) - - var triangles = [] - for i in points_size: - var index = i + vertex_data.size() - var a = index - var b = (index + 1) if i != points_size - 1 else vertex_data.size() - var c = index + points_size - var d = ((index + 1) if i != points_size - 1 else vertex_data.size()) + points_size - - triangles.append(Triangle.new(c, b, a)) - triangles.append(Triangle.new(d, b, c)) - - ArrayUtils.append_array(vertex_data, vertices) - ArrayUtils.append_array(triangle_data, triangles) - - var geom = SurfaceTool.new() - geom.clear() - - geom.begin(Mesh.PRIMITIVE_TRIANGLES) - for vert in vertex_data: - geom.add_vertex(vert) - for tri in triangle_data: - geom.add_index(tri.a) - geom.add_index(tri.b) - geom.add_index(tri.c) - geom.generate_normals() - - var material = SpatialMaterial.new() - material.albedo_color = label3d.color - material.metallic = label3d.metallic - material.roughness = label3d.roughness - - var mesh_inst = MeshInstance.new() - mesh_inst.mesh = geom.commit() - mesh_inst.material_override = material - mesh_inst.transform = label3d.transform - mesh_inst.name = label3d.name + "-mesh" - - emit_signal("mesh_generated", mesh_inst) - - image_cache.resize(0) - image.unlock() - $PopupDialog.hide() - - -func do_marching_squares(image, edges): - for y in image.get_height() - 1: - for x in image.get_width() - 1: - var i = Vector2(x, y) - - var p_ul = get_pixel(image, x, y) - var p_ll = get_pixel(image, x, y+1) - var p_ur = get_pixel(image, x+1, y) - var p_lr = get_pixel(image, x+1, y+1) - - var top = inverse_lerp(p_ul, p_ur, 1) - var bottom = inverse_lerp(p_ll, p_lr, 1) - var left = inverse_lerp(p_ul, p_ll, 1) - var right = inverse_lerp(p_ur, p_lr, 1) - - var ul = int(p_ul > 1) * 1 - var ll = int(p_ll > 1) * 2 - var ur = int(p_ur > 1) * 4 - var lr = int(p_lr > 1) * 8 - - var bit = ul | ll | ur | lr - - # Notice: cases 6 and 9 have not been implemented. - match bit: - # Corner Cases - 1: - create_edge(edges, i, x, y + left, x + top, y, Vector2.UP) - 2: - create_edge(edges, i, x + bottom, y + 1, x, y + left, Vector2.LEFT) - 4: - create_edge(edges, i, x + top, y, x + 1, y + right, Vector2.RIGHT) - 8: - create_edge(edges, i, x + 1, y + right, x + bottom, y + 1, Vector2.DOWN) - - # Edge Cases - 3: - create_edge(edges, i, x + bottom, y + 1, x + top, y, Vector2.UP) - 5: - create_edge(edges, i, x, y + left, x + 1, y + right, Vector2.RIGHT) - 10: - create_edge(edges, i, x + 1, y + right, x, y + left, Vector2.LEFT) - 12: - create_edge(edges, i, x + top, y, x + bottom, y + 1, Vector2.DOWN) - - # Inner Corner cases - 14: - create_edge(edges, i, x + top, y, x, y + left, Vector2.LEFT) - 13: - create_edge(edges, i, x, y + left, x + bottom, y + 1, Vector2.DOWN) - 11: - create_edge(edges, i, x + 1, y + right, x + top, y, Vector2.UP) - 7: - create_edge(edges, i, x + bottom, y + 1, x + 1, y + right, Vector2.RIGHT) - - if OS.get_ticks_msec() - os_time > 20: - var max_i = (image.get_width() - 1) * (image.get_height() - 1) - var curr_i = x + (y * image.get_width() - 1) - $PopupDialog/VBoxContainer/ProgressBar.value = float(curr_i) / max_i * 100 - - yield(get_tree(), "idle_frame") - os_time = OS.get_ticks_msec() - - finished_marching = true - -func collect_contours(edges, contours): - - var directions = [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT] - var coord = edges.keys()[0] - - var start_edge = edges[coord] - var contour = Contour.new(start_edge.direction == Vector2.UP || \ - start_edge.direction == Vector2.RIGHT) - contours.append(contour) - - contour.points.append(edges[coord].end) - contour.edges.append(edges[coord]) - var edge = edges[coord] - - edges.erase(coord) - - var loop_started = true - while loop_started or edge != start_edge: - var edge_found = false - - for dir in directions: - if edges.has(coord + dir) and edges[coord + dir].begin == edge.end: - edge_found = true - - coord += dir - contour.points.append(edges[coord].end) - contour.edges.append(edges[coord]) - edge = edges[coord] - edges.erase(coord) - - break - - if not edge_found: - break - - loop_started = false - - if not edges.empty(): - collect_contours(edges, contours) - -func decimate_holes(edges, contours, bounds): - for c in range(contours.size()-1, -1, -1): - var contour = contours[c] - if contour.is_hole: - for i in contour.edges.size(): - var edge = contour.edges[i] - var edge_found = false - - var dir = edge.direction - dir = Vector2(dir.y, -dir.x) - var pos = ((edge.begin + edge.end) / 2.0).floor() - var other_edge - - while not other_edge and bounds.has_point(pos): - pos += dir - if edges.has(pos): - other_edge = edges[pos] - - if other_edge: - - for other_contour in contours: - if other_contour == contour: - continue - - for j in other_contour.edges.size(): - var other_point = other_contour.edges[j] - if other_edge == other_point: - edge_found = true - other_contour.fuse_with(contour, j, i + 1) - break - - if edge_found: - break - - if edge_found: - break - - contours.remove(c) - -func douglas_peucker(points, tolerance): - var farthest = farthest_point(points) - - - # Farthest point not existing must mean the points only made of two, - # and cannot be simplified any further. - if not farthest: - return points - - if farthest.distance < tolerance: - return [points[0], points[-1]] - else: - var left = [] - var right = [] - ArrayUtils.split_array(points, farthest.index, left, right) - - left = douglas_peucker(left, tolerance) - right = douglas_peucker(right, tolerance) - - ArrayUtils.append_array(left, right) - return left - -func ear_clipping(contour, triangles): - var points = ArrayUtils.to_dictionary(contour.points) - - var i = 0 - var counter = 0 - while points.size() > 3: - counter += 1 - if(counter > 4000): - printerr("Hmmm... Infinite loop much?") - break - - var keys = points.keys() - var point_a = points[keys[i-1]] - var point_b = points[keys[i]] - var point_c = points[keys[(i+1) % keys.size()]] - - if (point_b - point_a).cross(point_c - point_b) < 0: - var has_point = false - for p in points: - var point = points[p] - if point == point_a or point == point_b or point == point_c: - continue - if point_inside_triangle(point, point_a, point_b, point_c): - has_point = true - break - - if not has_point: - var tri = Triangle.new(keys[i-1], keys[i], keys[(i+1) % keys.size()]) - triangles.append(tri) - points.erase(keys[i]) - - i = wrapi(i + 1, 0, points.size()) - - var keys = points.keys() - triangles.append(Triangle.new(keys[0], keys[1], keys[2])) - -# This returns a dictionary containing the farthest point, -# the distance associated with it, and its index in the array. -func farthest_point(points): - var first = points[0] - var last = points[-1] - - if points.size() < 3: - return - - var farthest - var max_dist = -1 - var index - for i in range(1, points.size() - 1): - var distance = distance_to_segment(points[i], first, last) - if distance > max_dist: - farthest = points[i] - max_dist = distance - index = i - - return {"point": farthest, "distance": max_dist, "index": index} - -func distance_to_segment(point, a, b): - # This is because I don't know how to snap a point onto a line, - # so I'm relying on Plane in the meantime. - var plane = Plane(Vector3(a.x, a.y, 0), - Vector3(b.x, b.y, 0), Vector3(a.x, a.y, 1)) - - var projected = plane.project(Vector3(point.x, point.y, 0)) - var t = inverse_lerp(a.x, b.x, projected.x) if a.x != b.x else \ - inverse_lerp(a.y, b.y, projected.y) - - var snapped = a.linear_interpolate(b, t) - return point.distance_squared_to(snapped) - -func vec_sign(p1, p2, p3): - return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y) - -func point_inside_triangle(point, a, b, c): - var d1 = vec_sign(point, a, b) - var d2 = vec_sign(point, b, c) - var d3 = vec_sign(point, c, a) - - var has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0) - var has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0) - return not (has_neg and has_pos) - -func create_edge(edges, offset, p1x, p1y, p2x, p2y, dir): - edges[offset] = MSEdge.new(Vector2(p1x, p1y), Vector2(p2x, p2y), dir) - -func get_pixel(image : Image, x : int, y : int): - if x < 1 || y < 1 || x > image.get_width()-1 || y > image.get_height()-1: - return 0 - - var i = x + y * image.get_width() - if image_cache.size() > i and image_cache[i] != null: - return image_cache[i] - else: - var real = image.get_pixel(x, y).r; - if image_cache.size() <= i: - image_cache.resize(i+1) - image_cache[i] = real - return real - - -class MSEdge: - var begin : Vector2 - var end : Vector2 - - var coordinate : Vector2 - - # One of the four cardinal directions - var direction : Vector2 - - func _init(begin, end, direction): - self.begin = begin - self.end = end - self.direction = direction - - -class Contour: - var points := [] - var edges := [] - var is_hole : bool - - func _init(is_hole): - self.is_hole = is_hole - - func fuse_with(contour, self_point, other_point): - var points_behind = [] - var points_after = [] - - ArrayUtils.split_array(points, self_point + 1, points_behind, points_after) - - var shifted_contour = contour.points.duplicate() - ArrayUtils.shift_array(shifted_contour, other_point) - - ArrayUtils.append_array(points_behind, shifted_contour) - points_behind.append(shifted_contour[0]) - points_behind.append(points[self_point]) - ArrayUtils.append_array(points_behind, points_after) - - points = points_behind - - -class Triangle: - var a : int - var b : int - var c : int - - func _init(a, b, c): - self.a = a - self.b = b - self.c = c - - func offset_indices(offset): - a += offset - b += offset - c += offset - - func reverse_order(): - var temp = a - a = b - b = temp - - func duplicate(): - return Triangle.new(a, b, c) diff --git a/addons/SIsilicon.3d.text/label_3d_converter.tscn b/addons/SIsilicon.3d.text/label_3d_converter.tscn deleted file mode 100644 index 1c02ff0..0000000 --- a/addons/SIsilicon.3d.text/label_3d_converter.tscn +++ /dev/null @@ -1,40 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/SIsilicon.3d.text/label_3d_converter.gd" type="Script" id=1] - -[node name="Button" type="Button"] -margin_right = 172.0 -margin_bottom = 20.0 -text = "Convert to MeshInstance" -flat = true -script = ExtResource( 1 ) - -[node name="PopupDialog" type="PopupDialog" parent="."] -visible = true -margin_left = 340.0 -margin_top = 200.0 -margin_right = 610.0 -margin_bottom = 268.0 -mouse_default_cursor_shape = 5 -popup_exclusive = true - -[node name="VBoxContainer" type="VBoxContainer" parent="PopupDialog"] -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = 6.0 -margin_top = 6.0 -margin_right = -6.0 -margin_bottom = -6.0 - -[node name="Label" type="Label" parent="PopupDialog/VBoxContainer"] -margin_right = 258.0 -margin_bottom = 14.0 -text = "Generating geometry" -align = 1 - -[node name="ProgressBar" type="ProgressBar" parent="PopupDialog/VBoxContainer"] -margin_top = 18.0 -margin_right = 258.0 -margin_bottom = 32.0 -step = 1.0 -[connection signal="pressed" from="." to="." method="_on_Button_pressed"] diff --git a/addons/SIsilicon.3d.text/plugin.cfg b/addons/SIsilicon.3d.text/plugin.cfg deleted file mode 100644 index 8c20b54..0000000 --- a/addons/SIsilicon.3d.text/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="3D Text" -description="This plugin allows you to make 3D text within the Godot engine." -author="SIsilicon" -version="0.6.1" -script="3d_text_plugin.gd" \ No newline at end of file diff --git a/addons/SIsilicon.3d.text/text_viewport.tscn b/addons/SIsilicon.3d.text/text_viewport.tscn deleted file mode 100644 index 18e2279..0000000 --- a/addons/SIsilicon.3d.text/text_viewport.tscn +++ /dev/null @@ -1,74 +0,0 @@ -[gd_scene load_steps=7 format=2] - -[ext_resource path="res://addons/SIsilicon.3d.text/default_font.ttf" type="DynamicFontData" id=1] - -[sub_resource type="Environment" id=1] -background_mode = 1 -background_energy = 0.0 - -[sub_resource type="World" id=2] -environment = SubResource( 1 ) - -[sub_resource type="DynamicFont" id=3] -size = 100 -font_data = ExtResource( 1 ) - -[sub_resource type="Shader" id=4] -code = "shader_type canvas_item; - -uniform int Max = 3; - -float getAlpha(sampler2D tex, ivec2 coord) { - return texelFetch(tex, coord, 0).r; -} - -void fragment() { - ivec2 coord = ivec2(FRAGCOORD.xy); - bool inside = getAlpha(SCREEN_TEXTURE, coord) > 0.5; - - float dist = float((Max+1) * (Max+1)); - for(int y = -Max; y <= Max; y++) { - for(int x = -Max; x <= Max; x++) { - if(x == 0 && y == 0) continue; - ivec2 delta = ivec2(x,y); - - if(inside) { - if(getAlpha(SCREEN_TEXTURE, coord + delta) < 0.5) { - dist = min(dist, float(x*x + y*y)); - } - } else { - if(getAlpha(SCREEN_TEXTURE, coord + delta) > 0.5) { - dist = min(dist, float(x*x + y*y)); - } - } - } - } - dist = sqrt(dist) * (float(!inside) * 2.0 - 1.0); - - COLOR = vec4(dist / float(Max+1)); -}" - -[sub_resource type="ShaderMaterial" id=5] -shader = SubResource( 4 ) -shader_param/Max = 10 - -[node name="Viewport" type="Viewport"] -size = Vector2( 190, 114 ) -own_world = true -world = SubResource( 2 ) -transparent_bg = true -handle_input_locally = false -render_target_update_mode = 1 -gui_disable_input = true - -[node name="Label" type="Label" parent="."] -margin_right = 190.0 -margin_bottom = 114.0 -size_flags_horizontal = 4 -custom_fonts/font = SubResource( 3 ) -text = "Text" - -[node name="ColorRect" type="ColorRect" parent="Label"] -material = SubResource( 5 ) -anchor_right = 1.0 -anchor_bottom = 1.0 diff --git a/addons/godot_xterm/import_plugins/asciicast_import_plugin.gd b/addons/godot_xterm/import_plugins/asciicast_import_plugin.gd index 4830c1c..98897fd 100644 --- a/addons/godot_xterm/import_plugins/asciicast_import_plugin.gd +++ b/addons/godot_xterm/import_plugins/asciicast_import_plugin.gd @@ -45,7 +45,13 @@ func import(source_file, save_path, options, r_platform_variant, r_gen_files): asciicast.add_track(Animation.TYPE_METHOD, 0) asciicast.track_set_path(0, ".") - var time: float + var frame = { + "time": 0.0, + "data": { + "method": "write", + "args": [PoolByteArray()] + } + } while not file.eof_reached(): var line = file.get_line() @@ -56,14 +62,25 @@ func import(source_file, save_path, options, r_platform_variant, r_gen_files): if typeof(p.result) != TYPE_ARRAY: continue - time = p.result[0] var event_type: String = p.result[1] var event_data: PoolByteArray = p.result[2].to_utf8() + # Asciicast recordings have a resolution of 0.000001, however animation + # track keys only have a resolution of 0.01, therefore we must combine + # events that would occur in the same keyframe, otherwise only the last + # event is inserted and the previous events are overwritten. + var time = stepify(p.result[0], 0.01) + if event_type == "o": - asciicast.track_insert_key(0, time, {"method": "write", - "args": [event_data]}) + if time == frame.time: + asciicast.track_remove_key_at_position(0, time) + frame.data.args[0] = frame.data.args[0] + event_data + else: + frame.time = time + frame.data.args = [event_data] + + asciicast.track_insert_key(0, frame.time, frame.data) - asciicast.length = time + asciicast.length = frame.time return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], asciicast) diff --git a/addons/godot_xterm/native/src/pseudoterminal.cpp b/addons/godot_xterm/native/src/pseudoterminal.cpp index 029ff31..52b6a57 100644 --- a/addons/godot_xterm/native/src/pseudoterminal.cpp +++ b/addons/godot_xterm/native/src/pseudoterminal.cpp @@ -1,6 +1,7 @@ #include "pseudoterminal.h" #include #include +#include #include using namespace godot; @@ -11,9 +12,11 @@ void Pseudoterminal::_register_methods() register_method("_init", &Pseudoterminal::_init); register_method("_ready", &Pseudoterminal::_ready); - register_method("put_data", &Pseudoterminal::put_data); + register_method("write", &Pseudoterminal::write); + register_method("resize", &Pseudoterminal::resize); - register_signal((char *)"data_received", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal((char *)"data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal((char *)"exited", "status", GODOT_VARIANT_TYPE_INT); } Pseudoterminal::Pseudoterminal() @@ -22,18 +25,20 @@ Pseudoterminal::Pseudoterminal() Pseudoterminal::~Pseudoterminal() { + pty_thread.join(); } void Pseudoterminal::_init() { - pty_thread = std::thread(&Pseudoterminal::process_pty, this); bytes_to_write = 0; + pty_thread = std::thread(&Pseudoterminal::process_pty, this); } void Pseudoterminal::process_pty() { int fd; char *name; + int status; should_process_pty = true; @@ -68,9 +73,30 @@ void Pseudoterminal::process_pty() } else { + Vector2 zero = Vector2(0, 0); + /* Parent */ while (1) { + { + std::lock_guard guard(size_mutex); + if (size != zero) + { + struct winsize ws; + memset(&ws, 0, sizeof(ws)); + ws.ws_col = size.x; + ws.ws_row = size.y; + + ioctl(fd, TIOCSWINSZ, &ws); + } + } + + if (waitpid(pty_pid, &status, WNOHANG)) + { + emit_signal("exited", status); + return; + } + int ready = -1; fd_set read_fds; fd_set write_fds; @@ -93,10 +119,7 @@ void Pseudoterminal::process_pty() if (bytes_to_write > 0) { - write(fd, write_buffer, bytes_to_write); - - Godot::print(String("wrote {0} bytes").format(Array::make(bytes_to_write))); - + ::write(fd, write_buffer, bytes_to_write); bytes_to_write = 0; } } @@ -114,30 +137,11 @@ void Pseudoterminal::process_pty() if (bytes_read <= 0) continue; - //while (1) - //{ - // ret = read(fd, read_buffer, 1); - - // if (ret == -1 || ret == 0) - // { - // break; - // } - // else - // { - // bytes_read += ret; - // } - //} - PoolByteArray data = PoolByteArray(); data.resize(bytes_read); memcpy(data.write().ptr(), read_buffer, bytes_read); - emit_signal("data_received", PoolByteArray(data)); - - if (bytes_read > 0) - { - //Godot::print(String("read {0} bytes").format(Array::make(bytes_read))); - } + emit_signal("data_sent", PoolByteArray(data)); } } } @@ -148,9 +152,15 @@ void Pseudoterminal::_ready() { } -void Pseudoterminal::put_data(PoolByteArray data) +void Pseudoterminal::write(PoolByteArray data) { std::lock_guard guard(write_buffer_mutex); bytes_to_write = data.size(); memcpy(write_buffer, data.read().ptr(), bytes_to_write); +} + +void Pseudoterminal::resize(Vector2 new_size) +{ + std::lock_guard guard(size_mutex); + size = new_size; } \ No newline at end of file diff --git a/addons/godot_xterm/native/src/pseudoterminal.h b/addons/godot_xterm/native/src/pseudoterminal.h index 98bd1a3..dd48cc3 100644 --- a/addons/godot_xterm/native/src/pseudoterminal.h +++ b/addons/godot_xterm/native/src/pseudoterminal.h @@ -29,6 +29,9 @@ namespace godot int bytes_to_read; std::mutex read_buffer_mutex; + Vector2 size; + std::mutex size_mutex; + void process_pty(); public: @@ -40,7 +43,8 @@ namespace godot void _init(); void _ready(); - void put_data(PoolByteArray data); + void write(PoolByteArray data); + void resize(Vector2 size); }; } // namespace godot diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 81e9c54..d690ef5 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -215,7 +215,17 @@ static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data for (int i = 0; i < len; i++) bytes.append(u8[i]); - term->emit_signal("data_read", bytes); + if (len > 0) + { + if (term->input_event_key.is_valid()) + { + // The callback was fired from a key press event so emit the "key_pressed" signal. + term->emit_signal("key_pressed", String(u8), term->input_event_key); + term->input_event_key.unref(); + } + + term->emit_signal("data_sent", bytes); + } } static int text_draw_cb(struct tsm_screen *con, @@ -258,7 +268,6 @@ static int text_draw_cb(struct tsm_screen *con, void Terminal::_register_methods() { - register_method("_init", &Terminal::_init); register_method("_ready", &Terminal::_ready); register_method("_gui_input", &Terminal::_gui_input); @@ -267,10 +276,12 @@ void Terminal::_register_methods() register_method("write", &Terminal::write); register_method("update_size", &Terminal::update_size); - //register_property("rows", &Terminal::rows, 24); - //register_property("cols", &Terminal::cols, 80); + register_property("rows", &Terminal::rows, 24); + register_property("cols", &Terminal::cols, 80); - register_signal("data_read", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal("data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + register_signal("key_pressed", "data", GODOT_VARIANT_TYPE_STRING, "event", GODOT_VARIANT_TYPE_OBJECT); + register_signal("size_changed", "new_size", GODOT_VARIANT_TYPE_VECTOR2); } Terminal::Terminal() @@ -349,6 +360,7 @@ void Terminal::_gui_input(Variant event) auto iter = keymap.find({unicode, scancode}); uint32_t keysym = (iter != keymap.end() ? iter->second : XKB_KEY_NoSymbol); + input_event_key = k; tsm_vte_handle_keyboard(vte, keysym, ascii, mods, unicode ? unicode : TSM_VTE_INVALID); } } @@ -425,6 +437,9 @@ void Terminal::draw_foreground(int row, int col, Color fgcolor) struct cell cell = cells[row][col]; + if (cell.ch == nullptr) + return; // No foreground to draw + /* Set the font */ Ref fontref = get_font(""); @@ -448,9 +463,6 @@ void Terminal::draw_foreground(int row, int col, Color fgcolor) /* Draw the foreground */ - if (cell.ch == nullptr) - return; // No foreground to draw - if (cell.attr.blink) ; // TODO: Handle blink @@ -523,7 +535,7 @@ void Terminal::update_size() rows = std::max(2, (int)floor(get_rect().size.y / cell_size.y)); cols = std::max(1, (int)floor(get_rect().size.x / cell_size.x)); - Godot::print(String("resized_rows: {0}, resized_cols: {1}").format(Array::make(rows, cols))); + emit_signal("size_changed", Vector2(cols, rows)); Cells new_cells = {}; diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index a6dec7e..4bd40c6 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -28,6 +28,8 @@ namespace godot Cells cells; + Ref input_event_key; + protected: tsm_screen *screen; tsm_vte *vte; diff --git a/addons/godot_xterm/nodes/terminal/README.md b/addons/godot_xterm/nodes/terminal/README.md new file mode 100644 index 0000000..f12e3ba --- /dev/null +++ b/addons/godot_xterm/nodes/terminal/README.md @@ -0,0 +1,131 @@ +# Terminal + +**Inherits:** [Control] < [CanvasItem] < [Node] < [Object] + + +Terminal emulator. + +**IMPORTANT:** + + + +- If you are not seeing anything in the terminal check that a theme has been set. If there is no theme, everything will be drawn in black by default. +- If the terminal isn't responding to keyboard or mouse input check that `focus_mode` is set to `All`, otherwise `_gui_input()` won't be called so no input will be processed. + + +## Description + +![Flow Diagram](./docs/flow_diagram.svg) + +### (1) User Input + +The users enters some data into the terminal, typically by typing something on the keyboard or clicking (and possibly dragging) somewhere on the screen. +This corresponds to the `_gui_input()` method which is implemented in [terminal.cpp](../native/src/terminal.cpp). + +### (2) Terminal Output + +The user input from (1) is processed by the terminal and converted. +For example, if the user were to press the down key, the terminal would +and the `data_sent` signal would be emitted with the value `"\u001b[A"`. +For a full list of escape sequences ["XTerm Control Sequences"](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html). + +### (3) Terminal Input + +In the other direction, characters can be sent to the terminal. This corresponds to the `write()` method. + +### (4) Draw + +The input from (3) is then intepreted by the terminal and drawn to the screen. +For example if the string "\u001b[39;m" was written to the terminal, then it would draw some colorful output to the screen. + +## Properties + +| Type | Name | Default | +|-------|------|---------| +| [int] | rows | 24 | +| [int] | cols | 80 | + +## Methods + +## Theme Properties + +| Type | Name | Default | +|---------|-------------------------------|------------------------------| +| [Color] | Terminal/colors/Background | Color(0.0, 0.0, 0.0, 1.0) | +| [Color] | Terminal/colors/Black | Color(0.0, 0.0, 0.0, 1.0) | +| [Color] | Terminal/colors/Blue | Color(0.0, 0.0, 0.5, 1.0) | +| [Color] | Terminal/colors/Cyan | Color(0.0, 0.5, 0.5, 1.0) | +| [Color] | Terminal/colors/Dark Grey | Color(0.5, 0.5, 0.5, 1.0) | +| [Color] | Terminal/colors/Foreground | Color(1.0, 1.0, 1.0, 1.0) | +| [Color] | Terminal/colors/Green | Color(0.0, 0.5, 0.0, 1.0) | +| [Color] | Terminal/colors/Light Blue | Color(0.0, 0.0, 1.0, 1.0) | +| [Color] | Terminal/colors/Light Cyan | Color(0.0, 1.0, 1.0, 1.0) | +| [Color] | Terminal/colors/Light Green | Color(0.0, 1.0, 0.0, 1.0) | +| [Color] | Terminal/colors/Light Grey | Color(0.75, 0.75, 0.75, 1.0) | +| [Color] | Terminal/colors/Light Magenta | Color(1.0, 0.0, 1.0, 1.0) | +| [Color] | Terminal/colors/Light Red | Color(1.0, 0.0, 0.0, 1.0) | +| [Color] | Terminal/colors/Light Yellow | Color(1.0, 1.0, 0.0, 1.0) | +| [Color] | Terminal/colors/Magenta | Color(0.5, 0.0, 0.5, 1.0) | +| [Color] | Terminal/colors/Red | Color(0.5, 0.0, 0.0, 1.0) | +| [Color] | Terminal/colors/White | Color(1.0, 1.0, 1.0, 1.0) | +| [Color] | Terminal/colors/Yellow | Color(0.5, 0.5, 0.0, 1.0) | +| [Font] | Terminal/fonts/Bold | | +| [Font] | Terminal/fonts/Bold Italic | | +| [Font] | Terminal/fonts/Italic | | +| [Font] | Terminal/fonts/Regular | | + +## Signals + +- **data_sent** **(** PoolByteArray data **)** + + Emitted when some data comes out of the terminal. + This typically occurs when the user interacts with the terminal by typing on the keyboard or clicking somewhere. + It might also occur. + Depending on several terminal settings can be interpreted differently, depending on modifier keys and the terminals settings/state. + In a typical setup, this data would be forwarded to the linux operating system. + +--- + +- **key_pressed** **(** int row **)** + + Emitted when a key is pressed. + +## Property Descriptions + +- [int] **rows** + + | | | + |-----------|-----------------| + | *Default* | 24 | + | *Setter* | set_rows(value) | + | *Getter* | get_rows(value) | + + The number of rows in the terminal's rect. + When using a monospace font, this is typically the number of characters that can fit from one side to another. + Therefore it is affected by the things thing. + +--- + +- [int] **cols** +The number of columns in the terminal's rect. + +## Method Descriptions + +- void **write** **(** String|PoolByteArray data **)** + + Writes data to the terminal emulator. Accepts either a String or PoolByteArray. + + Example: + ```gdscript + $Terminal.write("Hello World") + $Terminal.write("Hello World".to_utf8()) + $Terminal.write(PoolByteArray([0x1b, 0x9e]) + ``` + +[Control]: https://docs.godotengine.org/en/stable/classes/class_control.html#class-control +[CanvasItem]: https://docs.godotengine.org/en/stable/classes/class_canvasitem.html#class-canvasitem +[Node]: https://docs.godotengine.org/en/stable/classes/class_node.html#class-node +[Object]: https://docs.godotengine.org/en/stable/classes/class_object.html#class-object +[Color]: https://docs.godotengine.org/en/stable/classes/class_color.html#class-color +[Font]: https://docs.godotengine.org/en/stable/classes/class_font.html#class-font +[int]: https://docs.godotengine.org/en/stable/classes/class_int.html#class-int diff --git a/addons/godot_xterm/nodes/terminal/docs/.gdignore b/addons/godot_xterm/nodes/terminal/docs/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/addons/godot_xterm/nodes/terminal/docs/flow_diagram.svg b/addons/godot_xterm/nodes/terminal/docs/flow_diagram.svg new file mode 100644 index 0000000..3c3e64e --- /dev/null +++ b/addons/godot_xterm/nodes/terminal/docs/flow_diagram.svg @@ -0,0 +1,3777 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (1) + _gui_input() + + (2) + data_sent() + + (4) + _draw() + + (3) + write() + + + Image credits:"computer keyboard 2", from U.S. patent drawing, uploaded by johnny_automatic to https://openclipart.org/detail/2396/computer-keyboard-2, released under CC0."monitor", from U.S. patent drawing, uploaded by johnny_automatic to https://openclipart.org/detail/1637/monitor, released under CC0. + + diff --git a/addons/godot_xterm/nodes/terminal/docs/important_properties.png b/addons/godot_xterm/nodes/terminal/docs/important_properties.png new file mode 100644 index 0000000000000000000000000000000000000000..21ac26119677e96e423603f1e80eb779fe035328 GIT binary patch literal 11163 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 String: func is_class(name) -> bool: return name == get_class() or .is_class(name) + + +func _init(): + step = 0.01 # Parent override. diff --git a/addons/godot_xterm/util/tput.gd b/addons/godot_xterm/util/tput.gd new file mode 100644 index 0000000..e3bb8e5 --- /dev/null +++ b/addons/godot_xterm/util/tput.gd @@ -0,0 +1,66 @@ +extends Reference +class_name TPut + +# Control Sequence Introducer +const CSI = "\u001b[" + +const CURSOR_UP = "\u001b[A" +const CURSOR_DOWN = "\u001b[B" +const CURSOR_RIGHT = "\u001b[C" +const CURSOR_LEFT = "\u001b[D" + +const DEFAULT_FOREGROUND_COLOR = "\u001b[0m" + + +var terminal + + +func _init(p_terminal: Control) -> void: + if p_terminal: + terminal = p_terminal + + +func write_string(string: String, color: Color = Color.white) -> void: + if color: + var fg = "\u001b[38;2;%d;%d;%dm" % [color.r8, color.g8, color.b8] + terminal.write(fg.to_utf8()) + + terminal.write(string.to_utf8()) + + # Reset color back to default. + terminal.write("\u001b[0m".to_utf8()) + +# tput_* functions based on the tput command. +# See: https://man7.org/linux/man-pages/man1/tput.1.html for more info. + + +# Hide the cursor. +func civis(): + terminal.write("%s?25l" % CSI) + + +# Position the cursor at the given row and col. +func cup(row: int = 0, col: int = 0) -> void: + terminal.write("\u001b[%d;%dH" % [row, col]) + + +func setaf(color: Color) -> void: + var fg = "\u001b[38;2;%d;%d;%dm" % [color.r8, color.g8, color.b8] + terminal.write(fg) + + +func setab(color: Color) -> void: + var bg = "\u001b[48;2;%d;%d;%dm" % [color.r8, color.g8, color.b8] + terminal.write(bg) + + +func rev() -> void: + terminal.write("\u001b[7m") + + +func sgr0() -> void: + terminal.write("\u001b[0m") + + +func reset() -> void: + terminal.write("\u001bc") diff --git a/examples/asciicast/asciicast.tscn b/examples/asciicast/asciicast.tscn index f655220..e585608 100644 --- a/examples/asciicast/asciicast.tscn +++ b/examples/asciicast/asciicast.tscn @@ -1,24 +1,23 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=4 format=2] -[ext_resource path="res://examples/asciicast/asciicast.gd" type="Script" id=1] [ext_resource path="res://addons/godot_xterm/themes/default.theme" type="Theme" id=2] [ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=4] [ext_resource path="res://examples/asciicast/example.cast" type="Animation" id=6] -[node name="Container" type="Container"] -margin_right = 40.0 -margin_bottom = 40.0 -script = ExtResource( 1 ) +[node name="Terminal" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +focus_mode = 2 +theme = ExtResource( 2 ) +script = ExtResource( 4 ) __meta__ = { "_edit_use_anchors_": false } +rows = 31 +cols = 102 -[node name="Terminal" type="Control" parent="."] -margin_right = 40.0 -margin_bottom = 40.0 -theme = ExtResource( 2 ) -script = ExtResource( 4 ) - -[node name="AnimationPlayer" type="AnimationPlayer" parent="Terminal"] +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +autoplay = "example" +playback_speed = 2.0 method_call_mode = 1 -anims/a = ExtResource( 6 ) +anims/example = ExtResource( 6 ) diff --git a/examples/asciicast/example.cast b/examples/asciicast/example.cast index afba5c6..3ac1db0 100644 --- a/examples/asciicast/example.cast +++ b/examples/asciicast/example.cast @@ -1,61 +1,779 @@ -{"version": 2, "width": 86, "height": 29, "timestamp": 1589772748, "env": {"SHELL": "/run/current-system/sw/bin/bash", "TERM": "xterm"}} -[0.082961, "o", "> "] -[0.798002, "o", "e"] -[0.893414, "o", "c"] -[0.956255, "o", "h"] -[1.008677, "o", "o"] -[1.089472, "o", " "] -[1.189602, "o", "h"] -[1.266892, "o", "e"] -[1.347483, "o", "l"] -[1.46568, "o", "l"] -[1.541039, "o", "o"] -[1.726772, "o", "\r\n"] -[1.727475, "o", "hello\r\n> "] -[2.060109, "o", "#"] -[2.179668, "o", " "] -[2.471941, "o", "T"] -[2.652735, "o", "h"] -[2.746515, "o", "i"] -[2.810578, "o", "s"] -[2.921342, "o", " "] -[2.98886, "o", "i"] -[3.069095, "o", "s"] -[3.31728, "o", " "] -[3.399615, "o", "a"] -[3.513605, "o", " "] -[3.72609, "o", "d"] -[3.811197, "o", "e"] -[3.94649, "o", "m"] -[4.047162, "o", "o"] -[4.225042, "o", "\r\n"] -[4.225402, "o", "> "] -[4.935288, "o", "t"] -[5.163552, "o", "o"] -[5.323205, "o", "i"] -[5.46746, "o", "l"] -[5.561098, "o", "et "] -[6.064937, "o", "-"] -[6.41563, "o", "-"] -[6.60443, "o", "g"] -[6.666621, "o", "a"] -[6.768317, "o", "y"] -[6.848917, "o", " "] -[7.076406, "o", "H"] -[7.250067, "o", "E"] -[7.410878, "o", "L"] -[7.537016, "o", "L"] -[7.604155, "o", "O"] -[7.888992, "o", " "] -[8.193437, "o", "W"] -[8.365871, "o", "O"] -[8.454678, "o", "R"] -[8.525163, "o", "L"] -[8.60286, "o", "D"] -[8.873053, "o", "!"] -[9.216434, "o", "\r\n"] -[9.251462, "o", " \r\n \u001b[0;1;31;91mm\u001b[0m \u001b[0;1;36;96mm\u001b[0m \u001b[0;1;34;94mmm\u001b[0;1;35;95mmm\u001b[0;1;31;91mmm\u001b[0m \u001b[0;1;33;93mm\u001b[0m \u001b[0;1;35;95mm\u001b[0m \u001b[0;1;36;96mmm\u001b[0;1;34;94mmm\u001b[0m \u001b[0;1;36;96mm\u001b[0m \u001b[0;1;31;91mm\u001b[0m \u001b[0;1;33;93mm\u001b[0;1;32;92mmm\u001b[0;1;36;96mm\u001b[0m \u001b[0;1;34;94mm\u001b[0;1;35;95mmm\u001b[0;1;31;91mmm\u001b[0m \u001b[0;1;32;92mm\u001b[0m \u001b[0;1;35;95mm\u001b[0;1;31;91mmm\u001b[0;1;33;93mm\u001b[0m \r\n \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;31;91m#\u001b[0m \u001b[0;1;36;96mm\u001b[0;1;34;94m\"\u001b[0m \u001b[0;1;35;95m\"\u001b[0;1;31;91mm\u001b[0m \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;32;92mm\"\u001b[0m \u001b[0;1;34;94m\"m\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;33;93m\"\u001b[0;1;32;92m#\u001b[0m \u001b[0;1;36;96m#\u001b[0m \u001b[0;1;31;91m#\u001b[0m \u001b[0;1;32;92m\"\u001b[0;1;36;96mm\u001b[0m\r\n \u001b[0;1;32;92m#\u001b[0;1;36;96mmm\u001b[0;1;34;94mmm\u001b[0;1;35;95m#\u001b[0m \u001b[0;1;31;91m#m\u001b[0;1;33;93mmm\u001b[0;1;32;92mmm\u001b[0m \u001b[0;1;36;96m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;34;94m#\u001b"] -[9.251901, "o", "[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;35;95m\"\u001b[0m \u001b[0;1;31;91m#\"\u001b[0;1;33;93m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;36;96m#\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;31;91m#\u001b[0;1;33;93mmm\u001b[0;1;32;92mmm\u001b[0;1;36;96m\"\u001b[0m \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;34;94m#\u001b[0m\r\n \u001b[0;1;36;96m#\u001b[0m \u001b[0;1;31;91m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;31;91m#\u001b[0;1;33;93m#\u001b[0m \u001b[0;1;32;92m##\u001b[0;1;36;96m\"\u001b[0m \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;31;91m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;36;96m\"\u001b[0;1;34;94mm\u001b[0m \u001b[0;1;35;95m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;35;95m#\u001b[0m\r\n \u001b[0;1;34;94m#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;32;92m#m\u001b[0;1;36;96mmm\u001b[0;1;34;94mmm\u001b[0m \u001b[0;1;35;95m#\u001b[0;1;31;91mmm\u001b[0;1;33;93mmm\u001b[0;1;32;92mm\u001b[0m \u001b[0;1;36;96m#m\u001b[0;1;34;94mmm\u001b[0;1;35;95mmm\u001b[0m \u001b[0;1;33;93m#m\u001b[0;1;32;92mm#\u001b[0m \u001b[0;1;33;93m#\u001b[0m \u001b[0;1;36;96m#\u001b[0m \u001b[0;1;35;95m#\u001b[0;1;31;91mmm\u001b[0;1;33;93m#\u001b[0m \u001b[0;1;32;92m#\u001b[0m \u001b[0;1;35;95m\"\u001b[0m \u001b[0;1;31;91m#m\u001b[0;1;33;93mmm\u001b[0;1"] -[9.251944, "o", ";32;92mmm\u001b[0m \u001b[0;1;36;96m#\u001b[0;1;34;94mmm\u001b[0;1;35;95mm\"\u001b[0m \r\n \r\n \r\n \r\n \u001b[0;1;36;96mm\u001b[0m \r\n \u001b[0;1;34;94m#\u001b[0m \r\n \u001b[0;1;35;95m#\u001b[0m \r\n \u001b[0;1;31;91m\"\u001b[0m \r\n \u001b[0;1;33;93m#\u001b[0m \r\n \r\n \r\n"] -[9.252259, "o", "> "] -[12.56287, "o", "exit\r\n"] +{"version": 2, "width": 95, "height": 56, "timestamp": 1601879053, "env": {"SHELL": "/run/current-system/sw/bin/bash", "TERM": "xterm"}} +[0.009015, "o", "sh-4.4$ "] +[0.768572, "o", "#"] +[0.994511, "o", " "] +[1.251891, "o", "P"] +[1.390056, "o", "r"] +[1.556055, "o", "e"] +[1.633343, "o", "s"] +[1.758128, "o", "s"] +[1.837416, "o", " "] +[2.082763, "o", "E"] +[2.15487, "o", "S"] +[2.206575, "o", "C"] +[2.535156, "o", " "] +[2.675284, "o", "t"] +[2.764107, "o", "o"] +[2.826769, "o", " "] +[2.95468, "o", "e"] +[3.107351, "o", "x"] +[3.236995, "o", "i"] +[3.315722, "o", "t"] +[3.363186, "o", " "] +[3.464495, "o", "t"] +[3.502985, "o", "h"] +[3.561326, "o", "i"] +[3.637971, "o", "s"] +[3.696915, "o", " "] +[3.828476, "o", "d"] +[3.920953, "o", "e"] +[3.977016, "o", "m"] +[4.080181, "o", "o"] +[4.183719, "o", " "] +[4.411889, "o", "a"] +[4.505969, "o", "t"] +[4.602301, "o", " "] +[4.738613, "o", "a"] +[4.815285, "o", "n"] +[4.970011, "o", "y"] +[5.048946, "o", " "] +[5.140987, "o", "t"] +[5.22994, "o", "i"] +[5.300908, "o", "m"] +[5.391697, "o", "e"] +[5.833247, "o", "\r\n"] +[5.833292, "o", "sh-4.4$ "] +[6.497175, "o", "#"] +[6.584674, "o", " "] +[6.783908, "o", "T"] +[6.982053, "o", "h"] +[7.075839, "o", "i"] +[7.121841, "o", "s"] +[7.207194, "o", " "] +[7.472273, "o", "t"] +[7.56542, "o", "e"] +[7.633672, "o", "r"] +[7.708612, "o", "m"] +[7.794029, "o", "i"] +[7.852906, "o", "n"] +[7.960908, "o", "a"] +[8.034148, "o", "l"] +[8.1316, "o", " "] +[8.2592, "o", "s"] +[8.376712, "o", "e"] +[8.453216, "o", "s"] +[8.583006, "o", "s"] +[8.64767, "o", "i"] +[8.711495, "o", "o"] +[8.779941, "o", "n"] +[8.862385, "o", " "] +[9.112564, "o", "w"] +[9.236889, "o", "a"] +[9.29605, "o", "s"] +[9.376424, "o", " "] +[9.672264, "o", "r"] +[9.768395, "o", "e"] +[9.875859, "o", "c"] +[10.360921, "o", "o"] +[10.48178, "o", "r"] +[10.573511, "o", "d"] +[10.70553, "o", "e"] +[10.772014, "o", "d"] +[10.901859, "o", " "] +[11.222266, "o", "u"] +[11.314773, "o", "s"] +[11.388137, "o", "i"] +[11.462257, "o", "n"] +[11.508588, "o", "g"] +[11.610203, "o", " "] +[11.704458, "o", "t"] +[11.769386, "o", "h"] +[11.829674, "o", "e"] +[11.920249, "o", " "] +[12.057327, "o", "a"] +[12.150311, "o", "s"] +[12.221965, "o", "c"] +[12.335971, "o", "i"] +[12.461568, "o", "i"] +[12.659298, "o", "n"] +[12.787306, "o", "e"] +[12.861305, "o", "m"] +[13.243252, "o", "a"] +[13.415869, "o", " "] +[13.640678, "o", "p"] +[13.727956, "o", "r"] +[13.79999, "o", "o"] +[13.869981, "o", "g"] +[13.932686, "o", "r"] +[14.120489, "o", "a"] +[14.563155, "o", "m"] +[14.950597, "o", "\r\n"] +[14.950636, "o", "sh-4.4$ "] +[15.25787, "o", "#"] +[15.357041, "o", " "] +[15.549955, "o", "F"] +[15.69595, "o", "o"] +[15.742517, "o", "r"] +[15.816029, "o", " "] +[16.017346, "o", "m"] +[16.134482, "o", "o"] +[16.214998, "o", "r"] +[16.287911, "o", "e"] +[16.341746, "o", " "] +[16.536357, "o", "i"] +[16.63003, "o", "n"] +[16.757694, "o", "f"] +[16.840006, "o", "o"] +[16.955199, "o", " "] +[17.10089, "o", "v"] +[17.212207, "o", "i"] +[17.310707, "o", "s"] +[17.433414, "o", "i"] +[17.504056, "o", "t"] +[17.639282, "o", " "] +[18.027938, "o", "h"] +[18.148473, "o", "t"] +[18.254587, "o", "t"] +[18.365992, "o", "p"] +[18.422981, "o", "s"] +[18.69077, "o", ":"] +[18.981756, "o", "/"] +[19.072248, "o", "/"] +[19.451925, "o", "a"] +[19.52595, "o", "s"] +[19.590197, "o", "c"] +[19.690475, "o", "i"] +[19.812762, "o", "i"] +[20.002006, "o", "n"] +[20.285781, "o", "e"] +[20.436427, "o", "m"] +[20.528716, "o", "a"] +[20.763135, "o", "."] +[20.903967, "o", "o"] +[20.975119, "o", "r"] +[20.985324, "o", "g"] +[20.99331, "o", "\r\nsh-4.4$ "] +[21.605944, "o", "#"] +[21.807285, "o", " "] +[22.010063, "o", "L"] +[22.188024, "o", "e"] +[22.279903, "o", "t"] +[22.361474, "o", "'"] +[22.486357, "o", "s"] +[22.57465, "o", " "] +[22.740959, "o", "i"] +[22.813009, "o", "n"] +[22.857842, "o", "s"] +[22.954863, "o", "t"] +[23.066967, "o", "a"] +[23.150687, "o", "l"] +[23.263568, "o", "l"] +[23.34495, "o", " "] +[24.24858, "o", "a"] +[24.339416, "o", "s"] +[24.418355, "o", "c"] +[24.541209, "o", "i"] +[24.697872, "o", "i"] +[24.872434, "o", "n"] +[24.964613, "o", "e"] +[25.07858, "o", "m"] +[25.153616, "o", "a"] +[26.123044, "o", "\r\n"] +[26.123082, "o", "sh-4.4$ "] +[26.610637, "o", "n"] +[26.731314, "o", "i"] +[26.840493, "o", "x"] +[26.950531, "o", "-"] +[27.030416, "o", "e"] +[27.143202, "o", "nv "] +[27.380791, "o", "-"] +[27.494532, "o", "i"] +[27.766916, "o", "A"] +[27.947392, "o", " "] +[28.069982, "o", "n"] +[28.153264, "o", "i"] +[28.285577, "o", "x"] +[28.398953, "o", "o"] +[28.528587, "o", "s"] +[28.714452, "o", "."] +[29.066201, "o", "a"] +[29.178501, "o", "s"] +[29.257687, "o", "c"] +[29.36677, "o", "i"] +[29.487456, "o", "i"] +[29.617427, "o", "n"] +[29.708982, "o", "e"] +[29.793394, "o", "m"] +[29.885927, "o", "a"] +[30.166746, "o", "\r\n"] +[30.269591, "o", "installing 'asciinema-2.0.2'\r\n"] +[30.442995, "o", "these paths will be fetched (0.05 MiB download, 0.19 MiB unpacked):\r\n /nix/store/jn1kalsv6g21rzhr46wsmb33rlbnxbp2-asciinema-2.0.2\r\n"] +[30.443793, "o", "copying path '/nix/store/jn1kalsv6g21rzhr46wsmb33rlbnxbp2-asciinema-2.0.2' from 'https://aseipp-nix-cache.global.ssl.fastly.net'...\r\n"] +[30.772997, "o", "building '/nix/store/j8zk1ixrzz8ffl2r8bfcblrbp8jib14n-user-environment.drv'...\r\n"] +[30.827776, "o", "created 2238 symlinks in user environment\r\n"] +[30.837521, "o", "\r\nsh-4.4$ "] +[31.439756, "o", "#"] +[31.531759, "o", " "] +[31.733672, "o", "N"] +[31.885784, "o", "o"] +[31.969473, "o", "w"] +[32.04399, "o", " "] +[32.184525, "o", "t"] +[32.498387, "o", "\b\u001b[K"] +[32.674218, "o", "w"] +[32.780078, "o", "e"] +[32.869318, "o", " "] +[33.015693, "o", "c"] +[33.131168, "o", "a"] +[33.215492, "o", "n"] +[33.277638, "o", " "] +[33.435631, "o", "r"] +[33.491613, "o", "e"] +[33.769703, "o", "c"] +[33.878877, "o", "o"] +[33.964167, "o", "r"] +[34.051787, "o", "d"] +[34.231502, "o", " "] +[34.38911, "o", "a"] +[34.517173, "o", " "] +[34.660129, "o", "n"] +[34.75175, "o", "e"] +[34.83474, "o", "w"] +[34.908203, "o", " "] +[36.304364, "o", "s"] +[36.387211, "o", "e"] +[36.465211, "o", "s"] +[36.596101, "o", "s"] +[36.695781, "o", "i"] +[36.762876, "o", "o"] +[36.824392, "o", "n"] +[36.922221, "o", " "] +[37.108339, "o", "t"] +[37.264521, "o", "o"] +[37.361574, "o", " "] +[37.490987, "o", "a"] +[37.578879, "o", " "] +[38.018804, "o", "."] +[38.110959, "o", "c"] +[38.210231, "o", "a"] +[38.270978, "o", "s"] +[38.3198, "o", "t"] +[38.402376, "o", " "] +[38.566701, "o", "f"] +[38.660991, "o", "i"] +[38.73289, "o", "l"] +[38.813984, "o", "e"] +[39.342188, "o", "\r\n"] +[39.342365, "o", "sh-4.4$ "] +[39.885874, "o", "a"] +[39.959473, "o", "s"] +[40.034948, "o", "c"] +[40.133815, "o", "i"] +[40.215169, "o", "inema "] +[40.590943, "o", "r"] +[40.672976, "o", "e"] +[40.752245, "o", "c"] +[40.83314, "o", " "] +[41.861795, "o", "n"] +[41.938138, "o", "e"] +[42.032994, "o", "w"] +[42.144281, "o", "."] +[42.250499, "o", "c"] +[42.323403, "o", "a"] +[42.407712, "o", "s"] +[42.470832, "o", "t"] +[42.814799, "o", "\r\n"] +[42.90735, "o", "\u001b[0;32masciinema: recording asciicast to new.cast\u001b[0m\r\n\u001b[0;32masciinema: press or type \"exit\" when you're done\u001b[0m\r\n"] +[42.951473, "o", "\u001b]2;laptop:leroy:~/projects/godot-xterm\u0007\r\r\n\u001b[1;32m[\u001b]0;leroy@laptop: ~/projects/godot-xterm\u0007leroy@laptop:~/projects/godot-xterm]$\u001b[0m "] +[45.647452, "o", "l"] +[45.943288, "o", "s"] +[46.286881, "o", "\r\n"] +[46.288383, "o", "\u001b[0m\u001b[01;34maddons\u001b[0m docker-compose.yml example.cast \u001b[01;35micon.png\u001b[0m new.cast\r\nCHANGELOG.md \u001b[01;34mdockerfiles\u001b[0m example.cast.import icon.png.import project.godot\r\ndefault_env.tres \u001b[01;34mdocs\u001b[0m \u001b[01;34mexamples\u001b[0m LICENSE README.md\r\n"] +[46.288974, "o", "\u001b]2;laptop:leroy:~/projects/godot-xterm\u0007\r\r\n\u001b[1;32m[\u001b]0;leroy@laptop: ~/projects/godot-xterm\u0007leroy@laptop:~/projects/godot-xterm]$\u001b[0m "] +[48.264058, "o", "s"] +[48.35698, "o", "h"] +[48.465218, "o", "a"] +[48.860943, "o", "1"] +[49.137467, "o", "s"] +[49.255023, "o", "u"] +[49.402646, "o", "m"] +[49.994215, "o", " "] +[50.137741, "o", "."] +[50.251377, "o", "/"] +[50.478669, "o", "*"] +[51.035519, "o", " "] +[51.587166, "o", "|"] +[51.713609, "o", " "] +[51.863023, "o", "l"] +[51.96232, "o", "o"] +[52.012303, "o", "l"] +[52.240101, "o", "c"] +[52.353223, "o", "a"] +[52.447717, "o", "t"] +[52.565175, "o", " "] +[52.947413, "o", "-"] +[53.353261, "o", "F"] +[53.634436, "o", " "] +[53.936746, "o", "0"] +[54.099132, "o", "."] +[54.364446, "o", "3"] +[54.66516, "o", "\r\n"] +[54.666593, "o", "sha1sum: ./addons: Is a directory\r\n"] +[54.66663, "o", "sha1sum: ./dockerfiles: Is a directory\r\nsha1sum: ./docs: Is a directory\r\nsha1sum: ./examples: Is a directory\r\n"] +[54.819505, "o", "\u001b[38;2;180;1;202mb\u001b[39m\u001b[38;2;168;3;211m2\u001b[39m\u001b[38;2;156;6;221mf\u001b[39m"] +[54.819607, "o", "\u001b[38;2;143;10;229m7\u001b[39m\u001b[38;2;131;16;236m9\u001b[39m\u001b[38;2;118;23;242me\u001b[39m"] +[54.819638, "o", "\u001b[38;2;105;30;247m8\u001b[39m\u001b[38;2;93;39;251mb\u001b[39m\u001b[38;2;81;48;253m2\u001b[39m\u001b[38;2;69;59;254mc\u001b[39m\u001b[38;2;58;70;254m2\u001b[39m"] +[54.819747, "o", "\u001b[38;2;48;81;253m9\u001b[39m\u001b[38;2;39;93;251m1\u001b[39m"] +[54.819806, "o", "\u001b[38;2;30;106;247ma\u001b[39m\u001b[38;2;22;118;242m7\u001b[39m"] +[54.819936, "o", "\u001b[38;2;16;131;236m9\u001b[39m\u001b[38;2;10;144;228m2\u001b[39m\u001b[38;2;6;156;220m7\u001b[39m\u001b[38;2;3;168;211m4\u001b[39m\u001b[38;2;1;180;201m3\u001b[39m"] +[54.820001, "o", "\u001b[38;2;1;191;191m2\u001b[39m\u001b[38;2;1;202;179m2\u001b[39m"] +[54.820056, "o", "\u001b[38;2;3;212;167m2\u001b[39m\u001b[38;2;6;221;155md\u001b[39m\u001b[38;2;11;229;143m8"] +[54.82013, "o", "\u001b[39m\u001b[38;2;16;236;130md\u001b[39m\u001b[38;2;23;242;117m4\u001b[39m\u001b[38;2;31;247;105m7\u001b[39m"] +[54.820187, "o", "\u001b[38;2;39;251;92me\u001b[39m\u001b[38;2;49;253;80mc\u001b[39m"] +[54.820245, "o", "\u001b[38;2;59;254;69m2\u001b[39m\u001b[38;2;70;254;58m5\u001b[39m"] +[54.820314, "o", "\u001b[38;2;82;253;48me\u001b[39m\u001b[38;2;94;250;38m8\u001b[39m\u001b[38;2;106;246;30m1"] +[54.820376, "o", "\u001b[39m\u001b[38;2;119;241;22m3\u001b[39m\u001b[38;2;132;235;15ma\u001b[39m"] +[54.820439, "o", "\u001b[38;2;144;228;10m5\u001b[39m\u001b[38;2;157;220;6m2\u001b[39m\u001b[38;2;169;211;3m3\u001b[39m"] +[54.820504, "o", "\u001b[38;2;181;201;1m \u001b[39m\u001b[38;2;192;190;1m \u001b[39m"] +[54.820559, "o", "\u001b[38;2;203;179;1m.\u001b[39m\u001b[38;2;213;167;3m/\u001b[39m"] +[54.82062, "o", "\u001b[38;2;222;154;7mC\u001b[39m\u001b[38;2;230;142;11mH\u001b[39m\u001b[38;2;237;129;17mA\u001b[39m"] +[54.820678, "o", "\u001b[38;2;243;117;23mN\u001b[39m\u001b[38;2;247;104;31mG\u001b[39m"] +[54.820738, "o", "\u001b[38;2;251;92;40mE\u001b[39m\u001b[38;2;253;80;50mL\u001b[39m"] +[54.820795, "o", "\u001b[38;2;254;68;60mO\u001b[39m\u001b[38;2;254;57;71mG\u001b[39m\u001b[38;2;253;47;83m.\u001b[39m"] +[54.820855, "o", "\u001b[38;2;250;38;95mm\u001b[39m\u001b[38;2;246;29;107md\u001b[39m"] +[54.820926, "o", "\u001b[38;2;241;22;120m\u001b[39m\r\n"] +[54.820985, "o", "\u001b[38;2;143;10;229m8\u001b[39m"] +[54.821038, "o", "\u001b[38;2;131;16;236m0\u001b[39m\u001b[38;2;118;23;242m4\u001b[39m\u001b[38;2;105;30;247m2\u001b[39m"] +[54.821094, "o", "\u001b[38;2;93;39;251m5\u001b[39m\u001b[38;2;81;48;253m3\u001b[39m"] +[54.821149, "o", "\u001b[38;2;69;59;254mf\u001b[39m\u001b[38;2;58;70;254m5\u001b[39m"] +[54.821206, "o", "\u001b[38;2;48;81;253m2\u001b[39m\u001b[38;2;39;93;251m6\u001b[39m"] +[54.821258, "o", "\u001b[38;2;30;106;247m6\u001b[39m\u001b[38;2;22;118;242m5\u001b[39m"] +[54.821311, "o", "\u001b[38;2;16;131;236mc\u001b[39m"] +[54.821381, "o", "\u001b[38;2;10;144;228m6\u001b[39m"] +[54.821436, "o", "\u001b[38;2;6;156;220m8\u001b[39m\u001b[38;2;3;168;211m7\u001b[39m\u001b[38;2;1;180;201md\u001b[39m"] +[54.821486, "o", "\u001b[38;2;1;191;191m6\u001b[39m\u001b[38;2;1;202;179m6\u001b[39m\u001b[38;2;3;212;167mb\u001b[39m"] +[54.821577, "o", "\u001b[38;2;6;221;155m4\u001b[39m\u001b[38;2;11;229;143ma\u001b[39m\u001b[38;2;16;236;130m2\u001b[39m\u001b[38;2;23;242;117m4\u001b[39m\u001b[38;2;31;247;105mb\u001b[39m"] +[54.821633, "o", "\u001b[38;2;39;251;92m3\u001b[39m\u001b[38;2;49;253;80m9\u001b[39m"] +[54.82166, "o", "\u001b[38;2;59;254;69mf\u001b[39m"] +[54.821741, "o", "\u001b[38;2;70;254;58m1\u001b[39m\u001b[38;2;82;253;48mc\u001b[39m\u001b[38;2;94;250;38m3\u001b[39m"] +[54.821906, "o", "\u001b[38;2;106;246;30m2\u001b[39m\u001b[38;2;119;241;22m8\u001b[39m\u001b[38;2;132;235;15m6\u001b[39m\u001b[38;2;144;228;10m9\u001b[39m\u001b[38;2;157;220;6m6\u001b[39m"] +[54.822031, "o", "\u001b[38;2;169;211;3m7\u001b[39m"] +[54.822114, "o", "\u001b[38;2;181;201;1mc\u001b[39m"] +[54.822971, "o", "\u001b[38;2;192;190;1m9\u001b[39m"] +[54.82305, "o", "\u001b[38;2;203;179;1md\u001b[39m\u001b[38;2;213;167;3m \u001b[39m\u001b[38;2;222;154;7m \u001b[39m\u001b[38;2;230;142;11m.\u001b[39m"] +[54.823129, "o", "\u001b[38;2;237;129;17m/\u001b[39m\u001b[38;2;243;117;23md\u001b[39m"] +[54.823188, "o", "\u001b[38;2;247;104;31me\u001b[39m"] +[54.823241, "o", "\u001b[38;2;251;92;40mf\u001b[39m"] +[54.823306, "o", "\u001b[38;2;253;80;50ma\u001b[39m\u001b[38;2;254;68;60mu\u001b[39m"] +[54.823368, "o", "\u001b[38;2;254;57;71ml\u001b[39m"] +[54.823436, "o", "\u001b[38;2;253;47;83mt\u001b[39m\u001b[38;2;250;38;95m_\u001b[39m"] +[54.823503, "o", "\u001b[38;2;246;29;107me\u001b[39m\u001b[38;2;241;22;120mn\u001b[39m"] +[54.823562, "o", "\u001b[38;2;235;15;132mv\u001b[39m\u001b[38;2;228;10;145m.\u001b[39m"] +[54.823622, "o", "\u001b[38;2;219;6;158mt\u001b[39m"] +[54.82368, "o", "\u001b[38;2;210;3;170mr\u001b[39m\u001b[38;2;200;1;182me\u001b[39m"] +[54.82374, "o", "\u001b[38;2;189;1;193ms\u001b[39m\u001b[38;2;178;1;203m\u001b[39m\r\n"] +[54.823865, "o", "\u001b[38;2;105;30;247ma\u001b[39m"] +[54.823926, "o", "\u001b[38;2;93;39;251ma\u001b[39m\u001b[38;2;81;48;253ma\u001b[39m\u001b[38;2;69;59;254m3\u001b[39m"] +[54.823989, "o", "\u001b[38;2;58;70;254md\u001b[39m\u001b[38;2;48;81;253ma\u001b[39m"] +[54.824055, "o", "\u001b[38;2;39;93;251me\u001b[39m\u001b[38;2;30;106;247m7\u001b[39m"] +[54.824148, "o", "\u001b[38;2;22;118;242ma\u001b[39m\u001b[38;2;16;131;236m2\u001b[39m"] +[54.824208, "o", "\u001b[38;2;10;144;228m9\u001b[39m\u001b[38;2;6;156;220m8\u001b[39m"] +[54.824275, "o", "\u001b[38;2;3;168;211me\u001b[39m\u001b[38;2;1;180;201m9\u001b[39m"] +[54.824347, "o", "\u001b[38;2;1;191;191m5\u001b[39m\u001b[38;2;1;202;179m8\u001b[39m"] +[54.824414, "o", "\u001b[38;2;3;212;167m4\u001b[39m\u001b[38;2;6;221;155m1\u001b[39m"] +[54.824469, "o", "\u001b[38;2;11;229;143md\u001b[39m\u001b[38;2;16;236;130m8\u001b[39m"] +[54.824557, "o", "\u001b[38;2;23;242;117m4\u001b[39m\u001b[38;2;31;247;105m7\u001b[39m\u001b[38;2;39;251;92m5\u001b[39m"] +[54.824618, "o", "\u001b[38;2;49;253;80ma\u001b[39m\u001b[38;2;59;254;69mc\u001b[39m"] +[54.824683, "o", "\u001b[38;2;70;254;58m2\u001b[39m\u001b[38;2;82;253;48m3\u001b[39m"] +[54.824738, "o", "\u001b[38;2;94;250;38mb\u001b[39m"] +[54.824796, "o", "\u001b[38;2;106;246;30m8\u001b[39m\u001b[38;2;119;241;22ma\u001b[39m"] +[54.824852, "o", "\u001b[38;2;132;235;15me\u001b[39m\u001b[38;2;144;228;10mc\u001b[39m"] +[54.82491, "o", "\u001b[38;2;157;220;6m7\u001b[39m\u001b[38;2;169;211;3ma"] +[54.825026, "o", "\u001b[39m\u001b[38;2;181;201;1mb\u001b[39m\u001b[38;2;192;190;1mb\u001b[39m\u001b[38;2;203;179;1mb\u001b[39m"] +[54.825088, "o", "\u001b[38;2;213;167;3ma\u001b[39m\u001b[38;2;222;154;7ma\u001b[39m"] +[54.825155, "o", "\u001b[38;2;230;142;11m2\u001b[39m\u001b[38;2;237;129;17m \u001b[39m\u001b[38;2;243;117;23m \u001b[39m"] +[54.825218, "o", "\u001b[38;2;247;104;31m.\u001b[39m\u001b[38;2;251;92;40m/\u001b[39m"] +[54.825294, "o", "\u001b[38;2;253;80;50md\u001b[39m"] +[54.825351, "o", "\u001b[38;2;254;68;60mo\u001b[39m\u001b[38;2;254;57;71mc\u001b[39m"] +[54.825374, "o", "\u001b[38;2;253;47;83mk\u001b[39m"] +[54.825398, "o", "\u001b[38;2;250;38;95me\u001b[39m"] +[54.825502, "o", "\u001b[38;2;246;29;107mr\u001b[39m\u001b[38;2;241;22;120m-\u001b[39m\u001b[38;2;235;15;132mc\u001b[39m"] +[54.82557, "o", "\u001b[38;2;228;10;145mo\u001b[39m\u001b[38;2;219;6;158mm\u001b[39m"] +[54.825634, "o", "\u001b[38;2;210;3;170mp\u001b[39m\u001b[38;2;200;1;182mo\u001b[39m"] +[54.825699, "o", "\u001b[38;2;189;1;193ms\u001b[39m\u001b[38;2;178;1;203me\u001b[39m"] +[54.825832, "o", "\u001b[38;2;166;3;213m.\u001b[39m"] +[54.825889, "o", "\u001b[38;2;154;7;222my\u001b[39m"] +[54.825917, "o", "\u001b[38;2;141;11;230mm\u001b[39m"] +[54.825938, "o", "\u001b[38;2;129;17;237ml\u001b[39m"] +[54.826017, "o", "\u001b[38;2;116;24;243m\u001b[39m\r\n"] +[54.826105, "o", "\u001b[38;2;69;59;254m2\u001b[39m\u001b[38;2;58;70;254m8\u001b[39m"] +[54.826172, "o", "\u001b[38;2;48;81;253m8\u001b[39m\u001b[38;2;39;93;251mb\u001b[39m"] +[54.826245, "o", "\u001b[38;2;30;106;247m0\u001b[39m\u001b[38;2;22;118;242me\u001b[39m"] +[54.826317, "o", "\u001b[38;2;16;131;236m"] +[54.826382, "o", "4\u001b[39m\u001b[38;2;10;144;228md\u001b[39m"] +[54.826442, "o", "\u001b[38;2;6;156;220mb\u001b[39m"] +[54.826501, "o", "\u001b[38;2;3;168;211m3\u001b[39m\u001b[38;2;1;180;201m2\u001b[39m\u001b[38;2;1;191;191m1\u001b[39m"] +[54.826559, "o", "\u001b[38;2;1;202;179mb\u001b[39m"] +[54.826618, "o", "\u001b[38;2;3;212;167m8\u001b[39m"] +[54.826676, "o", "\u001b[38;2;6;221;155m3\u001b[39m\u001b[38;2;11;229;143mb\u001b[39m"] +[54.826734, "o", "\u001b[38;2;16;236;130m0\u001b[39m\u001b[38;2;23;242;117m1\u001b[39m\u001b[38;2;31;247;105m2"] +[54.826792, "o", "\u001b[39m\u001b[38;2;39;251;92m6"] +[54.826849, "o", "\u001b[39m\u001b[38;2;49;253;80m3\u001b[39m\u001b[38;2;59;254;69m0\u001b[39m"] +[54.826908, "o", "\u001b[38;2;70;254;58md\u001b[39m"] +[54.826965, "o", "\u001b[38;2;82;253;48m1\u001b[39m\u001b[38;2;94;250;38md\u001b[39m"] +[54.827023, "o", "\u001b[38;2;106;246;30md\u001b[39m\u001b[38;2;119;241;22m3\u001b[39m"] +[54.827081, "o", "\u001b[38;2;132;235;15m7\u001b[39m"] +[54.82714, "o", "\u001b[38;2;144;228;10mb\u001b[39m\u001b[38;2;157;220;6me\u001b[39m"] +[54.827212, "o", "\u001b[38;2;169;211;3m2\u001b[39m"] +[54.827282, "o", "\u001b[38;2;181;201;1m3\u001b[39m"] +[54.827347, "o", "\u001b[38;2;192;190;1m9\u001b[39m\u001b[38;2;203;179;1m9\u001b[39m\u001b[38;2;213;167;3ma\u001b[39m"] +[54.827406, "o", "\u001b[38;2;222;154;7m8\u001b[39m"] +[54.827466, "o", "\u001b[38;2;230;142;11m9\u001b[39m\u001b[38;2;237;129;17m6\u001b[39m"] +[54.827525, "o", "\u001b[38;2;243;117;23m4\u001b[39m\u001b[38;2;247;104;31m7\u001b[39m"] +[54.827584, "o", "\u001b[38;2;251;92;40m \u001b[39m"] +[54.827643, "o", "\u001b[38;2;253;80;50m \u001b[39m"] +[54.827701, "o", "\u001b[38;2;254;68;60m.\u001b[39m\u001b[38;2;254;57;71m/\u001b[39m"] +[54.827762, "o", "\u001b[38;2;253;47;83me\u001b[39m\u001b[38;2;250;38;95mx\u001b[39m"] +[54.827821, "o", "\u001b[38;2;246;29;107ma\u001b[39m\u001b[38;2;241;22;120mm"] +[54.827881, "o", "\u001b[39m\u001b[38;2;235;15;132mp\u001b[39m\u001b[38;2;228;10;145m"] +[54.82794, "o", "l\u001b[39m\u001b[38;2;219;6;158m"] +[54.827998, "o", "e\u001b[39m\u001b[38;2;210;3;170m.\u001b[39m\u001b[38;2;200;1;182mc"] +[54.828058, "o", "\u001b[39m\u001b[38;2;189;1;193ma\u001b[39m"] +[54.828118, "o", "\u001b[38;2;178;1;203ms\u001b[39m"] +[54.82864, "o", "\u001b[38;2;166;3;213mt\u001b[39m"] +[54.828711, "o", "\u001b[38;2;154;7;222m\u001b[39m\r\n"] +[54.828773, "o", "\u001b[38;2;39;93;251m5\u001b[39m"] +[54.828835, "o", "\u001b[38;2;30;106;247md\u001b[39m\u001b[38;2;22;118;242m5"] +[54.828897, "o", "\u001b[39m\u001b[38;2;16;131;236m1\u001b[39m\u001b[38;2;10;144;228md\u001b[39m"] +[54.828954, "o", "\u001b[38;2;6;156;220m3\u001b[39m"] +[54.829012, "o", "\u001b[38;2;3;168;211m9\u001b[39m"] +[54.829075, "o", "\u001b[38;2;1;180;201m8\u001b[39m\u001b[38;2;1;191;191mf\u001b[39m"] +[54.829137, "o", "\u001b[38;2;1;202;179m4\u001b[39m"] +[54.829213, "o", "\u001b[38;2;3;212;167m7\u001b[39m\u001b[38;2;6;221;155m1\u001b[39m"] +[54.829279, "o", "\u001b[38;2;11;229;143md\u001b[39m"] +[54.829412, "o", "\u001b[38;2;16;236;130m8\u001b[39m"] +[54.829498, "o", "\u001b[38;2;23;242;117ma\u001b[39m"] +[54.829565, "o", "\u001b[38;2;31;247;105ma\u001b[39m"] +[54.829626, "o", "\u001b[38;2;39;251;92me\u001b[39m"] +[54.829695, "o", "\u001b[38;2;49;253;80m2\u001b[39m\u001b[38;2;59;254;69m7\u001b[39m"] +[54.829729, "o", "\u001b[38;2;70;254;58mf\u001b[39m\u001b[38;2;82;253;48m0\u001b[39m"] +[54.829814, "o", "\u001b[38;2;94;250;38m0\u001b[39m"] +[54.829875, "o", "\u001b[38;2;106;246;30m8\u001b[39m\u001b[38;2;119;241;22mf\u001b[39m\u001b[38;2;132;235;15mf\u001b[39m"] +[54.829931, "o", "\u001b[38;2;144;228;10m9\u001b[39m\u001b[38;2;157;220;6m3\u001b[39m"] +[54.829981, "o", "\u001b[38;2;169;211;3m8\u001b[39m"] +[54.830032, "o", "\u001b[38;2;181;201;1m1\u001b[39m"] +[54.830082, "o", "\u001b[38;2;192;190;1m8\u001b[39m\u001b[38;2;203;179;1md\u001b[39m"] +[54.830141, "o", "\u001b[38;2;213;167;3m2\u001b[39m\u001b[38;2;222;154;7mc"] +[54.830201, "o", "\u001b[39m\u001b[38;2;230;142;11m4\u001b[39m\u001b[38;2;237;129;17m5\u001b[39m"] +[54.830254, "o", "\u001b[38;2;243;117;23m6\u001b[39m"] +[54.830316, "o", "\u001b[38;2;247;104;31m5\u001b[39m\u001b[38;2;251;92;40md\u001b[39m"] +[54.830371, "o", "\u001b[38;2;253;80;50m6\u001b[39m"] +[54.830401, "o", "\u001b[38;2;254;68;60m9\u001b[39m"] +[54.83049, "o", "\u001b[38;2;254;57;71m \u001b[39m\u001b[38;2;253;47;83m \u001b[39m\u001b[38;2;250;38;95m."] +[54.830522, "o", "\u001b[39m"] +[54.830611, "o", "\u001b[38;2;246;29;107m/\u001b[39m\u001b[38;2;241;22;120me\u001b[39m\u001b[38;2;235;15;132mx"] +[54.830665, "o", "\u001b[39m\u001b[38;2;228;10;145ma\u001b[39m"] +[54.830719, "o", "\u001b[38;2;219;6;158mm\u001b[39m"] +[54.830767, "o", "\u001b[38;2;210;3;170mp\u001b[39m"] +[54.830816, "o", "\u001b[38;2;200;1;182ml\u001b[39m\u001b[38;2;189;1;193me"] +[54.830869, "o", "\u001b[39m\u001b[38;2;178;1;203m.\u001b[39m\u001b[38;2;166;3;213mc\u001b[39m"] +[54.830919, "o", "\u001b[38;2;154;7;222ma\u001b[39m"] +[54.830972, "o", "\u001b[38;2;141;11;230ms\u001b[39m"] +[54.831023, "o", "\u001b[38;2;129;17;237mt\u001b[39m\u001b[38;2;116;24;243m.\u001b[39m"] +[54.831079, "o", "\u001b[38;2;103;32;248mi\u001b[39m"] +[54.831107, "o", "\u001b[38;2;91;40;251mm\u001b[39m"] +[54.831169, "o", "\u001b[38;2;79;50;253mp\u001b[39m\u001b[38;2;68;61;254m"] +[54.831239, "o", "o\u001b[39m\u001b[38;2;57;72;254mr\u001b[39m\u001b[38;2;46;83;253mt\u001b[39m"] +[54.831306, "o", "\u001b[38;2;37;95;250m\u001b[39m\r\n"] +[54.831393, "o", "\u001b[38;2;16;131;236mf\u001b[39m"] +[54.83146, "o", "\u001b[38;2;10;144;228m5\u001b[39m"] +[54.831523, "o", "\u001b[38;2;6;156;220m7\u001b[39m\u001b[38;2;3;168;211ma\u001b[39m"] +[54.831583, "o", "\u001b[38;2;1;180;201m3\u001b[39m\u001b[38;2;1;191;191m5\u001b[39m"] +[54.831645, "o", "\u001b[38;2;1;202;179me\u001b[39m"] +[54.831696, "o", "\u001b[38;2;3;212;167m0\u001b[39m\u001b[38;2;6;221;155md\u001b[39m"] +[54.831747, "o", "\u001b[38;2;11;229;143ma\u001b[39m"] +[54.831797, "o", "\u001b[38;2;16;236;130m6\u001b[39m"] +[54.831848, "o", "\u001b[38;2;23;242;117m2\u001b[39m\u001b[38;2;31;247;105m5\u001b[39m"] +[54.831898, "o", "\u001b[38;2;39;251;92m9\u001b[39m"] +[54.831948, "o", "\u001b[38;2;49;253;80m2\u001b[39m"] +[54.831999, "o", "\u001b[38;2;59;254;69md\u001b[39m\u001b[38;2;70;254;58me\u001b[39m"] +[54.832049, "o", "\u001b[38;2;82;253;48m5\u001b[39m"] +[54.8321, "o", "\u001b[38;2;94;250;38ma\u001b[39m"] +[54.832149, "o", "\u001b[38;2;106;246;30m0\u001b[39m"] +[54.8322, "o", "\u001b[38;2;119;241;22m7\u001b[39m\u001b[38;2;132;235;15m6\u001b[39m\u001b[38;2;144;228;10ma\u001b[39m"] +[54.832265, "o", "\u001b[38;2;157;220;6m5\u001b[39m"] +[54.832324, "o", "\u001b[38;2;169;211;3m8\u001b[39m"] +[54.832371, "o", "\u001b[38;2;181;201;1m1\u001b[39m"] +[54.832421, "o", "\u001b[38;2;192;190;1m1\u001b[39m\u001b[38;2;203;179;1m9\u001b[39m"] +[54.832469, "o", "\u001b[38;2;213;167;3m0\u001b[39m"] +[54.832518, "o", "\u001b[38;2;222;154;7m4\u001b[39m"] +[54.832565, "o", "\u001b[38;2;230;142;11m5\u001b[39m\u001b[38;2;237;129;17md\u001b[39m"] +[54.832614, "o", "\u001b[38;2;243;117;23m2\u001b[39m"] +[54.832665, "o", "\u001b[38;2;247;104;31m4\u001b[39m\u001b[38;2;251;92;40m9\u001b[39m"] +[54.832733, "o", "\u001b[38;2;253;80;50m0\u001b[39m"] +[54.832783, "o", "\u001b[38;2;254;68;60m8\u001b[39m\u001b[38;2;254;57;71m9\u001b[39m"] +[54.832831, "o", "\u001b[38;2;253;47;83m1\u001b[39m"] +[54.83288, "o", "\u001b[38;2;250;38;95m9\u001b[39m"] +[54.832927, "o", "\u001b[38;2;246;29;107m \u001b[39m\u001b[38;2;241;22;120m \u001b[39m"] +[54.832975, "o", "\u001b[38;2;235;15;132m.\u001b[39m"] +[54.833023, "o", "\u001b[38;2;228;10;145m/\u001b[39m"] +[54.833121, "o", "\u001b[38;2;219;6;158mi\u001b[39m"] +[54.83321, "o", "\u001b[38;2;210;3;170mc\u001b[39m"] +[54.833278, "o", "\u001b[38;2;200;1;182mo\u001b[39m\u001b[38;2;189;1;193mn\u001b[39m"] +[54.83331, "o", "\u001b[38;2;178;1;203m.\u001b[39m"] +[54.833363, "o", "\u001b[38;2;166;3;213mp\u001b[39m"] +[54.833433, "o", "\u001b[38;2;154;7;222mn\u001b[39m\u001b[38;2;141;11;230mg\u001b[39m\u001b[38;2;129;17;237m\u001b[39m"] +[54.833487, "o", "\r\n"] +[54.833545, "o", "\u001b[38;2;3;168;211md\u001b[39m"] +[54.833605, "o", "\u001b[38;2;1;180;201m8\u001b[39m"] +[54.833658, "o", "\u001b[38;2;1;191;191ma\u001b[39m"] +[54.833717, "o", "\u001b[38;2;1;202;179m1\u001b[39m\u001b[38;2;3;212;167m0\u001b[39m"] +[54.833786, "o", "\u001b[38;2;6;221;155me\u001b[39m"] +[54.833864, "o", "\u001b[38;2;11;229;143m8\u001b[39m"] +[54.833928, "o", "\u001b[38;2;16;236;130m5\u001b[39m\u001b[38;2;23;242;117m7\u001b[39m"] +[54.833993, "o", "\u001b[38;2;31;247;105m1\u001b[39m"] +[54.834054, "o", "\u001b[38;2;39;251;92mb\u001b[39m\u001b[38;2;49;253;80m7\u001b[39m"] +[54.834111, "o", "\u001b[38;2;59;254;69m8\u001b[39m\u001b[38;2;70;254;58m9\u001b[39m"] +[54.834177, "o", "\u001b[38;2;82;253;48mb\u001b[39m"] +[54.834256, "o", "\u001b[38;2;94;250;38mc\u001b[39m\u001b[38;2;106;246;30m7\u001b[39m"] +[54.834328, "o", "\u001b[38;2;119;241;22ma\u001b[39m\u001b[38;2;132;235;15mc"] +[54.834387, "o", "\u001b[39m"] +[54.834938, "o", "\u001b[38;2;144;228;10m0\u001b[39m"] +[54.83501, "o", "\u001b[38;2;157;220;6m9\u001b[39m\u001b[38;2;169;211;3mb\u001b[39m"] +[54.83508, "o", "\u001b[38;2;181;201;1ma\u001b[39m\u001b[38;2;192;190;1me\u001b[39m\u001b[38;2;203;179;1m"] +[54.835137, "o", "2\u001b[39m\u001b[38;2;213;167;3mc\u001b[39m"] +[54.835222, "o", "\u001b[38;2;222;154;7m8\u001b[39m\u001b[38;2;230;142;11m"] +[54.835296, "o", "e\u001b[39m\u001b[38;2;237;129;17me\u001b[39m\u001b[38;2;243;117;23m1\u001b[39m"] +[54.835365, "o", "\u001b[38;2;247;104;31m2\u001b[39m\u001b[38;2;251;92;40m"] +[54.835432, "o", "3\u001b[39m\u001b[38;2;253;80;50m6\u001b[39m"] +[54.835485, "o", "\u001b[38;2;254;68;60ma\u001b[39m\u001b[38;2;254;57;71m2\u001b[39m"] +[54.835546, "o", "\u001b[38;2;253;47;83mf\u001b[39m\u001b[38;2;250;38;95m"] +[54.835612, "o", "a\u001b[39m\u001b[38;2;246;29;107mc\u001b[39m"] +[54.835676, "o", "\u001b[38;2;241;22;120ma\u001b[39m\u001b[38;2;235;15;132ma\u001b[39m"] +[54.835731, "o", "\u001b[38;2;228;10;145m \u001b[39m\u001b[38;2;219;6;158m \u001b[39m"] +[54.835791, "o", "\u001b[38;2;210;3;170m.\u001b[39m"] +[54.835858, "o", "\u001b[38;2;200;1;182m/\u001b[39m\u001b[38;2;189;1;193mi\u001b[39m"] +[54.835922, "o", "\u001b[38;2;178;1;203mc\u001b[39m\u001b[38;2;166;3;213mo\u001b[39m"] +[54.835974, "o", "\u001b[38;2;154;7;222mn\u001b[39m"] +[54.83603, "o", "\u001b[38;2;141;11;230m.\u001b[39m\u001b[38;2;129;17;237mp\u001b[39m"] +[54.836085, "o", "\u001b[38;2;116;24;243mn\u001b[39m"] +[54.836148, "o", "\u001b[38;2;103;32;248mg\u001b[39m\u001b[38;2;91;40;251m.\u001b[39m"] +[54.836213, "o", "\u001b[38;2;79;50;253mi\u001b[39m\u001b[38;2;68;61;254mm\u001b[39m"] +[54.83628, "o", "\u001b[38;2;57;72;254mp\u001b[39m\u001b[38;2;46;83;253mo\u001b[39m"] +[54.836353, "o", "\u001b[38;2;37;95;250mr\u001b[39m\u001b[38;2;29;108;246mt"] +[54.836421, "o", "\u001b[39m\u001b[38;2;21;120;241m"] +[54.836493, "o", "\u001b[39m\r\n"] +[54.836592, "o", "\u001b[38;2;1;202;179m5\u001b[39m"] +[54.836652, "o", "\u001b[38;2;3;212;167m2\u001b[39m"] +[54.836709, "o", "\u001b[38;2;6;221;155m0\u001b[39m\u001b[38;2;11;229;143mc\u001b[39m"] +[54.83677, "o", "\u001b[38;2;16;236;130m7\u001b[39m"] +[54.836841, "o", "\u001b[38;2;23;242;117m0\u001b[39m\u001b[38;2;31;247;105m8\u001b[39m"] +[54.836898, "o", "\u001b[38;2;39;251;92mb\u001b[39m"] +[54.836965, "o", "\u001b[38;2;49;253;80me\u001b[39m\u001b[38;2;59;254;69ma\u001b[39m"] +[54.837021, "o", "\u001b[38;2;70;254;58m1\u001b[39m"] +[54.837075, "o", "\u001b[38;2;82;253;48m5\u001b[39m"] +[54.837132, "o", "\u001b[38;2;94;250;38ma\u001b[39m"] +[54.837194, "o", "\u001b[38;2;106;246;30m1\u001b[39m\u001b[38;2;119;241;22mf\u001b[39m"] +[54.837257, "o", "\u001b[38;2;132;235;15m5\u001b[39m"] +[54.837331, "o", "\u001b[38;2;144;228;10ma\u001b[39m\u001b[38;2;157;220;6m2\u001b[39m"] +[54.837398, "o", "\u001b[38;2;169;211;3m7\u001b[39m\u001b[38;2;181;201;1mc\u001b[39m"] +[54.837465, "o", "\u001b[38;2;192;190;1ma\u001b[39m\u001b[38;2;203;179;1m0\u001b[39m"] +[54.83753, "o", "\u001b[38;2;213;167;3mb\u001b[39m\u001b[38;2;222;154;7m9"] +[54.837594, "o", "\u001b[39m\u001b[38;2;230;142;11md\u001b[39m"] +[54.83765, "o", "\u001b[38;2;237;129;17mf\u001b[39m\u001b[38;2;243;117;23ma\u001b[39m"] +[54.83771, "o", "\u001b[38;2;247;104;31m8\u001b[39m"] +[54.837767, "o", "\u001b[38;2;251;92;40m8\u001b[39m\u001b[38;2;253;80;50mc\u001b[39m"] +[54.837824, "o", "\u001b[38;2;254;68;60m7\u001b[39m\u001b[38;2;254;57;71mf\u001b[39m"] +[54.837888, "o", "\u001b[38;2;253;47;83m0\u001b[39m\u001b[38;2;250;38;95ma\u001b[39m"] +[54.837966, "o", "\u001b[38;2;246;29;107mc\u001b[39m\u001b[38;2;241;22;120m"] +[54.838041, "o", "0\u001b[39m\u001b[38;2;235;15;132m4\u001b[39m"] +[54.83811, "o", "\u001b[38;2;228;10;145me\u001b[39m"] +[54.838176, "o", "\u001b[38;2;219;6;158mb\u001b[39m\u001b[38;2;210;3;170m6\u001b[39m"] +[54.838241, "o", "\u001b[38;2;200;1;182m \u001b[39m\u001b[38;2;189;1;193m \u001b[39m"] +[54.83832, "o", "\u001b[38;2;178;1;203m.\u001b[39m\u001b[38;2;166;3;213m/\u001b[39m"] +[54.838353, "o", "\u001b[38;2;154;7;222mL\u001b[39m\u001b[38;2;141;11;230mI"] +[54.838418, "o", "\u001b[39m\u001b[38;2;129;17;237mC\u001b[39m"] +[54.838464, "o", "\u001b[38;2;116;24;243mE\u001b[39m\u001b[38;2;103;32;248mN"] +[54.838512, "o", "\u001b[39m\u001b[38;2;91;40;251mS\u001b[39m"] +[54.838572, "o", "\u001b[38;2;79;50;253mE\u001b[39m\u001b[38;2;68;61;254m\u001b[39m\r\n"] +[54.838665, "o", "\u001b[38;2;11;229;143m2\u001b[39m"] +[54.838718, "o", "\u001b[38;2;16;236;130mb\u001b[39m"] +[54.838787, "o", "\u001b[38;2;23;242;117m9\u001b[39m\u001b[38;2;31;247;105me\u001b[39m"] +[54.838987, "o", "\u001b[38;2;39;251;92m7\u001b[39m"] +[54.839081, "o", "\u001b[38;2;49;253;80mf\u001b[39m"] +[54.839181, "o", "\u001b[38;2;59;254;69m6\u001b[39m"] +[54.839246, "o", "\u001b[38;2;70;254;58m0\u001b[39m\u001b[38;2;82;253;48m6\u001b[39m"] +[54.839312, "o", "\u001b[38;2;94;250;38mf\u001b[39m\u001b[38;2;106;246;30m9\u001b[39m"] +[54.839376, "o", "\u001b[38;2;119;241;22m5\u001b[39m"] +[54.839429, "o", "\u001b[38;2;132;235;15mc\u001b[39m\u001b[38;2;144;228;10m5\u001b[39m"] +[54.839482, "o", "\u001b[38;2;157;220;6me\u001b[39m\u001b[38;2;169;211;3m"] +[54.839535, "o", "c\u001b[39m\u001b[38;2;181;201;1m6\u001b[39m"] +[54.839587, "o", "\u001b[38;2;192;190;1m9\u001b[39m"] +[54.839637, "o", "\u001b[38;2;203;179;1md\u001b[39m\u001b[38;2;213;167;3m7\u001b[39m"] +[54.839688, "o", "\u001b[38;2;222;154;7m9\u001b[39m"] +[54.839738, "o", "\u001b[38;2;230;142;11mc\u001b[39m"] +[54.839787, "o", "\u001b[38;2;237;129;17md\u001b[39m\u001b[38;2;243;117;23mc\u001b[39m"] +[54.839842, "o", "\u001b[38;2;247;104;31m4\u001b[39m"] +[54.839885, "o", "\u001b[38;2;251;92;40m1\u001b[39m"] +[54.839941, "o", "\u001b[38;2;253;80;50m3\u001b[39m\u001b[38;2;254;68;60mb\u001b[39m"] +[54.83999, "o", "\u001b[38;2;254;57;71m0\u001b[39m"] +[54.840043, "o", "\u001b[38;2;253;47;83mb\u001b[39m\u001b[38;2;250;38;95m0\u001b[39m"] +[54.840089, "o", "\u001b[38;2;246;29;107md\u001b[39m"] +[54.840151, "o", "\u001b[38;2;241;22;120m9\u001b[39m\u001b[38;2;235;15;132mf"] +[54.840196, "o", "\u001b[39m\u001b[38;2;228;10;145ma\u001b[39m"] +[54.840245, "o", "\u001b[38;2;219;6;158m0\u001b[39m"] +[54.840306, "o", "\u001b[38;2;210;3;170me\u001b[39m"] +[54.840357, "o", "\u001b[38;2;200;1;182m8\u001b[39m\u001b[38;2;189;1;193m8\u001b[39m"] +[54.840409, "o", "\u001b[38;2;178;1;203ma\u001b[39m"] +[54.840458, "o", "\u001b[38;2;166;3;213m \u001b[39m\u001b[38;2;154;7;222m \u001b[39m"] +[54.840507, "o", "\u001b[38;2;141;11;230m.\u001b[39m"] +[54.840558, "o", "\u001b[38;2;129;17;237m/\u001b[39m"] +[54.840609, "o", "\u001b[38;2;116;24;243mn\u001b[39m\u001b[38;2;103;32;248me\u001b[39m"] +[54.840662, "o", "\u001b[38;2;91;40;251mw\u001b[39m"] +[54.840709, "o", "\u001b[38;2;79;50;253m.\u001b[39m\u001b[38;2;68;61;254m"] +[54.840762, "o", "c\u001b[39m"] +[54.841327, "o", "\u001b[38;2;57;72;254ma\u001b[39m"] +[54.841424, "o", "\u001b[38;2;46;83;253ms\u001b[39m\u001b[38;2;37;95;250mt\u001b[39m\u001b[38;2;29;108;246m\u001b[39m\r\n"] +[54.841493, "o", "\u001b[38;2;31;247;105mb\u001b[39m\u001b[38;2;39;251;92mb\u001b[39m"] +[54.841552, "o", "\u001b[38;2;49;253;80m9\u001b[39m"] +[54.841607, "o", "\u001b[38;2;59;254;69me\u001b[39m\u001b[38;2;70;254;58m2\u001b[39m"] +[54.841663, "o", "\u001b[38;2;82;253;48ma\u001b[39m"] +[54.841719, "o", "\u001b[38;2;94;250;38ma\u001b[39m\u001b[38;2;106;246;30m7\u001b[39m"] +[54.841779, "o", "\u001b[38;2;119;241;22m9\u001b[39m"] +[54.841812, "o", "\u001b[38;2;132;235;15ma\u001b[39m\u001b[38;2;144;228;10m6\u001b[39m"] +[54.841885, "o", "\u001b[38;2;157;220;6ma\u001b[39m"] +[54.841945, "o", "\u001b[38;2;169;211;3m6\u001b[39m\u001b[38;2;181;201;1mc\u001b[39m"] +[54.842006, "o", "\u001b[38;2;192;190;1m9\u001b[39m"] +[54.842066, "o", "\u001b[38;2;203;179;1m4\u001b[39m\u001b[38;2;213;167;3m"] +[54.842122, "o", "d\u001b[39m\u001b[38;2;222;154;7m4\u001b[39m"] +[54.842176, "o", "\u001b[38;2;230;142;11m4\u001b[39m"] +[54.842263, "o", "\u001b[38;2;237;129;17m6\u001b[39m\u001b[38;2;243;117;23m0\u001b[39m"] +[54.842396, "o", "\u001b[38;2;247;104;31m8\u001b[39m"] +[54.842476, "o", "\u001b[38;2;251;92;40m4\u001b[39m"] +[54.842562, "o", "\u001b[38;2;253;80;50m1\u001b[39m\u001b[38;2;254;68;60me\u001b[39m"] +[54.842659, "o", "\u001b[38;2;254;57;71m0\u001b[39m\u001b[38;2;253;47;83m5\u001b[39m"] +[54.842742, "o", "\u001b[38;2;250;38;95m3\u001b[39m\u001b[38;2;246;29;107m9\u001b[39m"] +[54.842848, "o", "\u001b[38;2;241;22;120m4\u001b[39m"] +[54.842933, "o", "\u001b[38;2;235;15;132m8\u001b[39m\u001b[38;2;228;10;145m4\u001b[39m\u001b[38;2;219;6;158me\u001b[39m"] +[54.843016, "o", "\u001b[38;2;210;3;170md\u001b[39m\u001b[38;2;200;1;182mc\u001b[39m\u001b[38;2;189;1;193m0\u001b[39m"] +[54.843101, "o", "\u001b[38;2;178;1;203m5\u001b[39m\u001b[38;2;166;3;213mf\u001b[39m\u001b[38;2;154;7;222ma\u001b[39m"] +[54.843189, "o", "\u001b[38;2;141;11;230m5\u001b[39m\u001b[38;2;129;17;237m \u001b[39m\u001b[38;2;116;24;243m \u001b[39m"] +[54.843298, "o", "\u001b[38;2;103;32;248m.\u001b[39m\u001b[38;2;91;40;251m/\u001b[39m"] +[54.843338, "o", "\u001b[38;2;79;50;253mp\u001b[39m\u001b[38;2;68;61;254mr\u001b[39m"] +[54.843438, "o", "\u001b[38;2;57;72;254mo\u001b[39m\u001b[38;2;46;83;253mj\u001b[39m\u001b[38;2;37;95;250me\u001b[39m"] +[54.843511, "o", "\u001b[38;2;29;108;246mc\u001b[39m\u001b[38;2;21;120;241mt\u001b[39m\u001b[38;2;15;133;235m.\u001b[39m"] +[54.843584, "o", "\u001b[38;2;10;146;227mg\u001b[39m\u001b[38;2;5;158;219mo\u001b[39m\u001b[38;2;3;170;210md"] +[54.843652, "o", "\u001b[39m\u001b[38;2;1;182;199mo\u001b[39m\u001b[38;2;1;193;189mt\u001b[39m"] +[54.843723, "o", "\u001b[38;2;1;204;177m\u001b[39m\r\n"] +[54.843797, "o", "\u001b[38;2;59;254;69m3\u001b[39m"] +[54.84387, "o", "\u001b[38;2;70;254;58ma\u001b[39m\u001b[38;2;82;253;48m8\u001b[39m\u001b[38;2;94;250;38m3"] +[54.843944, "o", "\u001b[39m\u001b[38;2;106;246;30m0\u001b[39m\u001b[38;2;119;241;22m0\u001b[39m"] +[54.844009, "o", "\u001b[38;2;132;235;15m5\u001b[39m\u001b[38;2;144;228;10ma\u001b[39m"] +[54.844071, "o", "\u001b[38;2;157;220;6m5\u001b[39m\u001b[38;2;169;211;3md\u001b[39m"] +[54.844155, "o", "\u001b[38;2;181;201;1mc\u001b[39m\u001b[38;2;192;190;1m8\u001b[39m"] +[54.844245, "o", "\u001b[38;2;203;179;1mb\u001b[39m\u001b[38;2;213;167;3ma\u001b[39m\u001b[38;2;222;154;7m5"] +[54.844341, "o", "\u001b[39m\u001b[38;2;230;142;11md\u001b[39m\u001b[38;2;237;129;17m0\u001b[39m\u001b[38;2;243;117;23mf\u001b[39m\u001b[38;2;247;104;31m0\u001b[39m"] +[54.844424, "o", "\u001b[38;2;251;92;40ma\u001b[39m\u001b[38;2;253;80;50mb\u001b[39m"] +[54.844495, "o", "\u001b[38;2;254;68;60mb\u001b[39m\u001b[38;2;254;57;71m3\u001b[39m"] +[54.844563, "o", "\u001b[38;2;253;47;83md\u001b[39m\u001b[38;2;250;38;95me\u001b[39m"] +[54.844639, "o", "\u001b[38;2;246;29;107m8\u001b[39m\u001b[38;2;241;22;120m4\u001b[39m\u001b[38;2;235;15;132m7\u001b[39m\u001b[38;2;228;10;145md\u001b[39m"] +[54.844728, "o", "\u001b[38;2;219;6;158m2\u001b[39m\u001b[38;2;210;3;170md\u001b[39m"] +[54.844818, "o", "\u001b[38;2;200;1;182me\u001b[39m\u001b[38;2;189;1;193m0\u001b[39m\u001b[38;2;178;1;203ma\u001b[39m"] +[54.844893, "o", "\u001b[38;2;166;3;213m4\u001b[39m\u001b[38;2;154;7;222m6\u001b[39m\u001b[38;2;141;11;230md\u001b[39m\u001b[38;2;129;17;237m7\u001b[39m"] +[54.84499, "o", "\u001b[38;2;116;24;243m7\u001b[39m\u001b[38;2;103;32;248mb\u001b[39m\u001b[38;2;91;40;251m \u001b[39m"] +[54.845083, "o", "\u001b[38;2;79;50;253m \u001b[39m\u001b[38;2;68;61;254m.\u001b[39m\u001b[38;2;57;72;254m/\u001b[39m\u001b[38;2;46;83;253mR\u001b[39m\u001b[38;2;37;95;250mE\u001b[39m"] +[54.845178, "o", "\u001b[38;2;29;108;246mA\u001b[39m\u001b[38;2;21;120;241mD\u001b[39m"] +[54.845256, "o", "\u001b[38;2;15;133;235mM\u001b[39m\u001b[38;2;10;146;227mE\u001b[39m"] +[54.845343, "o", "\u001b[38;2;5;158;219m.\u001b[39m\u001b[38;2;3;170;210mm\u001b[39m\u001b[38;2;1;182;199md\u001b[39m"] +[54.845422, "o", "\u001b[38;2;1;193;189m\u001b[39m\r\n"] +[54.845491, "o", "\u001b[m\u001b[?25h\u001b[?1;5;2004l"] +[54.847349, "o", "\u001b]2;laptop:leroy:~/projects/godot-xterm\u0007\r\r\n\u001b[1;32m[\u001b]0;leroy@laptop: ~/projects/godot-xterm\u0007leroy@laptop:~/projects/godot-xterm]$\u001b[0m "] +[57.064686, "o", "e"] +[57.230802, "o", "x"] +[59.010363, "o", "\b\u001b[K"] +[59.121216, "o", "\b\u001b[K"] +[59.456844, "o", "#"] +[59.676225, "o", "/"] +[60.10367, "o", "\b\u001b[K"] +[60.18748, "o", " "] +[60.4099, "o", "w"] +[60.748895, "o", "e"] +[60.992399, "o", "\b\u001b[K"] +[61.116881, "o", "\b\u001b[K"] +[61.313248, "o", "W"] +[61.473147, "o", "h"] +[61.562029, "o", "e"] +[61.645136, "o", "n"] +[61.917431, "o", " "] +[62.093406, "o", "w"] +[62.161239, "o", "e"] +[62.248416, "o", " "] +[62.33829, "o", "a"] +[62.415246, "o", "r"] +[62.507794, "o", "e"] +[62.557211, "o", " "] +[62.753875, "o", "f"] +[62.85347, "o", "i"] +[62.921221, "o", "n"] +[63.033374, "o", "i"] +[63.094372, "o", "s"] +[63.187799, "o", "h"] +[63.483077, "o", "e"] +[63.612519, "o", "d"] +[63.792416, "o", ","] +[63.894738, "o", " "] +[64.04963, "o", "j"] +[64.202261, "o", "u"] +[64.263783, "o", "s"] +[64.307216, "o", "t"] +[64.369715, "o", " "] +[64.492205, "o", "e"] +[64.653815, "o", "x"] +[64.746695, "o", "i"] +[64.858507, "o", "t"] +[65.103114, "o", " "] +[65.664488, "o", "t"] +[65.719435, "o", "h"] +[65.782418, "o", "e"] +[65.844266, "o", " "] +[65.963151, "o", "s"] +[66.037719, "o", "h"] +[66.121665, "o", "e"] +[66.224533, "o", "l"] +[66.323957, "o", "l"] +[66.562302, "o", "\r\n"] +[66.562831, "o", "\u001b]2;laptop:leroy:~/projects/godot-xterm\u0007\r\r\n\u001b[1;32m[\u001b]0;leroy@laptop: ~/projects/godot-xterm\u0007leroy@laptop:~/projects/godot-xterm]$\u001b[0m "] +[66.844263, "o", "e"] +[66.983977, "o", "x"] +[67.104686, "o", "i"] +[67.212146, "o", "t"] +[67.515156, "o", "\r\n"] +[67.515263, "o", "exit\r\n"] +[67.516389, "o", "\u001b[0;32masciinema: recording finished\u001b[0m\r\n\u001b[0;32masciinema: asciicast saved to new.cast\u001b[0m\r\n"] +[67.526166, "o", "sh-4.4$ "] +[69.107982, "o", "c"] +[69.201795, "o", "o"] +[69.306066, "o", "w"] +[69.520215, "o", "s"] +[69.638453, "o", "a"] +[69.696601, "o", "y"] +[70.376247, "o", " "] +[70.997599, "o", "A"] +[71.148845, "o", "l"] +[71.283482, "o", "l"] +[71.322607, "o", " "] +[71.823544, "o", "d"] +[71.910819, "o", "o"] +[72.005256, "o", "n"] +[72.059912, "o", "e"] +[72.234089, "o", "!"] +[72.62946, "o", "\r\n"] +[72.64376, "o", " ___________ \r\n< All done! >\r\n ----------- \r\n \\ ^__^\r\n \\ (oo)\\_______\r\n (__)\\ )\\/\\\r\n ||----w |\r\n || ||\r\n"] +[72.644144, "o", "sh-4.4$ "] +[73.525287, "o", "\r\n"] +[73.525434, "o", "sh-4.4$ "] +[74.180823, "o", "e"] +[74.345967, "o", "x"] +[74.476496, "o", "i"] +[74.626928, "o", "t"] +[75.019672, "o", "\r\nexit\r\n"] diff --git a/examples/menu/menu.gd b/examples/menu/menu.gd new file mode 100644 index 0000000..7b84558 --- /dev/null +++ b/examples/menu/menu.gd @@ -0,0 +1,149 @@ +tool +extends Control +# This scene demonstrates how we can control the Terminal node directly by +# sending and receiving strings and ANSI escape sequences to the terminal +# directly. + +# References: +# - https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html +# - https://www.youtube.com/watch?v=jTSQlIK_92w + +const TITLE = """ +░█▀▀░█▀█░█▀▄░█▀█░▀█▀░░░█░█░▀█▀░█▀▀░█▀▄░█▄█\r +░█░█░█░█░█░█░█░█░░█░░░░▄▀▄░░█░░█▀▀░█▀▄░█░█\r +░▀▀▀░▀▀▀░▀▀░░▀▀▀░░▀░░░░▀░▀░░▀░░▀▀▀░▀░▀░▀░▀\r +""" +const TITLE_WIDTH = 42 + +var menu_items := [ + { "name": "Asciicast", "scene": preload("../asciicast/asciicast.tscn") }, + { "name": "Terminal", "scene": preload("../terminal/terminal.tscn") }, + { "name": "Exit"} +] + +var selected_index := 0 + +var row: int +var menu_start_row: int +var offset: int + +onready var tput = TPut.new($Terminal) + + +func _ready(): + # warning-ignore:return_value_discarded + $Terminal.connect("size_changed", self, "draw_all") + $Terminal.grab_focus() + draw_all() + + +func draw_all(_size = Vector2.ZERO): + offset = int(floor(($Terminal.cols / 2.0) - (TITLE_WIDTH / 2.0))) + tput.reset() + row = 5 + tput.civis() # Hide the cursor. + draw_title() + draw_menu() + tput.sgr0() + row += 1 + + +func draw_title(): + tput.cup(row, 0) + + for line in TITLE.split("\r"): + row += 1 + tput.cup(row, offset) + $Terminal.write(line) + + # Get the plugin version from the plugin's config file. + var config = ConfigFile.new() + var err = config.load("res://addons/godot_xterm/plugin.cfg") + if err == OK: + $Terminal.write("\n") + $Terminal.write("Version: %s" % config.get_value("plugin", "version", + "unknown")) + row += 2 + + +func draw_menu(): + if not menu_start_row: + menu_start_row = row + 1 + + row = menu_start_row + + var col_offset: int + + for i in range(menu_items.size()): + row += 1 + var item = menu_items[i] + + if not col_offset: + col_offset = int(floor(($Terminal.cols / 2) - (item.name.length() / 2))) + + tput.cup(row, offset) + + if selected_index == i: + tput.setab(Color("#FF7500")) + tput.setaf(Color.black) + + $Terminal.write("%s. %s" % [i + 1, item.name]) + + if selected_index == i: + tput.sgr0() + + +func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void: + match(data): + TPut.CURSOR_UP: # Up arrow key + selected_index = int(clamp(selected_index - 1, 0, menu_items.size() - 1)) + draw_menu() + TPut.CURSOR_DOWN: # Down arrow key + selected_index = int(clamp(selected_index + 1, 0, menu_items.size() - 1)) + draw_menu() + "1": + selected_index = 0 + draw_menu() + "2": + selected_index = 1 + draw_menu() + "3": + selected_index = 2 + draw_menu() + + # We can also match against the raw InputEventKey. + if event.scancode == KEY_ENTER: + var item = menu_items[selected_index] + + match item.name: + "Asciicast": + var scene = item.scene.instance() + var animation_player: AnimationPlayer = scene.get_node("AnimationPlayer") + scene.connect("key_pressed", self, "_on_Asciicast_key_pressed", + [animation_player]) + get_tree().get_root().add_child(scene) + visible = false + scene.grab_focus() + yield(animation_player, "animation_finished") + visible = true + get_tree().get_root().remove_child(scene) + $Terminal.grab_focus() + scene.queue_free() + "Terminal": + var scene = item.scene.instance() + var pty = scene.get_node("Pseudoterminal") + get_tree().get_root().add_child(scene) + visible = false + scene.grab_focus() + yield(pty, "exited") + visible = true + $Terminal.grab_focus() + scene.queue_free() + "Exit": + get_tree().quit() + + +func _on_Asciicast_key_pressed(data: String, _event: InputEventKey, + animation_player: AnimationPlayer) -> void: + if data == "\u001b": + animation_player.emit_signal("animation_finished") diff --git a/examples/menu/menu.tscn b/examples/menu/menu.tscn new file mode 100644 index 0000000..b4dcfa5 --- /dev/null +++ b/examples/menu/menu.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/godot_xterm/nodes/terminal/terminal.gdns" type="Script" id=1] +[ext_resource path="res://examples/menu/menu.gd" type="Script" id=2] +[ext_resource path="res://addons/godot_xterm/themes/default.theme" type="Theme" id=3] + +[node name="Menu" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Terminal" type="Control" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +focus_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme = ExtResource( 3 ) +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +rows = 31 +cols = 102 +[connection signal="key_pressed" from="Terminal" to="." method="_on_Terminal_key_pressed"] diff --git a/examples/terminal/Node.gd b/examples/terminal/Node.gd new file mode 100644 index 0000000..593387e --- /dev/null +++ b/examples/terminal/Node.gd @@ -0,0 +1,7 @@ +extends Node + + + +func _on_Terminal_key_pressed(event: InputEventKey, data: PoolByteArray): + print(data as Array) + print(event.scancode) diff --git a/examples/terminal/Terminal.tscn b/examples/terminal/terminal.tscn similarity index 69% rename from examples/terminal/Terminal.tscn rename to examples/terminal/terminal.tscn index c54c563..276fe26 100644 --- a/examples/terminal/Terminal.tscn +++ b/examples/terminal/terminal.tscn @@ -8,15 +8,16 @@ anchor_right = 1.0 anchor_bottom = 1.0 focus_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 theme = ExtResource( 4 ) script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } +rows = 31 +cols = 102 [node name="Pseudoterminal" type="Node" parent="."] script = ExtResource( 2 ) -[connection signal="data_read" from="." to="Pseudoterminal" method="put_data"] -[connection signal="data_received" from="Pseudoterminal" to="." method="write"] +[connection signal="data_sent" from="." to="Pseudoterminal" method="write"] +[connection signal="size_changed" from="." to="Pseudoterminal" method="resize"] +[connection signal="data_sent" from="Pseudoterminal" to="." method="write"] diff --git a/project.godot b/project.godot index 09f49ea..b3b63e5 100644 --- a/project.godot +++ b/project.godot @@ -10,23 +10,30 @@ config_version=4 _global_script_classes=[ { "base": "Reference", -"class": "ArrayUtils", +"class": "TPut", "language": "GDScript", -"path": "res://addons/SIsilicon.3d.text/array_utils.gd" +"path": "res://addons/godot_xterm/util/tput.gd" } ] _global_script_class_icons={ -"ArrayUtils": "" +"TPut": "" } [application] config/name="Godot Xterm" +run/main_scene="res://examples/menu/menu.tscn" config/icon="res://icon.png" +[display] + +window/vsync/use_vsync=false + [editor_plugins] -enabled=PoolStringArray( "SIsilicon.3d.text", "godot_xterm" ) +enabled=PoolStringArray( "godot_xterm" ) [rendering] +quality/filters/anisotropic_filter_level=16 +quality/filters/msaa=4 environment/default_environment="res://default_env.tres"

+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