### Build Native Extension (Debug) Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Use this command to build the native extension in debug mode. Ensure Just is installed and the project is cloned. ```bash just build ``` -------------------------------- ### Display 'Hello World' in Terminal Source: https://github.com/lihop/godot-xterm/wiki/tutorials/Hello-World-Tutorial Add this script to your Terminal node to display 'Hello World' upon scene start. Ensure the script extends the base terminal node. ```gdscript extends "res://addons/godot_xterm/nodes/terminal/terminal.gd" func _ready(): write("Hello World") ``` -------------------------------- ### Build JavaScript/Web Target Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Compiles the extension for JavaScript/WebAssembly using a Docker container. Ensure Docker is installed and running. ```bash just build-javascript ``` -------------------------------- ### Start and Manage Processes with PTY Node Source: https://context7.com/lihop/godot-xterm/llms.txt Demonstrates how to fork, send signals (TERM, KILL, INT), and resize a process using the PTY node. Connects to data_received and exited signals for output and lifecycle management. ```gdscript extends Node @onready var pty = $PTY var _process_running := false func _ready(): pty.data_received.connect(_on_output) pty.exited.connect(_on_exit) func start_long_running_process(): # Start a continuous process like ping var result = pty.fork("ping", ["google.com"], ".", 80, 24) if result == OK: _process_running = true print("Process started") # Get PTY device name (Unix only) var pts_name = pty.get_pts_name() if pts_name != "": print("PTY device: ", pts_name) # e.g., /dev/pts/1 func stop_process_gracefully(): if _process_running: # Send SIGTERM for graceful termination pty.kill(PTY.IPCSIGNAL_SIGTERM) func stop_process_forcefully(): if _process_running: # Send SIGKILL for immediate termination pty.kill(PTY.IPCSIGNAL_SIGKILL) func send_interrupt(): if _process_running: # Send SIGINT (equivalent to Ctrl+C) pty.kill(PTY.IPCSIGNAL_SIGINT) func resize_terminal(cols: int, rows: int): # Resize PTY (sends SIGWINCH to process on Unix) pty.resize(cols, rows) # Or use vector variant: # pty.resizev(Vector2i(cols, rows)) func _on_output(data: PackedByteArray): print(data.get_string_from_utf8()) func _on_exit(exit_code: int, signal_code: int): _process_running = false print("Process exited: code=%d, signal=%d" % [exit_code, signal_code]) ``` -------------------------------- ### Stream output from a long-running process with PTY Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/pty_node.md Start a long-running process like 'ping', capture its real-time output, and then terminate it after a delay using the kill method with SIGINT. ```gdscript extends Node @onready var pty = $PTY func _ready(): # Connect to signals pty.data_received.connect(_on_output) pty.exited.connect(_on_exit) # Start ping var result = pty.fork("ping", ["google.com"]) if result != OK: print("Failed to start ping: ", result) return # Stop ping after 10 seconds await get_tree().create_timer(10.0).timeout print("Stopping ping...") pty.kill(2) # Send SIGINT to stop the process func _on_output(data: PackedByteArray): var output = data.get_string_from_utf8() print("Shell output: ", output) func _on_exit(exit_code: int, signal_code: int): print("Shell exited with code: ", exit_code) ``` -------------------------------- ### Build with Release Target Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Builds the native extension in release mode. Set the TARGET environment variable to 'release' before running the build command. ```bash TARGET=release just build ``` -------------------------------- ### Build All Targets Including JavaScript Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md This command builds all available targets, including those for JavaScript/WebAssembly. It requires all necessary build tools and potentially Docker for web builds. ```bash just build-all ``` -------------------------------- ### Extract Addon Files (Linux/macOS) Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/installation.md Use this command to extract the GodotXterm addon files into your project directory on Linux or macOS. Ensure you replace `/path/to/your/project` with the actual path to your Godot project's root. ```bash unzip godot-xterm-v4.0.0.zip -d /path/to/your/project ``` -------------------------------- ### Run All Tests Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Executes all defined tests, including unit, integration, and visual tests. This is useful for verifying the build and code integrity. ```bash just test ``` -------------------------------- ### Run Specific Test Types Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Allows running specific categories of tests. Use 'unit', 'integration', or 'visual' to target particular test suites. ```bash just test unit ``` ```bash just test integration ``` ```bash just test visual ``` -------------------------------- ### PTY Node - Interactive Shell Session Source: https://context7.com/lihop/godot-xterm/llms.txt Set up an interactive shell session by forking the default shell. Configure PTY dimensions, environment variables, and use threading for performance. Send commands and receive continuous output. ```gdscript extends Node @onready var pty = $PTY func _ready(): pty.data_received.connect(_on_output) pty.exited.connect(_on_exit) # Configure PTY dimensions pty.cols = 80 pty.rows = 24 # Configure environment variables pty.use_os_env = true # Inherit environment from Godot process pty.env = { "TERM": "xterm-256color", "COLORTERM": "truecolor", "CUSTOM_VAR": "my_value" } # Enable threading for better performance (default: true) pty.use_threads = true # Fork default shell (uses $SHELL env var, or sh/powershell as fallback) var result = pty.fork() # No arguments = default shell if result != OK: print("Failed to start shell: ", result) return # Send commands to the shell await get_tree().create_timer(0.5).timeout pty.write("echo 'Hello from GodotXterm'\n") pty.write("pwd\n") func send_command(command: String): pty.write(command + "\n") func send_interrupt(): # Send Ctrl+C (ETX character) pty.write(PackedByteArray([0x03])) func _on_output(data: PackedByteArray): print(data.get_string_from_utf8()) func _on_exit(exit_code: int, signal_code: int): print("Shell exited with code: ", exit_code) ``` -------------------------------- ### Run a simple command with PTY Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/pty_node.md Connect to PTY signals and fork a simple command like 'ls -la'. Output is printed via the data_received signal, and the exit code is handled by the exited signal. ```gdscript extends Node @onready var pty = $PTY func _ready(): # Connect to signals pty.data_received.connect(_on_output) pty.exited.connect(_on_exit) # Run a simple command var result = pty.fork("ls", ["-la"]) if result != OK: print("Failed to run command: ", result) func _on_output(data: PackedByteArray): var output = data.get_string_from_utf8() print("Command output: ", output) func _on_exit(exit_code: int, signal_code: int): print("Command finished with exit code: ", exit_code) ``` -------------------------------- ### Clean Build Artifacts Source: https://github.com/lihop/godot-xterm/blob/main/docs/development/building_from_source.md Removes all generated build artifacts. Run this to ensure a clean build environment. ```bash just clean ``` -------------------------------- ### Terminal Node - Basic Usage Source: https://context7.com/lihop/godot-xterm/llms.txt Demonstrates writing text, colored text using ANSI escape sequences, and retrieving terminal properties like dimensions, cursor position, and cell size. Clears the terminal screen. ```gdscript extends Control @onready var terminal = $Terminal func _ready(): # Write simple text to the terminal terminal.write("Hello, World!\n") # Write colored text using ANSI escape sequences terminal.write("\u001b[31mRed text\u001b[0m\n") # Red foreground terminal.write("\u001b[32mGreen text\u001b[0m\n") # Green foreground terminal.write("\u001b[1;34mBold blue\u001b[0m\n") # Bold blue # Write with background colors terminal.write("\u001b[41mRed background\u001b[0m\n") terminal.write("\u001b[38;2;255;165;0mOrange (RGB)\u001b[0m\n") # 24-bit color # Get terminal dimensions var cols = terminal.get_cols() var rows = terminal.get_rows() print("Terminal size: %d rows x %d columns" % [rows, cols]) # Get cursor position var cursor_pos = terminal.get_cursor_pos() print("Cursor at: col=%d, row=%d" % [cursor_pos.x, cursor_pos.y]) # Get cell size in pixels (useful for precise positioning) var cell_size = terminal.get_cell_size() print("Cell size: %s pixels" % cell_size) # Clear the terminal screen terminal.clear() ``` -------------------------------- ### Playing Asciicast Recordings with AnimationPlayer in GDScript Source: https://context7.com/lihop/godot-xterm/llms.txt Integrate and play terminal session recordings (.cast files) using Godot's AnimationPlayer. Requires the .cast file to be imported as an Animation resource and added to an AnimationLibrary. ```gdscript extends Control @onready var terminal = $Terminal @onready var animation_player = $Terminal/AnimationPlayer func _ready(): # The .cast file should be imported as an Animation resource # Add it to an AnimationLibrary in the AnimationPlayer # Load the asciicast animation var lib = AnimationLibrary.new() lib.add_animation("recording", load("res://recordings/demo.cast")) animation_player.add_animation_library("casts", lib) # Play the recording animation_player.play("casts/recording") # Control playback animation_player.speed_scale = 1.0 # Normal speed # animation_player.speed_scale = 2.0 # 2x speed func pause_playback(): animation_player.pause() func resume_playback(): animation_player.play() func seek_to_time(seconds: float): animation_player.seek(seconds) func _on_AnimationPlayer_animation_finished(anim_name: String): terminal.write("\n\u001b[33m[Recording ended]\u001b[0m\n") ``` -------------------------------- ### PTY Node - Running Commands Source: https://context7.com/lihop/godot-xterm/llms.txt Spawn and control system processes using the PTY node. Connect to data_received and exited signals to handle process output and termination. ```gdscript extends Node @onready var pty = $PTY func _ready(): # Connect to PTY signals pty.data_received.connect(_on_pty_data_received) pty.exited.connect(_on_pty_exited) # Run a simple command (ls -la in current directory) var result = pty.fork("ls", ["-la"]) if result != OK: print("Failed to run command: ", result) func _on_pty_data_received(data: PackedByteArray): # Process output is received here var output = data.get_string_from_utf8() print("Output: ", output) func _on_pty_exited(exit_code: int, signal_code: int): print("Command finished with exit code: ", exit_code) if signal_code != 0: print("Terminated by signal: ", signal_code) ``` -------------------------------- ### Basic Terminal Usage in GDScript Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/terminal_node.md Demonstrates essential methods for interacting with the Terminal node, including writing text, using ANSI escape sequences for color, retrieving terminal dimensions, and clearing the screen. Note that Godot's GDScript uses \u001b for escape characters, not \e. ```gdscript extends Control @onready var terminal = $Terminal func _ready(): # Write text to the terminal terminal.write("Hello, World!\n") # Write colored text using ANSI escape sequences terminal.write("\u001b[31mRed text\u001b[0m\n") terminal.write("\u001b[32mGreen text\u001b[0m\n") # Get terminal dimensions print("Terminal size: %d rows x %d columns" % [terminal.get_rows(), terminal.get_cols()]) # Clear the terminal terminal.clear() ``` -------------------------------- ### Connect PTY to Terminal Node Source: https://context7.com/lihop/godot-xterm/llms.txt Shows how to programmatically connect the PTY node to the Terminal node for a functional terminal emulator. Handles data transfer, size synchronization, and process exit events. ```gdscript extends Terminal @onready var pty = $PTY func _ready(): # Method 1: Set terminal_path in inspector (recommended) # This automatically connects all signals # Method 2: Manual signal connections # Connect Terminal output to PTY input data_sent.connect(func(data): pty.write(data)) # Connect PTY output to Terminal display pty.data_received.connect(func(data): write(data)) # Sync terminal size with PTY size_changed.connect(func(new_size): pty.resizev(new_size)) # Handle process exit pty.exited.connect(_on_pty_exited) # Fork a shell (inherits Terminal's size) var result = pty.fork("", [], ".", get_cols(), get_rows()) if result != OK: write("\u001b[31mFailed to start shell\u001b[0m\n") func _on_pty_exited(exit_code: int, signal_code: int): write("\n\u001b[33m[Process exited with code %d]\u001b[0m\n" % exit_code) ``` -------------------------------- ### Handling Terminal Signals in GDScript Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/terminal_node.md Shows how to connect to and handle key signals emitted by the Terminal node, including data sent by the user, bell events, and changes in terminal size. The `data_sent` signal provides user input as a PackedByteArray. ```gdscript func _ready(): # Connect to terminal signals terminal.data_sent.connect(_on_terminal_data_sent) terminal.bell.connect(_on_terminal_bell) terminal.size_changed.connect(_on_terminal_size_changed) func _on_terminal_data_sent(data: PackedByteArray): # User typed something - data contains the key sequence print("User input: ", data.get_string_from_utf8()) func _on_terminal_bell(): # Terminal bell was triggered print("Ding!") func _on_terminal_size_changed(new_size: Vector2i): # Terminal was resized print("New size: %d cols x %d rows" % [new_size.x, new_size.y]) ``` -------------------------------- ### Format Terminal Output with TPut Helper Source: https://context7.com/lihop/godot-xterm/llms.txt Utilizes the TPut helper class to perform common terminal formatting operations such as setting foreground/background colors (ANSI and RGB), positioning the cursor, and applying text attributes like reverse video. ```gdscript extends Control const TPut := preload("res://addons/godot_xterm/util/tput.gd") @onready var terminal = $Terminal var _tput: TPut func _ready(): _tput = TPut.new(terminal) # Set foreground color using ANSI color constants _tput.setaf(TPut.ANSIColor.bright_green) terminal.write("Bright green text\n") # Set background color _tput.setab(TPut.ANSIColor.blue) terminal.write("Blue background") # Reset all attributes _tput.sgr0() terminal.write("\n") # Use exact RGB colors _tput.setaf(Color(1.0, 0.5, 0.0)) # Orange terminal.write("Orange text\n") _tput.sgr0() # Position cursor at specific row and column _tput.cup(5, 10) # Row 5, Column 10 terminal.write("Positioned text") # Reverse video mode _tput.rev() terminal.write("Reversed") _tput.sgr0() # Hide cursor _tput.civis() # Reset terminal completely _tput.reset() func write_colored(text: String, color: Color): _tput.write_string(text, color) ``` -------------------------------- ### Terminal Node - Text Selection and Copy Source: https://context7.com/lihop/godot-xterm/llms.txt Configure copy-on-selection, programmatically select text, and retrieve selected or all terminal content. Connect a button press to copy selected text to the system clipboard. ```gdscript extends Control @onready var terminal = $Terminal func _ready(): # Enable automatic copy-on-selection (like Linux terminals) terminal.copy_on_selection = true # Write some content terminal.write("Line 1: Hello World\n") terminal.write("Line 2: GodotXterm Terminal\n") terminal.write("Line 3: Select me!\n") # Programmatically select text (from_line, from_column, to_line, to_column) # All coordinates are zero-based terminal.select(0, 0, 0, 10) # Select "Line 1: He" # Get the selected text var selected = terminal.copy_selection() print("Selected text: ", selected) # Copy all terminal content (including scrollback buffer) var all_content = terminal.copy_all() print("All terminal content:\n", all_content) func _on_copy_button_pressed(): var text = terminal.copy_selection() DisplayServer.clipboard_set(text) print("Copied to clipboard: ", text) ``` -------------------------------- ### Terminal Node - Properties Configuration Source: https://context7.com/lihop/godot-xterm/llms.txt Configure terminal properties such as bell behavior, blinking text, scrollback buffer size, and inverse video mode. Write text with various ANSI escape sequences for formatting. ```gdscript extends Control @onready var terminal = $Terminal func _ready(): # Bell configuration terminal.bell_muted = false # Enable bell signal terminal.bell_cooldown = 0.1 # Minimum 100ms between bell signals # Blinking text configuration (for \u001b[5m sequences) terminal.blink_on_time = 0.6 # Visible duration in seconds terminal.blink_off_time = 0.3 # Hidden duration in seconds # Scrollback buffer (terminal history) terminal.max_scrollback = 5000 # Keep 5000 lines of history (default 1000) # Inverse video mode (for \u001b[7m sequences) terminal.inverse_mode = Terminal.INVERSE_MODE_SWAP # Swap fg/bg colors # Or: Terminal.INVERSE_MODE_INVERT # Invert colors (default) # Copy on selection (Linux-style middle-click paste) terminal.copy_on_selection = true # Write text with various attributes terminal.write("\u001b[5mBlinking text\u001b[0m\n") terminal.write("\u001b[7mInverse video\u001b[0m\n") terminal.write("\u001b[1mBold\u001b[0m ") terminal.write("\u001b[3mItalic\u001b[0m ") terminal.write("\u001b[4mUnderline\u001b[0m\n") ``` -------------------------------- ### Connect PTY to a Terminal node Source: https://github.com/lihop/godot-xterm/blob/main/docs/getting_started/pty_node.md Set up a scene with a Terminal and PTY node, linking them via the Terminal Path inspector property. Fork a shell process using the PTY node. ```gdscript extends Terminal @onready var pty = $PTY func _ready(): # terminal_path is already set in inspector to connect signals automatically # Fork a shell process var result = pty.fork() if result != OK: print("Failed to start shell: ", result) ``` -------------------------------- ### Terminal Node - Signal Handling Source: https://context7.com/lihop/godot-xterm/llms.txt Connects to signals for user input (data_sent), bell events, size changes, and key presses. Handles user input by echoing it back and demonstrates responding to bell and size change events. ```gdscript extends Control @onready var terminal = $Terminal func _ready(): # Connect to terminal signals terminal.data_sent.connect(_on_terminal_data_sent) terminal.bell.connect(_on_terminal_bell) terminal.size_changed.connect(_on_terminal_size_changed) terminal.key_pressed.connect(_on_terminal_key_pressed) terminal.write("Type something and press Enter...\n") terminal.grab_focus() func _on_terminal_data_sent(data: PackedByteArray): # User typed something - data contains the key sequence var text = data.get_string_from_utf8() print("User input: ", text) # Echo the input back to terminal terminal.write(text) func _on_terminal_bell(): # Terminal bell was triggered (\u0007 character) print("Bell!") # Play a sound or show visual notification $AudioStreamPlayer.play() func _on_terminal_size_changed(new_size: Vector2i): # Terminal was resized (new_size.x = cols, new_size.y = rows) print("New size: %d cols x %d rows" % [new_size.x, new_size.y]) func _on_terminal_key_pressed(data: PackedByteArray, event: InputEventKey): # Intercept key presses before they're processed if event and event.keycode == KEY_ESCAPE: print("Escape key pressed") ``` -------------------------------- ### GDScript Custom Console Input Handling Source: https://context7.com/lihop/godot-xterm/llms.txt Implement line-by-line input processing, command history navigation, and character input for a custom console. Connects to the terminal's key_pressed signal and handles special keys like Enter, Backspace, Up, and Down arrows. ```gdscript extends Control @onready var terminal = $Terminal var current_line := "" var command_history := [] var history_index := -1 func _ready(): terminal.key_pressed.connect(_on_key_pressed) terminal.grab_focus() show_prompt() func show_prompt(): terminal.write("\u001b[32m> \u001b[0m") func _on_key_pressed(data: PackedByteArray, event: InputEventKey): if not event: return match event.keycode: KEY_ENTER: terminal.write("\r\n") process_command(current_line) if current_line.length() > 0: command_history.push_back(current_line) current_line = "" history_index = -1 show_prompt() KEY_BACKSPACE: if current_line.length() > 0: terminal.write("\b \b") # Move back, clear, move back current_line = current_line.substr(0, current_line.length() - 1) KEY_UP: # Navigate command history up if command_history.size() > 0: if history_index < command_history.size() - 1: history_index += 1 clear_current_line() current_line = command_history[command_history.size() - 1 - history_index] terminal.write(current_line) KEY_DOWN: # Navigate command history down if history_index > 0: history_index -= 1 clear_current_line() current_line = command_history[command_history.size() - 1 - history_index] terminal.write(current_line) elif history_index == 0: history_index = -1 clear_current_line() current_line = "" _: var char_str = char(event.unicode) if event.unicode >= 32: # Printable characters current_line += char_str terminal.write(char_str) func clear_current_line(): # Clear the current input line for i in current_line.length(): terminal.write("\b \b") func process_command(cmd: String): cmd = cmd.strip_edges() if cmd.is_empty(): return match cmd: "help": terminal.write("\u001b[33mAvailable commands: help, clear, echo, quit\u001b[0m\r\n") "clear": terminal.clear() "quit", "exit": get_tree().quit() _: if cmd.begins_with("echo "): terminal.write(cmd.substr(5) + "\r\n") else: terminal.write("\u001b[31mUnknown command: %s\u001b[0m\r\n" % cmd) ```