Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of GodotXterm.

PTY node

A PTY (pseudoterminal) spawns and controls actual shell processes like bash, cmd, or any command-line program. Unlike OS.execute(), which runs a command and gives you the final output, PTY gives you real-time streaming output and lets you send input while the process runs.

Note

PTY is supported on Linux, macOS, and Windows, but not Web. Windows support uses ConPTY which requires Windows 10 version 1903 (May 2019) or later.

Overview

PTY Diagram ”PTY Diagram” is a derivative of Termios-script-diagram.svg by Krishnavedala, used under CC0.

  1. Fork a process

    When fork() is called, the PTY creates a new shell process (for example bash in the diagram above). The OS also creates a pseudoterminal pair: a master side (connected to PTY) and a slave side (connected to the shell). The shell process treats the slave side as a real terminal.

  2. Write input

    Data is sent via write() (e.g., pty.write("ls -al\n")). This travels from PTY → pseudoterminal master → pseudoterminal slave → shell process. The shell receives the command as if it were typed on a keyboard.

  3. Read output

    The shell executes the command and writes output to its “terminal” (the slave side). This flows back: shell → pseudoterminal slave → pseudoterminal master → PTY. The data_received signal fires with the output data.

  4. Ongoing communication

    Steps (2) and (3) repeat for the lifetime of the process. Commands can be sent, output received, and special signals like ETX (Ctrl + C) can be sent to interrupt running programs.

Basic usage

PTY can be used to run one-off commands. Output will be emitted via the data_received signal. When the command completes, PTY emits the exited signal with the exit code.

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)

For long-running programs that produce continuous output, PTY provides real-time streaming. The kill method can be used to control or terminate the running process.

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)

Connecting a Terminal

To create a fully functional terminal, connect the PTY to a Terminal node:

  1. Add a Terminal node to your scene.

  2. Add a PTY node to your scene.

  3. In the PTY’s inspector, set the Terminal Path property to point to the Terminal node (e.g., "." if PTY is a child of the Terminal).

  4. Fork a process in code.

Scene tree with PTY child node attached to a Terminal node

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)

Note

If no args are provided to fork(), then PTY will default to using the program specified by the SHELL environment variable. If this environment variable is not set, then it will fall back to sh on Linux and macOS, and powershell on Windows.

Next steps