2022-06-29 17:58:48 +02:00
|
|
|
# Derived from https://github.com/microsoft/node-pty/blob/main/src/terminal.ts
|
2021-07-02 19:27:34 +02:00
|
|
|
# Copyright (c) 2012-2015, Christopher Jeffrey (MIT License).
|
|
|
|
# Copyright (c) 2016, Daniel Imms (MIT License).
|
|
|
|
# Copyright (c) 2018, Microsoft Corporation (MIT License).
|
2022-06-29 17:58:48 +02:00
|
|
|
# Copyright (c) 2021-2022, Leroy Hopson (MIT License).
|
2022-11-09 21:57:46 +01:00
|
|
|
@tool
|
2022-06-29 17:58:48 +02:00
|
|
|
extends Node
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
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")
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
const DEFAULT_NAME := "xterm-256color"
|
|
|
|
const DEFAULT_COLS := 80
|
|
|
|
const DEFAULT_ROWS := 24
|
|
|
|
const DEFAULT_ENV := {TERM = DEFAULT_NAME, COLORTERM = "truecolor"}
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# 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
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
signal data_received(data)
|
|
|
|
signal exited(exit_code, signum)
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-11-09 23:29:11 +01:00
|
|
|
@export var terminal_path: NodePath = NodePath() :
|
2022-11-09 21:57:46 +01:00
|
|
|
get:
|
2022-11-09 23:29:11 +01:00
|
|
|
return terminal_path
|
|
|
|
set(value):
|
|
|
|
terminal_path = value
|
|
|
|
_set_terminal(get_node_or_null(terminal_path))
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-11-09 21:57:46 +01:00
|
|
|
var _terminal: _Terminal = null :
|
|
|
|
get:
|
2022-11-09 22:52:54 +01:00
|
|
|
return _terminal # TODOConverter40 Non existent get function
|
2022-11-09 21:57:46 +01:00
|
|
|
set(mod_value):
|
|
|
|
mod_value # TODOConverter40 Copy here content of _set_terminal
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# The column size in characters.
|
2022-11-09 23:29:11 +01:00
|
|
|
@export var cols: int = DEFAULT_COLS :
|
2022-11-09 21:57:46 +01:00
|
|
|
get:
|
|
|
|
return cols # TODOConverter40 Copy here content of get_cols
|
|
|
|
set(mod_value):
|
|
|
|
mod_value # TODOConverter40 Copy here content of set_cols
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# The row size in characters.
|
2022-11-09 23:29:11 +01:00
|
|
|
@export var rows: int = DEFAULT_ROWS :
|
2022-11-09 21:57:46 +01:00
|
|
|
get:
|
|
|
|
return rows # TODOConverter40 Copy here content of get_rows
|
|
|
|
set(mod_value):
|
|
|
|
mod_value # TODOConverter40 Copy here content of set_rows
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# Environment to be set for the child program.
|
2022-11-09 23:29:11 +01:00
|
|
|
@export var env: Dictionary = DEFAULT_ENV
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# 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.
|
2022-11-09 23:29:11 +01:00
|
|
|
@export var use_os_env: bool = true
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-08-12 00:24:54 +02:00
|
|
|
var _cols := DEFAULT_COLS
|
|
|
|
var _rows := DEFAULT_ROWS
|
2022-06-29 17:58:48 +02:00
|
|
|
var _pty_native: _PTYNative
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func _init():
|
|
|
|
var os_name := OS.get_name()
|
|
|
|
match os_name:
|
|
|
|
"X11", "Server", "OSX":
|
|
|
|
_pty_native = _PTYUnix.new()
|
|
|
|
_:
|
2022-11-09 22:52:54 +01:00
|
|
|
push_error("PTY is not support on current platform (%s)." % os_name)
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-11-09 21:57:46 +01:00
|
|
|
_pty_native.connect("data_received",Callable(self,"_on_pty_native_data_received"))
|
|
|
|
_pty_native.connect("exited",Callable(self,"_on_pty_native_exited"))
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
add_child(_pty_native)
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-08-12 00:24:54 +02:00
|
|
|
func _ready():
|
|
|
|
if terminal_path and not _terminal:
|
|
|
|
set_terminal_path(terminal_path)
|
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func set_cols(value: int):
|
2022-08-12 00:24:54 +02:00
|
|
|
resize(value, _rows)
|
|
|
|
|
|
|
|
|
|
|
|
func get_cols() -> int:
|
|
|
|
return _cols
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func set_rows(value: int):
|
2022-08-12 00:24:54 +02:00
|
|
|
resize(_cols, value)
|
|
|
|
|
|
|
|
|
|
|
|
func get_rows() -> int:
|
|
|
|
return _rows
|
2022-11-09 23:29:11 +01:00
|
|
|
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func _set_terminal(value: _Terminal):
|
|
|
|
if _terminal == value:
|
|
|
|
return
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# Disconect the current terminal, if any.
|
|
|
|
if _terminal:
|
2022-11-09 21:57:46 +01:00
|
|
|
disconnect("data_received",Callable(_terminal,"write"))
|
|
|
|
_terminal.disconnect("data_sent",Callable(self,"write"))
|
|
|
|
_terminal.disconnect("size_changed",Callable(self,"resizev"))
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
_terminal = value
|
2021-07-03 15:54:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
if not _terminal:
|
|
|
|
return
|
2021-07-03 15:54:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# Connect the new terminal.
|
2022-08-15 00:35:01 +02:00
|
|
|
resize(_terminal.get_cols(), _terminal.get_rows())
|
2022-11-09 21:57:46 +01:00
|
|
|
if not _terminal.is_connected("size_changed",Callable(self,"resizev")):
|
|
|
|
_terminal.connect("size_changed",Callable(self,"resizev"))
|
|
|
|
if not _terminal.is_connected("data_sent",Callable(self,"write")):
|
|
|
|
_terminal.connect("data_sent",Callable(self,"write"))
|
|
|
|
if not is_connected("data_received",Callable(_terminal,"write")):
|
|
|
|
connect("data_received",Callable(_terminal,"write"))
|
2021-07-22 07:17:23 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# 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.
|
2022-08-12 00:24:54 +02:00
|
|
|
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)
|
2022-06-29 17:58:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
# Same as resize() but takes a Vector2.
|
|
|
|
func resizev(size: Vector2) -> void:
|
2022-08-12 00:24:54 +02:00
|
|
|
resize(int(size.x), int(size.y))
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
# Kill the pty.
|
|
|
|
# sigint: The signal to send. By default this is SIGHUP.
|
2022-11-09 22:52:54 +01:00
|
|
|
# This is not supported on Windows.
|
2022-06-29 17:58:48 +02:00
|
|
|
func kill(signum: int = Signal.SIGHUP) -> void:
|
|
|
|
_pty_native.kill(signum)
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func _notification(what: int):
|
|
|
|
match what:
|
|
|
|
NOTIFICATION_PARENTED:
|
|
|
|
var parent = get_parent()
|
|
|
|
if parent is _Terminal:
|
|
|
|
set_terminal_path(get_path_to(parent))
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func fork(
|
|
|
|
file: String = OS.get_environment("SHELL"),
|
2022-11-09 21:57:46 +01:00
|
|
|
args: PackedStringArray = PackedStringArray(),
|
2022-06-29 17:58:48 +02:00
|
|
|
cwd = _LibuvUtils.get_cwd(),
|
2022-08-12 00:24:54 +02:00
|
|
|
cols: int = _cols,
|
|
|
|
rows: int = _rows,
|
2022-06-29 17:58:48 +02:00
|
|
|
uid: int = -1,
|
|
|
|
gid: int = -1,
|
|
|
|
utf8 = true
|
|
|
|
) -> int:
|
2022-08-12 00:24:54 +02:00
|
|
|
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)
|
2022-06-29 17:58:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array:
|
|
|
|
return _pty_native.open(cols, rows)
|
|
|
|
|
|
|
|
|
|
|
|
func _on_pty_native_data_received(data):
|
|
|
|
emit_signal("data_received", data)
|
|
|
|
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-06-29 17:58:48 +02:00
|
|
|
func _on_pty_native_exited(exit_code: int, signum: int) -> void:
|
|
|
|
emit_signal("exited", exit_code, signum)
|
2022-08-12 00:24:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
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
|