godot-xterm/addons/godot_xterm/pty.gd
Leroy Hopson 41525959e1
Deprecate undocumented get_master() method
This method was never officially documented and used only as a workaround for
issue #53. It also returns an instance of the undocumented and scruffily
implemented Pipe class that I would prefer to keep internal.

Now that #53 has been fixed, this method can be removed from the unofficial
public API, but deprecate it just in case. If someone was using it then it
is still possible (although not supported) to access the `_pipe` property of
`_pty_native`.
2022-08-22 10:05:41 +12:00

191 lines
5.2 KiB
GDScript

# Derived from https://github.com/microsoft/node-pty/blob/main/src/terminal.ts
# Copyright (c) 2012-2015, Christopher Jeffrey (MIT License).
# Copyright (c) 2016, Daniel Imms (MIT License).
# Copyright (c) 2018, Microsoft Corporation (MIT License).
# Copyright (c) 2021-2022, Leroy Hopson (MIT License).
tool
extends Node
const _LibuvUtils := preload("./nodes/pty/libuv_utils.gd")
const _PTYNative := preload("./nodes/pty/pty_native.gd")
const _PTYUnix := preload("./nodes/pty/unix/pty_unix.gd")
const _Terminal := preload("./terminal.gd")
const DEFAULT_NAME := "xterm-256color"
const DEFAULT_COLS := 80
const DEFAULT_ROWS := 24
const DEFAULT_ENV := {TERM = DEFAULT_NAME, COLORTERM = "truecolor"}
# Any signal_number can be sent to the pty's process using the kill() function,
# these are just the signals with numbers specified in the POSIX standard.
const Signal = _PTYUnix.Signal
signal data_received(data)
signal exited(exit_code, signum)
export(NodePath) var terminal_path := NodePath() setget set_terminal_path
var _terminal: _Terminal = null setget _set_terminal
# The column size in characters.
export(int) var cols: int = DEFAULT_COLS setget set_cols, get_cols
# The row size in characters.
export(int) var rows: int = DEFAULT_ROWS setget set_rows, get_rows
# Environment to be set for the child program.
export(Dictionary) var env := DEFAULT_ENV
# If true the environment variables in the env Dictionary will be merged with
# the environment variables of the operating system (e.g. printenv), with the
# former taking precedence in the case of conflicts.
export(bool) var use_os_env := true
var _cols := DEFAULT_COLS
var _rows := DEFAULT_ROWS
var _pty_native: _PTYNative
func _init():
var os_name := OS.get_name()
match os_name:
"X11", "Server", "OSX":
_pty_native = _PTYUnix.new()
_:
push_error("PTY is not support on current platform (%s)." % os_name)
_pty_native.connect("data_received", self, "_on_pty_native_data_received")
_pty_native.connect("exited", self, "_on_pty_native_exited")
add_child(_pty_native)
func _ready():
if terminal_path and not _terminal:
set_terminal_path(terminal_path)
func set_cols(value: int):
resize(value, _rows)
func get_cols() -> int:
return _cols
func set_rows(value: int):
resize(_cols, value)
func get_rows() -> int:
return _rows
func set_terminal_path(value := NodePath()):
terminal_path = value
_set_terminal(get_node_or_null(terminal_path))
func _set_terminal(value: _Terminal):
if _terminal == value:
return
# Disconect the current terminal, if any.
if _terminal:
disconnect("data_received", _terminal, "write")
_terminal.disconnect("data_sent", self, "write")
_terminal.disconnect("size_changed", self, "resizev")
_terminal = value
if not _terminal:
return
# Connect the new terminal.
resize(_terminal.get_cols(), _terminal.get_rows())
if not _terminal.is_connected("size_changed", self, "resizev"):
_terminal.connect("size_changed", self, "resizev")
if not _terminal.is_connected("data_sent", self, "write"):
_terminal.connect("data_sent", self, "write")
if not is_connected("data_received", _terminal, "write"):
connect("data_received", _terminal, "write")
# Writes data to the socket.
# data: The data to write.
func write(data) -> void:
_pty_native.write(data)
# Resizes the dimensions of the pty.
# cols: The number of columns.
# rows: The number of rows.
func resize(cols = _cols, rows = _rows) -> void:
if not _valid_size(cols, rows):
push_error("Size of cols/rows must be a positive integer.")
return
_cols = cols
_rows = rows
_pty_native.resize(_cols, _rows)
# Same as resize() but takes a Vector2.
func resizev(size: Vector2) -> void:
resize(int(size.x), int(size.y))
# Kill the pty.
# sigint: The signal to send. By default this is SIGHUP.
# This is not supported on Windows.
func kill(signum: int = Signal.SIGHUP) -> void:
_pty_native.kill(signum)
func _notification(what: int):
match what:
NOTIFICATION_PARENTED:
var parent = get_parent()
if parent is _Terminal:
set_terminal_path(get_path_to(parent))
func fork(
file: String = OS.get_environment("SHELL"),
args: PoolStringArray = PoolStringArray(),
cwd = _LibuvUtils.get_cwd(),
cols: int = _cols,
rows: int = _rows,
uid: int = -1,
gid: int = -1,
utf8 = true
) -> int:
resize(cols, rows) # Ensures error message is printed if cols/rows are invalid.
if not _valid_size(cols, rows):
return ERR_INVALID_PARAMETER
return _pty_native.fork(file, args, cwd, _cols, _rows, uid, gid, utf8)
func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array:
return _pty_native.open(cols, rows)
func get_master():
push_warning(
"The method get_master() is deprecated and will be removed in a future version. If you really need to get the underlying Pipe of the pty, then you can access '_pty_native._pipe', but this is not supported and may change at any time."
)
return _pty_native.get_master()
func _on_pty_native_data_received(data):
emit_signal("data_received", data)
func _on_pty_native_exited(exit_code: int, signum: int) -> void:
emit_signal("exited", exit_code, signum)
static func _valid_size(cols: int, rows: int) -> bool:
return cols > 0 and rows > 0 and cols != NAN and rows != NAN and cols != INF and rows != INF