Skip to content

Commit

Permalink
feat(pty): add initial pty node
Browse files Browse the repository at this point in the history
  • Loading branch information
lihop committed Feb 18, 2024
1 parent 4771a3c commit 9545a52
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 48 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,12 @@ jobs:
matrix:
platform: [linux, windows] # Testing not currently supported on macOS.
bits: [64, 32]
test-type: [headless, rendering]
test-type: [headless, rendering, unix]
exclude:
- platform: windows
test-type: rendering
- platform: windows
test-type: unix
include:
- platform: linux
os: ubuntu-22.04
Expand Down
6 changes: 5 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ install:
{{godot}} --headless -s plug.gd install

test:
{{godot}} --headless -s addons/gut/gut_cmdln.gd -gtest=res://test/test_terminal.gd -gexit
{{godot}} --headless -s addons/gut/gut_cmdln.gd -gtest=res://test/test_terminal.gd,res://test/test_pty.gd -gexit

test-rendering:
{{godot}} --windowed --resolution 400x200 --position 0,0 -s addons/gut/gut_cmdln.gd -gtest=res://test/test_rendering.gd -gopacity=0 -gexit


test-unix:
{{godot}} --headless -s addons/gut/gut_cmdln.gd -gtest=res://test/test_unix.gd -gexit

uninstall:
{{godot}} --headless -s plug.gd uninstall
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
extends Control

const EditorTerminal := preload("./editor_terminal.tscn")
const PTY := preload("../../pty.gd")
const TerminalSettings := preload("./settings/terminal_settings.gd")

const SETTINGS_FILE_PATH := "res://.gdxterm/settings.tres"
Expand Down
22 changes: 22 additions & 0 deletions addons/godot_xterm/native/SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ env.Append(CPPPATH=[
"thirdparty/libtsm/src/tsm",
"thirdparty/libtsm/external",
"thirdparty/libtsm/src/shared",
'thirdparty/libuv/src',
'thirdparty/libuv/include',
])

sources = Glob("src/*.cpp") + Glob("thirdparty/libtsm/src/tsm/*.c")
Expand All @@ -23,6 +25,26 @@ sources.append([
'thirdparty/libtsm/src/shared/shl-htable.c',
])

if env['platform'] == 'web':
env.Append(CPPDEFINES=['_PTY_DISABLED'])
else:
if env['platform'] != 'windows':
env.Append(LIBS=['util', env.File('thirdparty/libuv/build/libuv_a.a')])
else:
if ('MSVC' in env['TOOLS'] or 'msvc' in env['TOOLS']):
if env['target'] == 'template_debug':
env.Append(CCFLAGS=['/MDd'])
elif env['target'] == 'template_release':
env.Append(CCFLAGS=['/MD'])
env.Append(LIBS=[
env.File('thirdparty/libuv/build/{}/uv_a.lib'.format(env["target"].replace("template_", "").capitalize())),
'Advapi32.lib',
'Iphlpapi.lib',
'user32.lib',
'userenv.lib',
'Ws2_32.lib',
])

if env["platform"] == "macos":
library = env.SharedLibrary(
"bin/libgodot-xterm.{}.{}.framework/libgodot-xterm.{}.{}".format(
Expand Down
153 changes: 153 additions & 0 deletions addons/godot_xterm/native/src/pty.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "pty.h"

#include <uv.h>

using namespace godot;

PTY::PTY() {
os = OS::get_singleton();

env["TERM"] = "xterm-256color";
env["COLORTERM"] = "truecolor";

#if defined(__linux__) || defined(__APPLE__)
pty_unix = memnew(PTYUnix);
#endif
}

PTY::~PTY() {
}

int PTY::get_cols() const {
return cols;
}

int PTY::get_rows() const {
return rows;
}

Dictionary PTY::get_env() const {
return env;
}

void PTY::set_env(const Dictionary &value) {
env = value;
}

bool PTY::get_use_os_env() const {
return use_os_env;
}

void PTY::set_use_os_env(const bool value) {
use_os_env = value;
}

Error PTY::fork(const String &file, const PackedStringArray &args, const String &cwd, const int cols, const int rows) {
String fork_file = _get_fork_file(file);
Dictionary fork_env = _get_fork_env();
Array result;

#if defined(__linux__) || defined(__APPLE__)
// TODO: Convert for_env to PackedStringArray.
// TODO: On exit callback.
result = pty_unix->fork(fork_file, 0, args, PackedStringArray(), cwd, cols, rows, 0, 0, true, Callable());
#endif

return static_cast<Error>((int)result[0]);
}

void PTY::kill(const int signum) const {
}

Error PTY::open(const int cols, const int rows) const {
Array result;

#if defined(__linux__) || defined(__APPLE__)
result = pty_unix->open(cols, rows);
#endif

return static_cast<Error>((int)result[0]);
}

void PTY::resize(const int cols, const int rows) const {
}

void PTY::write(const Variant &data) const {
}

void PTY::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_env"), &PTY::get_env);
ClassDB::bind_method(D_METHOD("set_env", "env"), &PTY::set_env);
ClassDB::add_property("PTY", PropertyInfo(Variant::DICTIONARY, "env"), "set_env", "get_env");

ClassDB::bind_method(D_METHOD("get_use_os_env"), &PTY::get_use_os_env);
ClassDB::bind_method(D_METHOD("set_use_os_env", "use_os_env"), &PTY::set_use_os_env);
ClassDB::add_property("PTY", PropertyInfo(Variant::BOOL, "use_os_env"), "set_use_os_env", "get_use_os_env");

ClassDB::bind_method(D_METHOD("fork", "file", "args", "cwd", "cols", "rows"), &PTY::fork, DEFVAL(""), DEFVAL(PackedStringArray()), DEFVAL("."), DEFVAL(80), DEFVAL(24));
ClassDB::bind_method(D_METHOD("open", "cols", "rows"), &PTY::open, DEFVAL(80), DEFVAL(24));
ClassDB::bind_method(D_METHOD("write", "data"), &PTY::write);
}

String PTY::_get_fork_file(const String &file) const {
if (!file.is_empty()) return file;

String shell_env = os->get_environment("SHELL");
if (!shell_env.is_empty()) {
return shell_env;
}

#if defined(__linux__)
return "sh";
#endif
#if defined(__APPLE__)
return "zsh";
#endif
#if defined(_WIN32)
return "cmd.exe";
#endif

return "";
}

Dictionary PTY::_get_fork_env() const {
if (!use_os_env) return env;

#if defined(_PTY_DISABLED)
return env;
#endif

Dictionary os_env;
uv_env_item_t *uv_env;
int count;

uv_os_environ(&uv_env, &count);
for (int i = 0; i < count; i++) {
os_env[uv_env[i].name] = uv_env[i].value;
}
uv_os_free_environ(uv_env, count);

// Make sure we didn't start our server from inside tmux.
os_env.erase("TMUX");
os_env.erase("TMUX_PANE");

// Make sure we didn't start our server from inside screen.
// http://web.mit.edu/gnu/doc/html/screen_20.html
os_env.erase("STY");
os_env.erase("WINDOW");

// Delete some variables that might confuse our terminal.
os_env.erase("WINDOWID");
os_env.erase("TERMCAP");
os_env.erase("COLUMNS");
os_env.erase("LINES");

// Merge in our custom environment.
PackedStringArray keys = PackedStringArray(env.keys());
for (int i = 0; i < keys.size(); i++) {
String key = keys[i];
os_env[key] = env[key];
}

return os_env;
}
80 changes: 80 additions & 0 deletions addons/godot_xterm/native/src/pty.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2021-2024 Leroy Hopson <[email protected]>
// SPDX-License-Identifier: MIT

#pragma once

#if defined(__linux__) || defined(__APPLE__)
#include "pty_unix.h"
#endif

#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/os.hpp>

namespace godot
{
class PTY : public Node
{
GDCLASS(PTY, Node)

public:

enum Signal {
// FIXME: Formatted like this as an apple macro already defines the non underscored version.
SIG_HUP = 1,
SIG_INT = 2,
SIG_QUIT = 3,
SIG_ILL = 4,
SIG_TRAP = 5,
SIG_ABRT = 6,
SIG_BUS = 7,
SIG_FPE = 8,
SIG_KILL = 9,
SIG_USR1 = 10,
SIG_SEGV = 11,
SIG_USR2 = 12,
SIG_PIPE = 13,
SIG_ALRM = 14,
SIG_TERM = 15,
};

PTY();
~PTY();

int get_cols() const;
int get_rows() const;

Dictionary get_env() const;
void set_env(const Dictionary &value);

bool get_use_os_env() const;
void set_use_os_env(const bool value);

Error fork(const String &file = "", const PackedStringArray &args = PackedStringArray(), const String &cwd = ".", const int cols = 80, const int rows = 24);
void kill(const int signum = Signal::SIGHUP) const;
Error open(const int cols = 80, const int rows = 24) const;
void resize(const int cols, const int rows) const;
void resizev(const Vector2i &size) const { resize(size.x, size.y); };
void write(const Variant &data) const;

protected:
static void _bind_methods();

private:
OS *os;

unsigned int cols = 0;
unsigned int rows = 0;

Dictionary env = Dictionary();
bool use_os_env = true;

String _get_fork_file(const String &file) const;
Dictionary _get_fork_env() const;

#if defined(__linux__) || defined(__APPLE__)
PTYUnix *pty_unix;
#endif
};
} // namespace godot

VARIANT_ENUM_CAST(PTY::Signal);
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
* Copyright (c) 2017, Daniel Imms (MIT License)
* Copyright (c) 2021, Leroy Hopson (MIT License)
* Copyright (c) 2021, 2024 Leroy Hopson (MIT License)
*
* SPDX-License-Identifier: MIT
*
* pty.cc:
* This file is responsible for starting processes
Expand All @@ -18,8 +20,9 @@
* Includes
*/

#include "pty.h"
#include "libuv_utils.h"
#if !defined(_WIN32) && !defined(_PTY_DISABLED)

#include "pty_unix.h"
#include <godot_cpp/variant/callable.hpp>
#include <uv.h>

Expand All @@ -29,7 +32,6 @@
#include <unistd.h>

#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
Expand Down Expand Up @@ -339,14 +341,15 @@ Error PTYUnix::resize(int p_fd, int p_cols, int p_rows) {

if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
switch (errno) {
case EBADF:
RETURN_UV_ERR(UV_EBADF)
case EFAULT:
RETURN_UV_ERR(UV_EFAULT)
case EINVAL:
RETURN_UV_ERR(UV_EINVAL);
case ENOTTY:
RETURN_UV_ERR(UV_ENOTTY);
// TODO: Fixme!
//case EBADF:
// RETURN_UV_ERR(UV_EBADF)
//case EFAULT:
// RETURN_UV_ERR(UV_EFAULT)
//case EINVAL:
// RETURN_UV_ERR(UV_EINVAL);
//case ENOTTY:
// RETURN_UV_ERR(UV_ENOTTY);
}
ERR_PRINT("ioctl(2) failed");
return FAILED;
Expand Down Expand Up @@ -673,3 +676,5 @@ void PTYUnix::_bind_methods() {
}

void PTYUnix::_init() {}

#endif
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson <godot-xterm@leroy.geek.nz>
// SPDX-FileCopyrightText: 2021-2022, 2024 Leroy Hopson <godot-xterm@leroy.nix.nz>
// SPDX-License-Identifier: MIT

#ifndef GODOT_XTERM_PTY_H
#define GODOT_XTERM_PTY_H
#pragma once

#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/variant/callable.hpp>
Expand Down Expand Up @@ -30,5 +29,3 @@ class PTYUnix : public RefCounted {
};

} // namespace godot

#endif // GODOT_XTERM_PTY_H
Loading

0 comments on commit 9545a52

Please sign in to comment.