From 44f7e3801c78312c17ac66f8fe5ab7dc1fe2d30c Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Thu, 29 Dec 2022 22:52:13 +1300 Subject: [PATCH] Convert from GDNative to GDExtension Work in progress. --- addons/godot_xterm/.gitignore | 3 + addons/godot_xterm/godot_xterm.gdextension | 6 + addons/godot_xterm/native/SConstruct | 40 +++-- addons/godot_xterm/native/build.sh | 22 ++- addons/godot_xterm/native/src/libuv_utils.cpp | 62 +++---- addons/godot_xterm/native/src/libuv_utils.h | 25 ++- .../native/src/node_pty/unix/pty.cc | 66 +++---- .../native/src/node_pty/unix/pty.h | 21 ++- .../native/src/node_pty/win/conpty.h | 17 +- addons/godot_xterm/native/src/pipe.cpp | 45 ++--- addons/godot_xterm/native/src/pipe.h | 22 +-- .../godot_xterm/native/src/register_types.cpp | 60 +++++++ .../godot_xterm/native/src/register_types.h | 11 ++ addons/godot_xterm/native/src/terminal.cpp | 164 ++++++++---------- addons/godot_xterm/native/src/terminal.h | 32 ++-- .../godot_xterm/native/thirdparty/godot-cpp | 2 +- addons/godot_xterm/nodes/pty/libuv_utils.gd | 4 +- addons/godot_xterm/nodes/pty/unix/pty_unix.gd | 26 +-- .../godot_xterm/nodes/terminal/viewport.tscn | 9 +- addons/godot_xterm/pty.gd | 10 +- addons/godot_xterm/terminal.gd | 21 +-- examples/menu/menu.gd | 14 +- examples/menu/menu.tscn | 10 +- project.godot | 4 - shell.nix | 16 ++ 25 files changed, 408 insertions(+), 304 deletions(-) create mode 100644 addons/godot_xterm/godot_xterm.gdextension create mode 100644 addons/godot_xterm/native/src/register_types.cpp create mode 100644 addons/godot_xterm/native/src/register_types.h create mode 100644 shell.nix diff --git a/addons/godot_xterm/.gitignore b/addons/godot_xterm/.gitignore index e2bd516..6a73673 100644 --- a/addons/godot_xterm/.gitignore +++ b/addons/godot_xterm/.gitignore @@ -346,6 +346,9 @@ platform/windows/godot_res.res # Scons construction environment dump .scons_env.json +# Scons cache +.cache + # Scons progress indicator .scons_node_count diff --git a/addons/godot_xterm/godot_xterm.gdextension b/addons/godot_xterm/godot_xterm.gdextension new file mode 100644 index 0000000..07bade2 --- /dev/null +++ b/addons/godot_xterm/godot_xterm.gdextension @@ -0,0 +1,6 @@ +[configuration] + +entry_symbol = "godot_xterm_library_init" + +[libraries] +linux.debug.x86_64 = "res://addons/godot_xterm/native/bin/libgodot-xterm.linux.64.so" diff --git a/addons/godot_xterm/native/SConstruct b/addons/godot_xterm/native/SConstruct index a15a194..d66d77d 100644 --- a/addons/godot_xterm/native/SConstruct +++ b/addons/godot_xterm/native/SConstruct @@ -14,6 +14,8 @@ import os import sys import subprocess +EnsureSConsVersion(4, 0) + # Try to detect the host platform automatically. # This is used if no `platform` argument is passed. if sys.platform.startswith('linux'): @@ -58,8 +60,8 @@ opts.Add(EnumVariable( opts.Add(EnumVariable( 'target', 'Compilation target', - 'debug', - allowed_values=('debug', 'release'), + 'editor', + allowed_values=('editor', 'release'), ignorecase=2 )) opts.Add(EnumVariable("macos_arch", "Target macOS architecture", @@ -73,6 +75,15 @@ opts.Add(BoolVariable( opts.Update(env) Help(opts.GenerateHelpText(env)) +# Cache if SCONS_CACHE env var set. +scons_cache_path = os.environ.get("SCONS_CACHE") +if scons_cache_path is not None: + CacheDir(scons_cache_path) + Decider("MD5") +print("caching to " + scons_cache_path) +exit + + # Allows 32bit builds on windows 64bit. if env['platform'] == 'windows': if env['bits'] == '64': @@ -96,7 +107,7 @@ if env['platform'] == 'linux': env.Append(CCFLAGS=['-fPIC', '-Wwrite-strings']) env.Append(LINKFLAGS=["-Wl,-R'$$ORIGIN'", '-static-libstdc++']) - if env['target'] == 'debug': + if env['target'] == 'editor': env.Append(CCFLAGS=['-Og', '-g']) elif env['target'] == 'release': env.Append(CCFLAGS=['-O3']) @@ -153,7 +164,7 @@ elif env['platform'] == 'osx': env.Append(LINKFLAGS=['-Wl,-undefined,dynamic_lookup']) - if env['target'] == 'debug': + if env['target'] == 'editor': env.Append(CCFLAGS=['-Og', '-g']) elif env['target'] == 'release': env.Append(CCFLAGS=['-O3']) @@ -166,14 +177,14 @@ elif env['platform'] == 'windows': # On Windows using MSVC. if host_platform == 'windows': - if env['target'] == 'debug': + if env['target'] == 'editor': env.Append(CCFLAGS=['/Z7', '/Od', '/EHsc', '/D_DEBUG', '/MDd']) elif env['target'] == 'release': env.Append(CCFLAGS=['/O2', '/EHsc', '/DNDEBUG', '/MD']) # On Windows, Linux, or MacOS using MinGW. elif host_platform == 'linux' or host_platform == 'osx': - env.Append(CCFLAGS=['-std=c++14', '-Wwrite-strings']) + env.Append(CCFLAGS=['-std=c++17', '-Wwrite-strings']) env.Append(LINKFLAGS=[ '--static', '-Wl,--no-undefined', @@ -181,7 +192,7 @@ elif env['platform'] == 'windows': '-static-libstdc++', ]) - if env['target'] == 'debug': + if env['target'] == 'editor': env.Append(CCFLAGS=['-Og', '-g']) elif env['target'] == 'release': env.Append(CCFLAGS=['-O3']) @@ -231,16 +242,15 @@ Default(libtsm) # Build libgodot-xterm. if env['platform'] != 'windows': - env.Append(CXXFLAGS=['-std=c++14']) + env.Append(CXXFLAGS=['-std=c++17']) env.Append(CPPPATH=[ 'src/', 'thirdparty/libtsm/build/src/tsm', 'thirdparty/libtsm/build/src/shared', - 'thirdparty/godot-cpp/include/', - 'thirdparty/godot-cpp/include/core/', - 'thirdparty/godot-cpp/include/gen/', - 'thirdparty/godot-cpp/godot-headers/', + 'thirdparty/godot-cpp/gdextension', + 'thirdparty/godot-cpp/include', + 'thirdparty/godot-cpp/gen/include', 'thirdparty/libuv/src', 'thirdparty/libuv/include' ]) @@ -254,7 +264,7 @@ env.Append(LIBS=[ env['platform'], env['target'], 'wasm' if env['platform'] == 'javascript' else env['macos_arch'] if ( - env['macos_arch'] != 'universal' and env['platform'] == 'osx') else env['bits'], + env['macos_arch'] != 'universal' and env['platform'] == 'osx') else 'x86_64', # FIXME use correct arch. env['LIBSUFFIX'], )), env.File('thirdparty/libtsm/build/bin/libtsm.{}.{}.{}{}'.format( @@ -266,7 +276,7 @@ env.Append(LIBS=[ ]) sources = [] -sources.append('src/libgodotxtermnative.cpp') +sources.append('src/register_types.cpp') sources.append('src/terminal.cpp') @@ -280,7 +290,7 @@ else: sources.append('src/node_pty/unix/pty.cc') env.Append(LIBS=['util', env.File('thirdparty/libuv/build/libuv_a.a')]) else: - #sources.append('src/node_pty/win/conpty.cc') + sources.append('src/node_pty/win/conpty.cc') env.Append(LIBS=[ env.File('thirdparty/libuv/build/{}/uv_a.lib'.format(env["target"].capitalize())), 'Advapi32.lib', diff --git a/addons/godot_xterm/native/build.sh b/addons/godot_xterm/native/build.sh index 5875e07..be1680e 100755 --- a/addons/godot_xterm/native/build.sh +++ b/addons/godot_xterm/native/build.sh @@ -16,14 +16,14 @@ while [[ $# -gt 0 ]]; do shift ;; *) - echo "Usage: ./build.sh [-t|--target ] [--disable_pty]"; + echo "Usage: ./build.sh [-t|--target ] [--disable_pty]"; exit 128 shift ;; esac done # Set defaults. -target=${target:-debug} +target=${target:-editor} disable_pty=${disable_pty:-no} nproc=$(nproc || sysctl -n hw.ncpu) @@ -31,6 +31,9 @@ nproc=$(nproc || sysctl -n hw.ncpu) #GODOT_DIR Get the absolute path to the directory this script is in. NATIVE_DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +# Cache built files. +export SCONS_CACHE=${NATIVE_DIR}/.cache + # Run script inside a nix shell if it is available. if command -v nix-shell && [ $NIX_PATH ] && [ -z $IN_NIX_SHELL ]; then @@ -56,9 +59,10 @@ updateSubmodules GODOT_CPP_DIR ${NATIVE_DIR}/thirdparty/godot-cpp # Build godot-cpp bindings. -cd ${GODOT_CPP_DIR} -echo "scons generate_bindings=yes target=$target -j$nproc" -scons generate_bindings=yes macos_arch=$(uname -m) target=$target -j$nproc +# FIXME: Commented out to improve build time, but needs to be uncommented and run for initial build of godot-cpp. +#cd ${GODOT_CPP_DIR} +#echo "scons generate_bindings=yes target=$target -j$nproc" +#scons generate_bindings=yes macos_arch=$(uname -m) target=$target -j$nproc # Build libuv as a static library. cd ${LIBUV_DIR} @@ -80,7 +84,7 @@ cd ${NATIVE_DIR} scons target=$target macos_arch=$(uname -m) disable_pty=$disable_pty -j$nproc # Use Docker to build libgodot-xterm javascript. -if [ -x "$(command -v docker-compose)" ]; then - UID_GID="0:0" TARGET=$target docker-compose build javascript - UID_GID="$(id -u):$(id -g)" TARGET=$target docker-compose run --rm javascript -fi +#if [ -x "$(command -v docker-compose)" ]; then +# UID_GID="0:0" TARGET=$target docker-compose build javascript +# UID_GID="$(id -u):$(id -g)" TARGET=$target docker-compose run --rm javascript +#fi diff --git a/addons/godot_xterm/native/src/libuv_utils.cpp b/addons/godot_xterm/native/src/libuv_utils.cpp index 656978c..6827af0 100644 --- a/addons/godot_xterm/native/src/libuv_utils.cpp +++ b/addons/godot_xterm/native/src/libuv_utils.cpp @@ -1,23 +1,23 @@ +// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson +// SPDX-License-Identifier: MIT + #include "libuv_utils.h" +#include #include using namespace godot; -void LibuvUtils::_register_methods() { - register_method("_init", &LibuvUtils::_init); +void LibuvUtils::_bind_methods() { + ClassDB::bind_static_method("LibuvUtils", D_METHOD("get_os_environ"), &LibuvUtils::get_os_environ); + ClassDB::bind_static_method("LibuvUtils", D_METHOD("get_os_release"), &LibuvUtils::get_os_release); + ClassDB::bind_static_method("LibuvUtils", D_METHOD("get_cwd"), &LibuvUtils::get_cwd); - register_method("get_os_environ", &LibuvUtils::get_os_environ); - register_method("get_os_release", &LibuvUtils::get_os_release); - register_method("get_cwd", &LibuvUtils::get_cwd); - - register_method("kill", &LibuvUtils::kill); + ClassDB::bind_static_method("LibuvUtils", D_METHOD("kill", "pid", "signum"), &LibuvUtils::kill); } LibuvUtils::LibuvUtils() {} LibuvUtils::~LibuvUtils() {} -void LibuvUtils::_init() {} - Dictionary LibuvUtils::get_os_environ() { Dictionary result; @@ -63,50 +63,50 @@ String LibuvUtils::get_cwd() { return result; } -godot_error LibuvUtils::kill(int pid, int signum) { +Error LibuvUtils::kill(int pid, int signum) { RETURN_UV_ERR(uv_kill(pid, signum)); } -godot_error LibuvUtils::translate_uv_errno(int uv_err) { +Error LibuvUtils::translate_uv_errno(int uv_err) { if (uv_err >= 0) - return GODOT_OK; + return OK; // Rough translation of libuv error to godot error. // Not necessarily accurate. switch (uv_err) { case UV_EEXIST: // file already exists - return GODOT_ERR_ALREADY_EXISTS; + return ERR_ALREADY_EXISTS; case UV_EADDRINUSE: // address already in use - return GODOT_ERR_ALREADY_IN_USE; + return ERR_ALREADY_IN_USE; case UV_EBUSY: // resource busy or locked case UV_ETXTBSY: // text file is busy - return GODOT_ERR_BUSY; + return ERR_BUSY; case UV_ECONNREFUSED: // connection refused - return GODOT_ERR_CANT_CONNECT; + return ERR_CANT_CONNECT; case UV_ECONNABORTED: // software caused connection abort case UV_ECONNRESET: // connection reset by peer case UV_EISCONN: // socket is already connected case UV_ENOTCONN: // socket is not connected - return GODOT_ERR_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; case UV_ENODEV: // no such device case UV_ENXIO: // no such device or address case UV_ESRCH: // no such process - return GODOT_ERR_DOES_NOT_EXIST; + return ERR_DOES_NOT_EXIST; case UV_EROFS: // read-only file system - return GODOT_ERR_FILE_CANT_WRITE; + return ERR_FILE_CANT_WRITE; case UV_EOF: // end of file - return GODOT_ERR_FILE_EOF; + return ERR_FILE_EOF; case UV_ENOENT: // no such file or directory - return GODOT_ERR_FILE_NOT_FOUND; + return ERR_FILE_NOT_FOUND; case UV_EAI_BADFLAGS: // bad ai_flags value case UV_EAI_BADHINTS: // invalid value for hints @@ -115,13 +115,13 @@ godot_error LibuvUtils::translate_uv_errno(int uv_err) { case UV_EINVAL: // invalid argument case UV_ENOTTY: // inappropriate ioctl for device case UV_EPROTOTYPE: // protocol wrong type for socket - return GODOT_ERR_INVALID_PARAMETER; // Parameter passed is invalid + return ERR_INVALID_PARAMETER; // Parameter passed is invalid case UV_ENOSYS: // function not implemented - return GODOT_ERR_METHOD_NOT_FOUND; + return ERR_METHOD_NOT_FOUND; case UV_EAI_MEMORY: // out of memory - return GODOT_ERR_OUT_OF_MEMORY; + return ERR_OUT_OF_MEMORY; case UV_E2BIG: // argument list too long case UV_EFBIG: // file too large @@ -129,15 +129,15 @@ godot_error LibuvUtils::translate_uv_errno(int uv_err) { case UV_ENAMETOOLONG: // name too long case UV_EOVERFLOW: // value too large for defined data type case UV_ERANGE: // result too large - return GODOT_ERR_PARAMETER_RANGE_ERROR; // Parameter given out of range + return ERR_PARAMETER_RANGE_ERROR; // Parameter given out of range case UV_ETIMEDOUT: - return GODOT_ERR_TIMEOUT; // connection timed out + return ERR_TIMEOUT; // connection timed out case UV_EACCES: // permission denied case UV_EPERM: // operation not permitted case UV_EXDEV: // cross-device link not permitted - return GODOT_ERR_UNAUTHORIZED; + return ERR_UNAUTHORIZED; case UV_EADDRNOTAVAIL: // address not available case UV_EAFNOSUPPORT: // address family not supported @@ -150,12 +150,12 @@ godot_error LibuvUtils::translate_uv_errno(int uv_err) { case UV_ENOTSUP: // operation not supported on socket case UV_EPROTONOSUPPORT: // protocol not supported case UV_ESOCKTNOSUPPORT: // socket type not supported - return GODOT_ERR_UNAVAILABLE; // What is requested is + return ERR_UNAVAILABLE; // What is requested is // unsupported/unavailable case UV_EAI_NODATA: // no address case UV_EDESTADDRREQ: // destination address required - return GODOT_ERR_UNCONFIGURED; + return ERR_UNCONFIGURED; case UV_EAI_AGAIN: // temporary failure case UV_EAI_CANCELED: // request canceled @@ -191,6 +191,6 @@ godot_error LibuvUtils::translate_uv_errno(int uv_err) { case UV_ESPIPE: // invalid seek case UV_UNKNOWN: // unknown error default: - return GODOT_FAILED; // Generic fail error + return FAILED; // Generic fail error } -} \ No newline at end of file +} diff --git a/addons/godot_xterm/native/src/libuv_utils.h b/addons/godot_xterm/native/src/libuv_utils.h index 37f91f5..b15703c 100644 --- a/addons/godot_xterm/native/src/libuv_utils.h +++ b/addons/godot_xterm/native/src/libuv_utils.h @@ -1,7 +1,7 @@ #ifndef GODOT_XTERM_UV_UTILS_H #define GODOT_XTERM_UV_UTILS_H -#include +#include #include #define UV_ERR_PRINT(uv_err) \ @@ -21,27 +21,26 @@ namespace godot { -class LibuvUtils : public Reference { - GODOT_CLASS(LibuvUtils, Reference) +class LibuvUtils : public RefCounted { + GDCLASS(LibuvUtils, RefCounted) public: - static void _register_methods(); - LibuvUtils(); ~LibuvUtils(); - void _init(); + static Dictionary get_os_environ(); + static String get_os_release(); + static String get_cwd(); - Dictionary get_os_environ(); - String get_os_release(); - String get_cwd(); - - godot_error kill(int pid, int signum); + static Error kill(int pid, int signum); public: - static godot_error translate_uv_errno(int uv_err); + static Error translate_uv_errno(int uv_err); + +protected: + static void _bind_methods(); }; } // namespace godot -#endif // GODOT_XTERM_UV_UTILS_H \ No newline at end of file +#endif // GODOT_XTERM_UV_UTILS_H diff --git a/addons/godot_xterm/native/src/node_pty/unix/pty.cc b/addons/godot_xterm/native/src/node_pty/unix/pty.cc index 2f52ca0..a51a458 100644 --- a/addons/godot_xterm/native/src/node_pty/unix/pty.cc +++ b/addons/godot_xterm/native/src/node_pty/unix/pty.cc @@ -20,7 +20,7 @@ #include "pty.h" #include "libuv_utils.h" -#include +#include #include #include @@ -89,7 +89,7 @@ using namespace godot; */ struct pty_baton { - Ref cb; + Callable cb; int exit_code; int signal_code; pid_t pid; @@ -119,11 +119,11 @@ static void pty_after_waitpid(uv_async_t *); static void pty_after_close(uv_handle_t *); -Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, - PoolStringArray p_env, String p_cwd, int p_cols, int p_rows, - int p_uid, int p_gid, bool p_utf8, Ref p_on_exit) { +Array PTYUnix::fork(String p_file, int _ignored, PackedStringArray p_args, + PackedStringArray p_env, String p_cwd, int p_cols, int p_rows, + int p_uid, int p_gid, bool p_utf8, Callable p_on_exit) { // file - char *file = p_file.alloc_c_string(); + char *file = strdup(p_file.utf8().get_data()); // args int i = 0; @@ -133,7 +133,7 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, argv[0] = strdup(file); argv[argl - 1] = NULL; for (; i < argc; i++) { - char *arg = p_args[i].alloc_c_string(); + char *arg = strdup(p_args[i].utf8().get_data()); argv[i + 1] = strdup(arg); } @@ -143,12 +143,12 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, char **env = new char *[envc + 1]; env[envc] = NULL; for (; i < envc; i++) { - char *pairs = p_env[i].alloc_c_string(); + char *pairs = strdup(p_env[i].utf8().get_data()); env[i] = strdup(pairs); } // cwd - char *cwd = strdup(p_cwd.alloc_c_string()); + char *cwd = strdup(p_cwd.utf8().get_data()); // size struct winsize winp; @@ -240,7 +240,7 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, switch (pid) { case -1: ERR_PRINT("forkpty(3) failed."); - return Array::make(GODOT_FAILED); + return Array::make(FAILED); case 0: if (strlen(cwd)) { if (chdir(cwd) == -1) { @@ -267,10 +267,10 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, default: if (pty_nonblock(master) == -1) { ERR_PRINT("Could not set master fd to nonblocking."); - return Array::make(GODOT_FAILED); + return Array::make(FAILED); } - Dictionary result = Dictionary::make(); + Dictionary result; result["fd"] = (int)master; result["pid"] = (int)pid; result["pty"] = ptsname(master); @@ -286,10 +286,10 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args, uv_thread_create(&baton->tid, pty_waitpid, static_cast(baton)); - return Array::make(GODOT_OK, result); + return Array::make(OK, result); } - return Array::make(GODOT_FAILED); + return Array::make(FAILED); } Array PTYUnix::open(int p_cols, int p_rows) { @@ -306,28 +306,28 @@ Array PTYUnix::open(int p_cols, int p_rows) { if (ret == -1) { ERR_PRINT("openpty(3) failed."); - return Array::make(GODOT_FAILED); + return Array::make(FAILED); } if (pty_nonblock(master) == -1) { ERR_PRINT("Could not set master fd to nonblocking."); - return Array::make(GODOT_FAILED); + return Array::make(FAILED); } if (pty_nonblock(slave) == -1) { ERR_PRINT("Could not set slave fd to nonblocking."); - return Array::make(GODOT_FAILED); + return Array::make(FAILED); } - Dictionary dict = Dictionary::make(); + Dictionary dict; dict["master"] = master; dict["slave"] = slave; dict["pty"] = ptsname(master); - return Array::make(GODOT_OK, dict); + return Array::make(OK, dict); } -godot_error PTYUnix::resize(int p_fd, int p_cols, int p_rows) { +Error PTYUnix::resize(int p_fd, int p_cols, int p_rows) { int fd = p_fd; struct winsize winp; @@ -348,10 +348,10 @@ godot_error PTYUnix::resize(int p_fd, int p_cols, int p_rows) { RETURN_UV_ERR(UV_ENOTTY); } ERR_PRINT("ioctl(2) failed"); - return GODOT_FAILED; + return FAILED; } - return GODOT_OK; + return OK; } /** @@ -360,7 +360,7 @@ godot_error PTYUnix::resize(int p_fd, int p_cols, int p_rows) { String PTYUnix::process(int p_fd, String p_tty) { int fd = p_fd; - char *tty = p_tty.alloc_c_string(); + char *tty = strdup(p_tty.utf8().get_data()); char *name = pty_getproc(fd, tty); std::free(tty); @@ -445,9 +445,9 @@ static void pty_after_waitpid(uv_async_t *async) { Array argv = Array::make(baton->exit_code, baton->signal_code); - if (baton->cb != nullptr && baton->cb->is_valid()) { - baton->cb->call_funcv(argv); - baton->cb = (Ref)nullptr; + if (baton->cb != nullptr && baton->cb.is_valid()) { + baton->cb.callv(argv); + baton->cb = (Variant)nullptr; } uv_close((uv_handle_t *)async, pty_after_close); @@ -663,12 +663,12 @@ static pid_t pty_forkpty(int *amaster, char *name, const struct termios *termp, * Init */ -void PTYUnix::_register_methods() { - register_method("_init", &PTYUnix::_init); - register_method("fork", &PTYUnix::fork); - register_method("open", &PTYUnix::open); - register_method("resize", &PTYUnix::resize); - register_method("process", &PTYUnix::process); +void PTYUnix::_bind_methods() { + ClassDB::bind_method(D_METHOD("_init"), &PTYUnix::_init); + ClassDB::bind_method(D_METHOD("fork"), &PTYUnix::fork); + ClassDB::bind_method(D_METHOD("open"), &PTYUnix::open); + ClassDB::bind_method(D_METHOD("resize"), &PTYUnix::resize); + ClassDB::bind_method(D_METHOD("process"), &PTYUnix::process); } -void PTYUnix::_init() {} \ No newline at end of file +void PTYUnix::_init() {} diff --git a/addons/godot_xterm/native/src/node_pty/unix/pty.h b/addons/godot_xterm/native/src/node_pty/unix/pty.h index f9fe3fe..d412a6d 100644 --- a/addons/godot_xterm/native/src/node_pty/unix/pty.h +++ b/addons/godot_xterm/native/src/node_pty/unix/pty.h @@ -1,29 +1,32 @@ -// Copyright (c) 2021, Leroy Hopson (MIT License). +// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson +// SPDX-License-Identifier: MIT #ifndef GODOT_XTERM_PTY_H #define GODOT_XTERM_PTY_H -#include -#include +#include +#include namespace godot { -class PTYUnix : public Reference { - GODOT_CLASS(PTYUnix, Reference) +class PTYUnix : public RefCounted { + GDCLASS(PTYUnix, RefCounted) public: Array fork(String file, int _ignored, /* FIXME: For some reason Pipe throws ENOTSOCK in read callback if args (or another non-empty, non-zero) value is in this position. */ - PoolStringArray args, PoolStringArray env, String cwd, int cols, - int rows, int uid, int gid, bool utf8, Ref on_exit); + PackedStringArray args, PackedStringArray env, String cwd, int cols, + int rows, int uid, int gid, bool utf8, Callable on_exit); Array open(int cols, int rows); - godot_error resize(int fd, int cols, int rows); + Error resize(int fd, int cols, int rows); String process(int fd, String tty); void _init(); - static void _register_methods(); + +protected: + static void _bind_methods(); }; } // namespace godot diff --git a/addons/godot_xterm/native/src/node_pty/win/conpty.h b/addons/godot_xterm/native/src/node_pty/win/conpty.h index be66641..9f6f433 100644 --- a/addons/godot_xterm/native/src/node_pty/win/conpty.h +++ b/addons/godot_xterm/native/src/node_pty/win/conpty.h @@ -1,15 +1,16 @@ -// Copyright (c) 2021, Leroy Hopson (MIT License). +// SPDX-FileCopyrightText: 2021 Leroy Hopson +// SPDX-License-Identifier: MIT #ifndef GODOT_XTERM_CONPTY_H #define GODOT_XTERM_CONPTY_H -#include -#include +#include +#include namespace godot { -class ConPTY : public Reference { - GODOT_CLASS(ConPTY, Reference) +class ConPTY : public RefCounted { + GDCLASS(ConPTY, RefCounted) public: // Array fork(String file, @@ -25,9 +26,11 @@ public: // String process(int fd, String tty); void _init(); - static void _register_methods(); + +protected: + static void _bind_methods(); }; } // namespace godot -#endif // GODOT_XTERM_CONPTY_H \ No newline at end of file +#endif // GODOT_XTERM_CONPTY_H diff --git a/addons/godot_xterm/native/src/pipe.cpp b/addons/godot_xterm/native/src/pipe.cpp index 403f1db..7c4670d 100644 --- a/addons/godot_xterm/native/src/pipe.cpp +++ b/addons/godot_xterm/native/src/pipe.cpp @@ -2,12 +2,14 @@ #include "pipe.h" #include "libuv_utils.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -19,17 +21,16 @@ using namespace godot; -void Pipe::_register_methods() { - register_method("_init", &Pipe::_init); +void Pipe::_bind_methods() { + ClassDB::bind_method(D_METHOD("_init"), &Pipe::_init); - register_method("poll", &Pipe::_poll_connection); - register_method("open", &Pipe::open); - register_method("write", &Pipe::write); - register_method("get_status", &Pipe::get_status); - register_method("close", &Pipe::close); + ClassDB::bind_method(D_METHOD("poll"), &Pipe::_poll_connection); + ClassDB::bind_method(D_METHOD("open"), &Pipe::open); + ClassDB::bind_method(D_METHOD("write"), &Pipe::write); + ClassDB::bind_method(D_METHOD("get_status"), &Pipe::get_status); + ClassDB::bind_method(D_METHOD("close"), &Pipe::close); - register_signal("data_received", "data", - GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); + ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); } Pipe::Pipe() {} @@ -47,7 +48,7 @@ void _write_cb(uv_write_t *req, int status); void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); -godot_error Pipe::open(int fd, bool ipc = false) { +Error Pipe::open(int fd, bool ipc = false) { RETURN_IF_UV_ERR(uv_pipe_init(uv_default_loop(), &handle, ipc)); handle.data = this; @@ -58,7 +59,7 @@ godot_error Pipe::open(int fd, bool ipc = false) { uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb)); status = 1; - return GODOT_OK; + return OK; } void Pipe::close() { @@ -66,8 +67,8 @@ void Pipe::close() { uv_run(uv_default_loop(), UV_RUN_NOWAIT); } -godot_error Pipe::write(PoolByteArray data) { - char *s = (char *)data.read().ptr(); +Error Pipe::write(PackedByteArray data) { + char *s = (char *)data.ptr(); ULONG len = data.size(); uv_buf_t buf; @@ -80,7 +81,7 @@ godot_error Pipe::write(PoolByteArray data) { uv_write(req, (uv_stream_t *)&handle, &buf, 1, _write_cb); uv_run(uv_default_loop(), UV_RUN_NOWAIT); - return GODOT_OK; + return OK; } int Pipe::get_status() { @@ -116,9 +117,9 @@ void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { return; } - PoolByteArray data; + PackedByteArray data; data.resize(nread); - { memcpy(data.write().ptr(), buf->base, nread); } + { memcpy(data.ptrw(), buf->base, nread); } std::free((char *)buf->base); pipe->emit_signal("data_received", data); diff --git a/addons/godot_xterm/native/src/pipe.h b/addons/godot_xterm/native/src/pipe.h index 19ebb5a..343a8d5 100644 --- a/addons/godot_xterm/native/src/pipe.h +++ b/addons/godot_xterm/native/src/pipe.h @@ -1,32 +1,31 @@ -// Copyright (c) 2021, Leroy Hopson (MIT License). +// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson +// SPDX-License-Identifier: MIT #ifndef GODOT_XTERM_PIPE_H #define GODOT_XTERM_PIPE_H -#include -#include +#include +#include #include namespace godot { -class Pipe : public Reference { - GODOT_CLASS(Pipe, Reference) +class Pipe : public RefCounted { + GDCLASS(Pipe, RefCounted) public: uv_pipe_t handle; - static void _register_methods(); - Pipe(); ~Pipe(); void _init(); - godot_error open(int fd, bool ipc); + Error open(int fd, bool ipc); void close(); int get_status(); - godot_error write(PoolByteArray data); + Error write(PackedByteArray data); void pause(); void resume(); @@ -34,10 +33,13 @@ public: public: int status; +protected: + static void _bind_methods(); + private: void _poll_connection(); - static godot_error _translate_error(int err); + static Error _translate_error(int err); }; } // namespace godot diff --git a/addons/godot_xterm/native/src/register_types.cpp b/addons/godot_xterm/native/src/register_types.cpp new file mode 100644 index 0000000..bf5eece --- /dev/null +++ b/addons/godot_xterm/native/src/register_types.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2022, Leroy Hopson (MIT License). + +#include "register_types.h" + +#include + +#include +#include +#include + +#include "terminal.h" + +#if !defined(_PTY_DISABLED) +#include "libuv_utils.h" +#include "pipe.h" +#if defined(__linux__) || defined(__APPLE__) +#include "node_pty/unix/pty.h" +#endif +#if defined(__WIN32) +//#include "node_pty/win/conpty.h" +#endif +#endif + +using namespace godot; + +void initialize_godot_xterm_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + ClassDB::register_class(); +#if !defined(_PTY_DISABLED) + ClassDB::register_class(); + ClassDB::register_class(); +#if defined(__linux__) || defined(__APPLE__) + ClassDB::register_class(); +#endif +#if defined(__WIN32) + //ClassDB::register_class(); +#endif +#endif +} + +void uninitialize_godot_xterm_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} + +extern "C" +// Initialization +GDExtensionBool GDE_EXPORT godot_xterm_library_init(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { + godot::GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); + + init_obj.register_initializer(initialize_godot_xterm_module); + init_obj.register_terminator(uninitialize_godot_xterm_module); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + + return init_obj.init(); +} diff --git a/addons/godot_xterm/native/src/register_types.h b/addons/godot_xterm/native/src/register_types.h new file mode 100644 index 0000000..6452338 --- /dev/null +++ b/addons/godot_xterm/native/src/register_types.h @@ -0,0 +1,11 @@ +#ifndef GODOT_XTERM_REGISTER_TYPES_H +#define GODOT_XTERM_REGISTER_TYPES_H + +#include + +using namespace godot; + +void initialize_godot_xterm_module(ModuleInitializationLevel p_level); +void uninitialize_godot_xterm_module(ModuleInitializationLevel p_level); + +#endif // GODOT_XTERM_REGISTER_TYPES_H diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 2f0e4ab..8a85cd7 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -1,19 +1,19 @@ // Copyright (c) 2021, Leroy Hopson (MIT License). #include "terminal.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include // For _populate_key_list(), see below. #if !defined(__EMSCRIPTEN__) && !defined(__APPLE__) -#include +#include #endif using namespace godot; @@ -23,22 +23,8 @@ void Terminal::_populate_key_list() { if (!_key_list.empty()) return; -// The following error is thrown on the javascript platform when using -// GlobalConstants from the header: abort(Assertion failed: bad export type for -// `_ZN5godot15GlobalConstants8KEY_KP_0E`: undefined). Build with -s -// ASSERTIONS=1 for more info. -#if !defined(__EMSCRIPTEN__) && !defined(__APPLE__) -#define GLOBAL_CONSTANT(VAR) GlobalConstants::VAR -#else -#define GLOBAL_CONSTANT(VAR) get_constant(#VAR) - const godot_dictionary _constants = godot::api->godot_get_global_constants(); - const Dictionary *constants = (Dictionary *)&_constants; - - auto get_constant = [constants](std::string name) -> int64_t { - godot::Variant key = (godot::Variant)(godot::String(name.c_str())); - return constants->operator[](key); - }; -#endif +// TODO: Remove GLOBAL_CONSTANT macro. +#define GLOBAL_CONSTANT(VAR) VAR // Godot does not have seperate scancodes for keypad keys when NumLock is off. // We can check the unicode value to determine whether it is off and set the @@ -226,9 +212,9 @@ static void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) { Terminal *term = static_cast(data); - PoolByteArray bytes = PoolByteArray(); + PackedByteArray bytes; bytes.resize(len); - { memcpy(bytes.write().ptr(), u8, len); } + { memcpy(bytes.ptrw(), u8, len); } if (len > 0) { if (term->input_event_key.is_valid()) { @@ -276,51 +262,53 @@ static void bell_cb(tsm_vte *_vte, void *data) { terminal->emit_signal("bell"); } -void Terminal::_register_methods() { - register_method("_init", &Terminal::_init); - register_method("_ready", &Terminal::_ready); - register_method("_notification", &Terminal::_notification); - register_method("_gui_input", &Terminal::_gui_input); - register_method("_draw", &Terminal::_draw); +void Terminal::_bind_methods() { + //ClassDB::bind_method(D_METHOD("_ready"), &Terminal::_ready); + ClassDB::bind_method(D_METHOD("_notification"), &Terminal::_notification); + ClassDB::bind_method(D_METHOD("__gui_input"), &Terminal::_gui_input); + //ClassDB::bind_method(D_METHOD("_draw"), &Terminal::_draw); - register_method("write", &Terminal::write); + ClassDB::bind_method(D_METHOD("write"), &Terminal::write); - register_method("sb_up", &Terminal::sb_up); - register_method("sb_down", &Terminal::sb_down); - register_method("sb_reset", &Terminal::sb_reset); - register_method("clear_sb", &Terminal::clear_sb); + ClassDB::bind_method(D_METHOD("sb_up"), &Terminal::sb_up); + ClassDB::bind_method(D_METHOD("sb_down"), &Terminal::sb_down); + ClassDB::bind_method(D_METHOD("sb_reset"), &Terminal::sb_reset); + ClassDB::bind_method(D_METHOD("clear_sb"), &Terminal::clear_sb); - register_method("start_selection", &Terminal::start_selection); - register_method("select_to_pointer", &Terminal::select_to_pointer); - register_method("reset_selection", &Terminal::reset_selection); - register_method("copy_selection", &Terminal::copy_selection); - register_method("copy_all", &Terminal::copy_all); + ClassDB::bind_method(D_METHOD("start_selection"), &Terminal::start_selection); + ClassDB::bind_method(D_METHOD("select_to_pointer"), &Terminal::select_to_pointer); + ClassDB::bind_method(D_METHOD("reset_selection"), &Terminal::reset_selection); + ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection); + ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all); - register_method("_update_theme", &Terminal::update_theme); - register_method("_update_size", &Terminal::update_theme); + ClassDB::bind_method(D_METHOD("_update_theme"), &Terminal::update_theme); + ClassDB::bind_method(D_METHOD("_update_size"), &Terminal::update_theme); - register_property("cell_size", &Terminal::cell_size, - Vector2(0, 0)); - register_property("rows", &Terminal::rows, 24); - register_property("cols", &Terminal::cols, 80); - register_property("update_mode", &Terminal::update_mode, - UpdateMode::AUTO); + ClassDB::bind_method(D_METHOD("get_cell_size"), &Terminal::get_cell_size); + ClassDB::bind_method(D_METHOD("get_rows"), &Terminal::get_rows); + ClassDB::bind_method(D_METHOD("get_cols"), &Terminal::get_cols); + ClassDB::bind_method(D_METHOD("get_update_mode"), &Terminal::get_update_mode); + ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &Terminal::set_update_mode); + ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode"), "set_update_mode", "get_update_mode"); - register_signal("data_sent", "data", - GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); - register_signal("key_pressed", "data", - GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY, "event", - GODOT_VARIANT_TYPE_OBJECT); - register_signal("size_changed", "new_size", - GODOT_VARIANT_TYPE_VECTOR2); - register_signal("bell", Dictionary()); + ADD_SIGNAL(MethodInfo("data_sent", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("key_pressed", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"), + PropertyInfo(Variant::OBJECT, "event"))); + ADD_SIGNAL(MethodInfo("size_changed", PropertyInfo(Variant::VECTOR2, "new_size"))); + ADD_SIGNAL(MethodInfo("bell")); } -Terminal::Terminal() {} - Terminal::~Terminal() {} -void Terminal::_init() { +// TODO: Investigate using UPDATE_MODE enum instead of int. +int Terminal::get_update_mode() { return update_mode; } +void Terminal::set_update_mode(int p_update_mode) { update_mode = p_update_mode; }; + +Vector2 Terminal::get_cell_size() { return cell_size; } +int Terminal::get_rows() { return rows; } +int Terminal::get_cols() { return cols; } + +Terminal::Terminal() { framebuffer_age = 0; update_mode = UpdateMode::AUTO; @@ -358,16 +346,16 @@ void Terminal::_gui_input(Variant event) { return; } - int64_t scancode = k->get_scancode(); + int64_t scancode = k->get_keycode(); int64_t unicode = k->get_unicode(); uint32_t ascii = unicode <= 127 ? unicode : 0; unsigned int mods = 0; - if (k->get_alt()) + if (k->is_alt_pressed()) mods |= TSM_ALT_MASK; - if (k->get_control()) + if (k->is_ctrl_pressed()) mods |= TSM_CONTROL_MASK; - if (k->get_shift()) + if (k->is_shift_pressed()) mods |= TSM_SHIFT_MASK; uint32_t keysym = mapkey({unicode, scancode}); @@ -418,8 +406,8 @@ void Terminal::update_theme() { Color default_color) -> void { Color c; - c = has_color(theme_color, "Terminal") ? get_color(theme_color, "Terminal") - : has_color_override(theme_color) ? get_color(theme_color, "") + c = has_theme_color(theme_color, "Terminal") ? get_theme_color(theme_color, "Terminal") + : has_theme_color_override(theme_color) ? get_theme_color(theme_color, "") : (default_theme != nullptr && default_theme->has_color(theme_color, "Terminal")) ? default_theme->get_color(theme_color, "Terminal") @@ -488,17 +476,17 @@ void Terminal::update_theme() { auto load_font = [this, default_theme](String font_style) -> void { Ref fontref; - if (has_font(font_style, "Terminal")) { - fontref = get_font(font_style, "Terminal"); - } else if (has_font_override(font_style)) { - fontref = get_font(font_style, ""); - } else if (has_font("regular", "Terminal")) { - fontref = get_font("regular", "Terminal"); + if (has_theme_font(font_style, "Terminal")) { + fontref = get_theme_font(font_style, "Terminal"); + } else if (has_theme_font_override(font_style)) { + fontref = get_theme_font(font_style, ""); + } else if (has_theme_font("regular", "Terminal")) { + fontref = get_theme_font("regular", "Terminal"); } else if (default_theme != nullptr && default_theme->has_font("regular", "Terminal")) { fontref = default_theme->get_font("regular", "Terminal"); } else { - fontref = get_font(""); + fontref = get_theme_font(""); } fontmap.insert(std::pair>(font_style, fontref)); @@ -523,7 +511,7 @@ void Terminal::draw_foreground(int row, int col, char *ch, const tsm_screen_attr *attr, Color fgcolor) { /* Set the font */ - Ref fontref = get_font(""); + Ref fontref = get_theme_font(""); if (attr->bold && attr->italic) { fontref = fontmap["bold_italic"]; @@ -543,10 +531,10 @@ void Terminal::draw_foreground(int row, int col, char *ch, int font_height = fontref.ptr()->get_height(); Vector2 foreground_pos = Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25); - draw_string(fontref, foreground_pos, ch, fgcolor); + draw_string(fontref, foreground_pos, ch, HORIZONTAL_ALIGNMENT_LEFT, -1, 16, fgcolor); // FIXME if (attr->underline) - draw_string(fontref, foreground_pos, "_", fgcolor); + draw_string(fontref, foreground_pos, "_", HORIZONTAL_ALIGNMENT_LEFT, -1, 16, fgcolor); // FIXME } std::pair Terminal::get_cell_colors(const tsm_screen_attr *attr) { @@ -596,10 +584,10 @@ void Terminal::update_size() { Ref fontref; if (fontmap.count("regular")) fontref = fontmap["regular"]; - else if (has_font("regular", "Terminal")) - fontref = get_font("regular", "Terminal"); + else if (has_theme_font("regular", "Terminal")) + fontref = get_theme_font("regular", "Terminal"); else - fontref = get_font(""); + fontref = get_theme_font(""); cell_size = fontref->get_string_size("W"); @@ -611,43 +599,43 @@ void Terminal::update_size() { emit_signal("size_changed", Vector2(cols, rows)); } -void Terminal::write(PoolByteArray data) { - tsm_vte_input(vte, (char *)data.read().ptr(), data.size()); +void Terminal::write(PackedByteArray data) { + tsm_vte_input(vte, (char *)data.ptr(), data.size()); } void Terminal::sb_up(int num) { tsm_screen_sb_up(screen, num); - update(); + queue_redraw(); } void Terminal::sb_down(int num) { tsm_screen_sb_down(screen, num); - update(); + queue_redraw(); } void Terminal::sb_reset() { tsm_screen_sb_reset(screen); - update(); + queue_redraw(); } void Terminal::clear_sb() { tsm_screen_clear_sb(screen); - update(); + queue_redraw(); } void Terminal::start_selection(Vector2 position) { tsm_screen_selection_start(screen, position.x, position.y); - update(); + queue_redraw(); } void Terminal::select_to_pointer(Vector2 position) { tsm_screen_selection_target(screen, position.x, position.y); - update(); + queue_redraw(); } void Terminal::reset_selection() { tsm_screen_selection_reset(screen); - update(); + queue_redraw(); } String Terminal::copy_selection() { diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index 47c364a..0db6b38 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -1,11 +1,13 @@ -// Copyright (c) 2021, Leroy Hopson (MIT License). +// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson +// SPDX-License-Identifier: MIT #ifndef TERMINAL_H #define TERMINAL_H -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -13,12 +15,14 @@ namespace godot { class Terminal : public Control { - GODOT_CLASS(Terminal, Control) + GDCLASS(Terminal, Control) public: Ref input_event_key; protected: + static void _bind_methods(); + tsm_screen *screen; tsm_vte *vte; @@ -42,18 +46,15 @@ public: Color fgcol); public: - static void _register_methods(); - Terminal(); ~Terminal(); - void _init(); void _ready(); void _notification(int what); void _gui_input(Variant event); void _draw(); - void write(PoolByteArray data); + void write(PackedByteArray data); void sb_up(int num); void sb_down(int num); @@ -72,11 +73,16 @@ public: ALL, ALL_NEXT_FRAME, }; + int update_mode = UpdateMode::AUTO; + int get_update_mode(); + void set_update_mode(int update_mode); - Vector2 cell_size; - int rows; - int cols; - int update_mode; + Vector2 cell_size = Vector2(0, 0); + Vector2 get_cell_size(); + int rows = 24; + int get_rows(); + int cols = 80; + int get_cols(); uint8_t color_palette[TSM_COLOR_NUM][3]; diff --git a/addons/godot_xterm/native/thirdparty/godot-cpp b/addons/godot_xterm/native/thirdparty/godot-cpp index 867374d..3da6db4 160000 --- a/addons/godot_xterm/native/thirdparty/godot-cpp +++ b/addons/godot_xterm/native/thirdparty/godot-cpp @@ -1 +1 @@ -Subproject commit 867374da404887337909e8b7b9de5a8acbc47569 +Subproject commit 3da6db4fe41b8f3d3aaeb0dc80f1556f83fd7fe6 diff --git a/addons/godot_xterm/nodes/pty/libuv_utils.gd b/addons/godot_xterm/nodes/pty/libuv_utils.gd index a2b2887..8a818ea 100644 --- a/addons/godot_xterm/nodes/pty/libuv_utils.gd +++ b/addons/godot_xterm/nodes/pty/libuv_utils.gd @@ -6,14 +6,12 @@ extends Object # GDNative does not currently support registering static functions so we fake it. # Only the static functions of this class should be called. -const LibuvUtils = preload("./libuv_utils.gdns") - static func get_os_environ() -> Dictionary: # While Godot has OS.get_environment(), I could see a way to get all environent # variables, other than by OS.execute() which would require to much platform # specific code. Easier to use libuv's utility function. - return LibuvUtils.new().get_os_environ() + return LibuvUtils.get_os_environ() static func get_cwd() -> String: diff --git a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd index a87c174..44ed973 100644 --- a/addons/godot_xterm/nodes/pty/unix/pty_unix.gd +++ b/addons/godot_xterm/nodes/pty/unix/pty_unix.gd @@ -6,10 +6,6 @@ @tool extends "../pty_native.gd" -const LibuvUtils := preload("../libuv_utils.gd") -const Pipe := preload("../pipe.gdns") -const PTYUnix = preload("./pty_unix.gdns") - const DEFAULT_NAME := "xterm-256color" const DEFAULT_COLS := 80 const DEFAULT_ROWS := 24 @@ -25,7 +21,7 @@ const FALLBACK_FILE = "sh" # 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. -enum Signal { +enum IPCSignal { SIGHUP = 1, # Hangup SIGINT = 2, # Terminal interrupt signal SIGQUIT = 3, # Terminal quit signal @@ -68,10 +64,7 @@ var _exit_cb: Callable # Writes data to the socket. # data: The data to write. func write(data) -> void: - assert( - data is PackedByteArray or data is String, - "Invalid type for argument 'data'. Should be of type PackedByteArray or String" - ) + assert(data is PackedByteArray or data is String, "Invalid type for argument 'data'. Should be of type PackedByteArray or String") if _pipe: _pipe.write(data if data is PackedByteArray else data.to_utf8_buffer()) @@ -81,7 +74,7 @@ func resize(cols: int, rows: int) -> void: PTYUnix.new().resize(_fd, cols, rows) -func kill(signum: int = Signal.SIGHUP) -> void: +func kill(signum: int = IPCSignal.SIGHUP) -> void: if _pipe: _pipe.close() if _pid > 0: @@ -133,13 +126,12 @@ func fork( var parsed_env: PackedStringArray = _parse_env(final_env) # Exit callback. - _exit_cb = Callable.new() - _exit_cb.set_instance(self) - _exit_cb.set_function("_on_exit") + _exit_cb = Callable(self, "on_exit") # Actual fork. - var result = PTYUnix.new().fork( # VERY IMPORTANT: The must be set null or 0, otherwise will get an ENOTSOCK error after connecting our pipe to the fd. - file, null, args, parsed_env, cwd, cols, rows, uid, gid, utf8, _exit_cb + var result = PTYUnix.new().fork( + # VERY IMPORTANT: The second argument must be 0, otherwise will get an ENOTSOCK error after connecting our pipe to the fd. + file, 0, args, parsed_env, cwd, cols, rows, uid, gid, utf8, _exit_cb ) if result[0] != OK: @@ -154,7 +146,7 @@ func fork( _pid = result[1].pid _pipe = Pipe.new() - _pipe.open(_fd) + _pipe.open(_fd, true) # FIXME: _pipe.open(_fd) should be sufficient but requires two args. # Must connect to signal AFTER opening, otherwise we will get error ENOTSOCK. _pipe.connect("data_received",Callable(self,"_on_pipe_data_received")) @@ -169,7 +161,7 @@ func open(cols: int = DEFAULT_COLS, rows: int = DEFAULT_ROWS) -> Array: func _exit_tree(): _exit_cb = null if _pid > 1: - LibuvUtils.kill(_pid, Signal.SIGHUP) + LibuvUtils.kill(_pid, IPCSignal.SIGHUP) if _pipe: while _pipe.get_status() != 0: continue diff --git a/addons/godot_xterm/nodes/terminal/viewport.tscn b/addons/godot_xterm/nodes/terminal/viewport.tscn index 81e2090..669241a 100644 --- a/addons/godot_xterm/nodes/terminal/viewport.tscn +++ b/addons/godot_xterm/nodes/terminal/viewport.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://c62updkby54f3"] -[sub_resource type="GDScript" id="1"] +[sub_resource type="GDScript" id="GDScript_d8lvm"] script/source = "@tool extends SubViewport " @@ -11,12 +11,13 @@ handle_input_locally = false gui_snap_controls_to_pixels = false size = Vector2i(2, 2) render_target_clear_mode = 1 -script = SubResource("1") +script = SubResource("GDScript_d8lvm") -[node name="Terminal" type="Control" parent="."] -layout_mode = 3 +[node name="Terminal" type="Terminal" parent="."] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 offset_right = -2.0 offset_bottom = -2.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/addons/godot_xterm/pty.gd b/addons/godot_xterm/pty.gd index 3dba29a..5ffbfe3 100644 --- a/addons/godot_xterm/pty.gd +++ b/addons/godot_xterm/pty.gd @@ -18,7 +18,7 @@ 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 +const IPCSignal = _PTYUnix.IPCSignal signal data_received(data) signal exited(exit_code, signum) @@ -78,8 +78,8 @@ func _init(): func _ready(): - if terminal_path and not _terminal: - set_terminal_path(terminal_path) + if not (terminal_path.is_empty()) and not _terminal: + self.terminal_path = terminal_path func set_cols(value: int): @@ -151,7 +151,7 @@ func resizev(size: Vector2) -> void: # 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: +func kill(signum: int = IPCSignal.SIGHUP) -> void: _pty_native.kill(signum) @@ -160,7 +160,7 @@ func _notification(what: int): NOTIFICATION_PARENTED: var parent = get_parent() if parent is _Terminal: - set_terminal_path(get_path_to(parent)) + self.terminal_path = get_path_to(parent) func fork( diff --git a/addons/godot_xterm/terminal.gd b/addons/godot_xterm/terminal.gd index 1cf91db..e896274 100644 --- a/addons/godot_xterm/terminal.gd +++ b/addons/godot_xterm/terminal.gd @@ -27,8 +27,8 @@ enum SelectionMode { @export var update_mode: UpdateMode = UpdateMode.AUTO : get: return update_mode # TODOConverter40 Non existent get function - set(mod_value): - mod_value # TODOConverter40 Copy here content of set_update_mode + set(p_update_mode): + set_update_mode(p_update_mode) # If true, text in the terminal will be copied to the clipboard when selected. @export var copy_on_selection: bool @@ -90,7 +90,7 @@ func write(data) -> void: func _flush(): for data in _buffer: _native_terminal.write(data if data is PackedByteArray else data.to_utf8_buffer()) - _native_terminal.update() + _native_terminal.queue_redraw() _buffer.clear() @@ -170,29 +170,30 @@ func _update_theme(): func _refresh(): - _screen.update() + _screen.queue_redraw() if update_mode == UpdateMode.AUTO: _native_terminal.update_mode = UpdateMode.ALL_NEXT_FRAME func _gui_input(event): - _native_terminal._gui_input(event) + _native_terminal.__gui_input(event) # FIXME: use _gui_input rather than __gui_input. if event is InputEventKey and event.pressed: # Return to bottom of scrollback buffer if we scrolled up. Ignore modifier # keys pressed in isolation or if Ctrl+Shift modifier keys are pressed. if ( - not event.scancode in [KEY_ALT, KEY_SHIFT, KEY_CTRL, KEY_META, KEY_MASK_CMD_OR_CTRL] - and not (event.control and event.shift) + not event.keycode in [KEY_ALT, KEY_SHIFT, KEY_CTRL, KEY_META, KEY_MASK_CMD_OR_CTRL] + and not (event.ctrl_pressed and event.shift_pressed) ): _native_terminal.sb_reset() # Prevent focus changing to other inputs when pressing Tab or Arrow keys. - if event.scancode in [KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_TAB]: + if event.keycode in [KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_TAB]: accept_event() - _handle_mouse_wheel(event) - _handle_selection(event) + # FIXME + #_handle_mouse_wheel(event) + #_handle_selection(event) func _handle_mouse_wheel(event: InputEventMouseButton): diff --git a/examples/menu/menu.gd b/examples/menu/menu.gd index 2bd207d..2827257 100644 --- a/examples/menu/menu.gd +++ b/examples/menu/menu.gd @@ -110,10 +110,10 @@ func draw_menu(): func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void: match data: - tput.CURSOR_UP: # Up arrow key + TPut.CURSOR_UP: # Up arrow key selected_index = int(clamp(selected_index - 1, 0, menu_items.size() - 1)) draw_menu() - tput.CURSOR_DOWN: # Down arrow key + TPut.CURSOR_DOWN: # Down arrow key selected_index = int(clamp(selected_index + 1, 0, menu_items.size() - 1)) draw_menu() "1": @@ -127,7 +127,7 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void: draw_menu() # We can also match against the raw InputEventKey. - if event.scancode == KEY_ENTER: + if event.keycode == KEY_ENTER: var item = menu_items[selected_index] match item.name: @@ -160,9 +160,11 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void: $Terminal.grab_focus() scene.queue_free() "Exit": - if OS.has_feature("JavaScript"): - JavaScript.eval("window.history.back() || window.close()") - get_tree().quit() + pass + # FIXME + #if OS.has_feature("JavaScript"): + #JavaScript.eval("window.history.back() || window.close()") + #get_tree().quit() func _on_Asciicast_key_pressed( diff --git a/examples/menu/menu.tscn b/examples/menu/menu.tscn index ca19289..4576d86 100644 --- a/examples/menu/menu.tscn +++ b/examples/menu/menu.tscn @@ -8,15 +8,17 @@ layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 script = ExtResource("2") -__meta__ = { -"_edit_use_anchors_": false -} [node name="Terminal" type="Control" parent="."] -layout_mode = 3 +layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 focus_mode = 1 script = ExtResource("1") +copy_on_selection = false \ No newline at end of file diff --git a/project.godot b/project.godot index 99a4057..5c9d0d4 100644 --- a/project.godot +++ b/project.godot @@ -29,10 +29,6 @@ config/icon="res://docs/media/icon.png" window/vsync/use_vsync=false -[editor_plugins] - -enabled=PackedStringArray() - [rendering] quality/driver/driver_name="GLES2" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..1112466 --- /dev/null +++ b/shell.nix @@ -0,0 +1,16 @@ +{ pkgs ? import { } }: +pkgs.mkShell { + LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ + vulkan-loader + alsa-lib + libGL + libGLU + xorg.libX11 + xorg.libXcursor + xorg.libXext + xorg.libXfixes + xorg.libXi + xorg.libXinerama + xorg.libXrandr + ]; +}