mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-22 01:30:25 +01:00
Convert from GDNative to GDExtension
Work in progress.
This commit is contained in:
parent
6b47d35835
commit
44f7e3801c
25 changed files with 408 additions and 304 deletions
3
addons/godot_xterm/.gitignore
vendored
3
addons/godot_xterm/.gitignore
vendored
|
@ -346,6 +346,9 @@ platform/windows/godot_res.res
|
||||||
# Scons construction environment dump
|
# Scons construction environment dump
|
||||||
.scons_env.json
|
.scons_env.json
|
||||||
|
|
||||||
|
# Scons cache
|
||||||
|
.cache
|
||||||
|
|
||||||
# Scons progress indicator
|
# Scons progress indicator
|
||||||
.scons_node_count
|
.scons_node_count
|
||||||
|
|
||||||
|
|
6
addons/godot_xterm/godot_xterm.gdextension
Normal file
6
addons/godot_xterm/godot_xterm.gdextension
Normal file
|
@ -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"
|
|
@ -14,6 +14,8 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
EnsureSConsVersion(4, 0)
|
||||||
|
|
||||||
# Try to detect the host platform automatically.
|
# Try to detect the host platform automatically.
|
||||||
# This is used if no `platform` argument is passed.
|
# This is used if no `platform` argument is passed.
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith('linux'):
|
||||||
|
@ -58,8 +60,8 @@ opts.Add(EnumVariable(
|
||||||
opts.Add(EnumVariable(
|
opts.Add(EnumVariable(
|
||||||
'target',
|
'target',
|
||||||
'Compilation target',
|
'Compilation target',
|
||||||
'debug',
|
'editor',
|
||||||
allowed_values=('debug', 'release'),
|
allowed_values=('editor', 'release'),
|
||||||
ignorecase=2
|
ignorecase=2
|
||||||
))
|
))
|
||||||
opts.Add(EnumVariable("macos_arch", "Target macOS architecture",
|
opts.Add(EnumVariable("macos_arch", "Target macOS architecture",
|
||||||
|
@ -73,6 +75,15 @@ opts.Add(BoolVariable(
|
||||||
opts.Update(env)
|
opts.Update(env)
|
||||||
Help(opts.GenerateHelpText(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.
|
# Allows 32bit builds on windows 64bit.
|
||||||
if env['platform'] == 'windows':
|
if env['platform'] == 'windows':
|
||||||
if env['bits'] == '64':
|
if env['bits'] == '64':
|
||||||
|
@ -96,7 +107,7 @@ if env['platform'] == 'linux':
|
||||||
env.Append(CCFLAGS=['-fPIC', '-Wwrite-strings'])
|
env.Append(CCFLAGS=['-fPIC', '-Wwrite-strings'])
|
||||||
env.Append(LINKFLAGS=["-Wl,-R'$$ORIGIN'", '-static-libstdc++'])
|
env.Append(LINKFLAGS=["-Wl,-R'$$ORIGIN'", '-static-libstdc++'])
|
||||||
|
|
||||||
if env['target'] == 'debug':
|
if env['target'] == 'editor':
|
||||||
env.Append(CCFLAGS=['-Og', '-g'])
|
env.Append(CCFLAGS=['-Og', '-g'])
|
||||||
elif env['target'] == 'release':
|
elif env['target'] == 'release':
|
||||||
env.Append(CCFLAGS=['-O3'])
|
env.Append(CCFLAGS=['-O3'])
|
||||||
|
@ -153,7 +164,7 @@ elif env['platform'] == 'osx':
|
||||||
|
|
||||||
env.Append(LINKFLAGS=['-Wl,-undefined,dynamic_lookup'])
|
env.Append(LINKFLAGS=['-Wl,-undefined,dynamic_lookup'])
|
||||||
|
|
||||||
if env['target'] == 'debug':
|
if env['target'] == 'editor':
|
||||||
env.Append(CCFLAGS=['-Og', '-g'])
|
env.Append(CCFLAGS=['-Og', '-g'])
|
||||||
elif env['target'] == 'release':
|
elif env['target'] == 'release':
|
||||||
env.Append(CCFLAGS=['-O3'])
|
env.Append(CCFLAGS=['-O3'])
|
||||||
|
@ -166,14 +177,14 @@ elif env['platform'] == 'windows':
|
||||||
|
|
||||||
# On Windows using MSVC.
|
# On Windows using MSVC.
|
||||||
if host_platform == 'windows':
|
if host_platform == 'windows':
|
||||||
if env['target'] == 'debug':
|
if env['target'] == 'editor':
|
||||||
env.Append(CCFLAGS=['/Z7', '/Od', '/EHsc', '/D_DEBUG', '/MDd'])
|
env.Append(CCFLAGS=['/Z7', '/Od', '/EHsc', '/D_DEBUG', '/MDd'])
|
||||||
elif env['target'] == 'release':
|
elif env['target'] == 'release':
|
||||||
env.Append(CCFLAGS=['/O2', '/EHsc', '/DNDEBUG', '/MD'])
|
env.Append(CCFLAGS=['/O2', '/EHsc', '/DNDEBUG', '/MD'])
|
||||||
|
|
||||||
# On Windows, Linux, or MacOS using MinGW.
|
# On Windows, Linux, or MacOS using MinGW.
|
||||||
elif host_platform == 'linux' or host_platform == 'osx':
|
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=[
|
env.Append(LINKFLAGS=[
|
||||||
'--static',
|
'--static',
|
||||||
'-Wl,--no-undefined',
|
'-Wl,--no-undefined',
|
||||||
|
@ -181,7 +192,7 @@ elif env['platform'] == 'windows':
|
||||||
'-static-libstdc++',
|
'-static-libstdc++',
|
||||||
])
|
])
|
||||||
|
|
||||||
if env['target'] == 'debug':
|
if env['target'] == 'editor':
|
||||||
env.Append(CCFLAGS=['-Og', '-g'])
|
env.Append(CCFLAGS=['-Og', '-g'])
|
||||||
elif env['target'] == 'release':
|
elif env['target'] == 'release':
|
||||||
env.Append(CCFLAGS=['-O3'])
|
env.Append(CCFLAGS=['-O3'])
|
||||||
|
@ -231,16 +242,15 @@ Default(libtsm)
|
||||||
|
|
||||||
# Build libgodot-xterm.
|
# Build libgodot-xterm.
|
||||||
if env['platform'] != 'windows':
|
if env['platform'] != 'windows':
|
||||||
env.Append(CXXFLAGS=['-std=c++14'])
|
env.Append(CXXFLAGS=['-std=c++17'])
|
||||||
|
|
||||||
env.Append(CPPPATH=[
|
env.Append(CPPPATH=[
|
||||||
'src/',
|
'src/',
|
||||||
'thirdparty/libtsm/build/src/tsm',
|
'thirdparty/libtsm/build/src/tsm',
|
||||||
'thirdparty/libtsm/build/src/shared',
|
'thirdparty/libtsm/build/src/shared',
|
||||||
'thirdparty/godot-cpp/include/',
|
'thirdparty/godot-cpp/gdextension',
|
||||||
'thirdparty/godot-cpp/include/core/',
|
'thirdparty/godot-cpp/include',
|
||||||
'thirdparty/godot-cpp/include/gen/',
|
'thirdparty/godot-cpp/gen/include',
|
||||||
'thirdparty/godot-cpp/godot-headers/',
|
|
||||||
'thirdparty/libuv/src',
|
'thirdparty/libuv/src',
|
||||||
'thirdparty/libuv/include'
|
'thirdparty/libuv/include'
|
||||||
])
|
])
|
||||||
|
@ -254,7 +264,7 @@ env.Append(LIBS=[
|
||||||
env['platform'],
|
env['platform'],
|
||||||
env['target'],
|
env['target'],
|
||||||
'wasm' if env['platform'] == 'javascript' else env['macos_arch'] if (
|
'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['LIBSUFFIX'],
|
||||||
)),
|
)),
|
||||||
env.File('thirdparty/libtsm/build/bin/libtsm.{}.{}.{}{}'.format(
|
env.File('thirdparty/libtsm/build/bin/libtsm.{}.{}.{}{}'.format(
|
||||||
|
@ -266,7 +276,7 @@ env.Append(LIBS=[
|
||||||
])
|
])
|
||||||
|
|
||||||
sources = []
|
sources = []
|
||||||
sources.append('src/libgodotxtermnative.cpp')
|
sources.append('src/register_types.cpp')
|
||||||
sources.append('src/terminal.cpp')
|
sources.append('src/terminal.cpp')
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,7 +290,7 @@ else:
|
||||||
sources.append('src/node_pty/unix/pty.cc')
|
sources.append('src/node_pty/unix/pty.cc')
|
||||||
env.Append(LIBS=['util', env.File('thirdparty/libuv/build/libuv_a.a')])
|
env.Append(LIBS=['util', env.File('thirdparty/libuv/build/libuv_a.a')])
|
||||||
else:
|
else:
|
||||||
#sources.append('src/node_pty/win/conpty.cc')
|
sources.append('src/node_pty/win/conpty.cc')
|
||||||
env.Append(LIBS=[
|
env.Append(LIBS=[
|
||||||
env.File('thirdparty/libuv/build/{}/uv_a.lib'.format(env["target"].capitalize())),
|
env.File('thirdparty/libuv/build/{}/uv_a.lib'.format(env["target"].capitalize())),
|
||||||
'Advapi32.lib',
|
'Advapi32.lib',
|
||||||
|
|
|
@ -16,14 +16,14 @@ while [[ $# -gt 0 ]]; do
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: ./build.sh [-t|--target <release|debug>] [--disable_pty]";
|
echo "Usage: ./build.sh [-t|--target <release|editor>] [--disable_pty]";
|
||||||
exit 128
|
exit 128
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
# Set defaults.
|
# Set defaults.
|
||||||
target=${target:-debug}
|
target=${target:-editor}
|
||||||
disable_pty=${disable_pty:-no}
|
disable_pty=${disable_pty:-no}
|
||||||
nproc=$(nproc || sysctl -n hw.ncpu)
|
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.
|
#GODOT_DIR Get the absolute path to the directory this script is in.
|
||||||
NATIVE_DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
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.
|
# Run script inside a nix shell if it is available.
|
||||||
if command -v nix-shell && [ $NIX_PATH ] && [ -z $IN_NIX_SHELL ]; then
|
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.
|
# Build godot-cpp bindings.
|
||||||
cd ${GODOT_CPP_DIR}
|
# FIXME: Commented out to improve build time, but needs to be uncommented and run for initial build of godot-cpp.
|
||||||
echo "scons generate_bindings=yes target=$target -j$nproc"
|
#cd ${GODOT_CPP_DIR}
|
||||||
scons generate_bindings=yes macos_arch=$(uname -m) target=$target -j$nproc
|
#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.
|
# Build libuv as a static library.
|
||||||
cd ${LIBUV_DIR}
|
cd ${LIBUV_DIR}
|
||||||
|
@ -80,7 +84,7 @@ cd ${NATIVE_DIR}
|
||||||
scons target=$target macos_arch=$(uname -m) disable_pty=$disable_pty -j$nproc
|
scons target=$target macos_arch=$(uname -m) disable_pty=$disable_pty -j$nproc
|
||||||
|
|
||||||
# Use Docker to build libgodot-xterm javascript.
|
# Use Docker to build libgodot-xterm javascript.
|
||||||
if [ -x "$(command -v docker-compose)" ]; then
|
#if [ -x "$(command -v docker-compose)" ]; then
|
||||||
UID_GID="0:0" TARGET=$target docker-compose build javascript
|
# UID_GID="0:0" TARGET=$target docker-compose build javascript
|
||||||
UID_GID="$(id -u):$(id -g)" TARGET=$target docker-compose run --rm javascript
|
# UID_GID="$(id -u):$(id -g)" TARGET=$target docker-compose run --rm javascript
|
||||||
fi
|
#fi
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "libuv_utils.h"
|
#include "libuv_utils.h"
|
||||||
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
void LibuvUtils::_register_methods() {
|
void LibuvUtils::_bind_methods() {
|
||||||
register_method("_init", &LibuvUtils::_init);
|
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);
|
ClassDB::bind_static_method("LibuvUtils", D_METHOD("kill", "pid", "signum"), &LibuvUtils::kill);
|
||||||
register_method("get_os_release", &LibuvUtils::get_os_release);
|
|
||||||
register_method("get_cwd", &LibuvUtils::get_cwd);
|
|
||||||
|
|
||||||
register_method("kill", &LibuvUtils::kill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LibuvUtils::LibuvUtils() {}
|
LibuvUtils::LibuvUtils() {}
|
||||||
LibuvUtils::~LibuvUtils() {}
|
LibuvUtils::~LibuvUtils() {}
|
||||||
|
|
||||||
void LibuvUtils::_init() {}
|
|
||||||
|
|
||||||
Dictionary LibuvUtils::get_os_environ() {
|
Dictionary LibuvUtils::get_os_environ() {
|
||||||
Dictionary result;
|
Dictionary result;
|
||||||
|
|
||||||
|
@ -63,50 +63,50 @@ String LibuvUtils::get_cwd() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
godot_error LibuvUtils::kill(int pid, int signum) {
|
Error LibuvUtils::kill(int pid, int signum) {
|
||||||
RETURN_UV_ERR(uv_kill(pid, 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)
|
if (uv_err >= 0)
|
||||||
return GODOT_OK;
|
return OK;
|
||||||
|
|
||||||
// Rough translation of libuv error to godot error.
|
// Rough translation of libuv error to godot error.
|
||||||
// Not necessarily accurate.
|
// Not necessarily accurate.
|
||||||
|
|
||||||
switch (uv_err) {
|
switch (uv_err) {
|
||||||
case UV_EEXIST: // file already exists
|
case UV_EEXIST: // file already exists
|
||||||
return GODOT_ERR_ALREADY_EXISTS;
|
return ERR_ALREADY_EXISTS;
|
||||||
|
|
||||||
case UV_EADDRINUSE: // address already in use
|
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_EBUSY: // resource busy or locked
|
||||||
case UV_ETXTBSY: // text file is busy
|
case UV_ETXTBSY: // text file is busy
|
||||||
return GODOT_ERR_BUSY;
|
return ERR_BUSY;
|
||||||
|
|
||||||
case UV_ECONNREFUSED: // connection refused
|
case UV_ECONNREFUSED: // connection refused
|
||||||
return GODOT_ERR_CANT_CONNECT;
|
return ERR_CANT_CONNECT;
|
||||||
|
|
||||||
case UV_ECONNABORTED: // software caused connection abort
|
case UV_ECONNABORTED: // software caused connection abort
|
||||||
case UV_ECONNRESET: // connection reset by peer
|
case UV_ECONNRESET: // connection reset by peer
|
||||||
case UV_EISCONN: // socket is already connected
|
case UV_EISCONN: // socket is already connected
|
||||||
case UV_ENOTCONN: // socket is not 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_ENODEV: // no such device
|
||||||
case UV_ENXIO: // no such device or address
|
case UV_ENXIO: // no such device or address
|
||||||
case UV_ESRCH: // no such process
|
case UV_ESRCH: // no such process
|
||||||
return GODOT_ERR_DOES_NOT_EXIST;
|
return ERR_DOES_NOT_EXIST;
|
||||||
|
|
||||||
case UV_EROFS: // read-only file system
|
case UV_EROFS: // read-only file system
|
||||||
return GODOT_ERR_FILE_CANT_WRITE;
|
return ERR_FILE_CANT_WRITE;
|
||||||
|
|
||||||
case UV_EOF: // end of file
|
case UV_EOF: // end of file
|
||||||
return GODOT_ERR_FILE_EOF;
|
return ERR_FILE_EOF;
|
||||||
|
|
||||||
case UV_ENOENT: // no such file or directory
|
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_BADFLAGS: // bad ai_flags value
|
||||||
case UV_EAI_BADHINTS: // invalid value for hints
|
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_EINVAL: // invalid argument
|
||||||
case UV_ENOTTY: // inappropriate ioctl for device
|
case UV_ENOTTY: // inappropriate ioctl for device
|
||||||
case UV_EPROTOTYPE: // protocol wrong type for socket
|
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
|
case UV_ENOSYS: // function not implemented
|
||||||
return GODOT_ERR_METHOD_NOT_FOUND;
|
return ERR_METHOD_NOT_FOUND;
|
||||||
|
|
||||||
case UV_EAI_MEMORY: // out of memory
|
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_E2BIG: // argument list too long
|
||||||
case UV_EFBIG: // file too large
|
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_ENAMETOOLONG: // name too long
|
||||||
case UV_EOVERFLOW: // value too large for defined data type
|
case UV_EOVERFLOW: // value too large for defined data type
|
||||||
case UV_ERANGE: // result too large
|
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:
|
case UV_ETIMEDOUT:
|
||||||
return GODOT_ERR_TIMEOUT; // connection timed out
|
return ERR_TIMEOUT; // connection timed out
|
||||||
|
|
||||||
case UV_EACCES: // permission denied
|
case UV_EACCES: // permission denied
|
||||||
case UV_EPERM: // operation not permitted
|
case UV_EPERM: // operation not permitted
|
||||||
case UV_EXDEV: // cross-device link 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_EADDRNOTAVAIL: // address not available
|
||||||
case UV_EAFNOSUPPORT: // address family not supported
|
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_ENOTSUP: // operation not supported on socket
|
||||||
case UV_EPROTONOSUPPORT: // protocol not supported
|
case UV_EPROTONOSUPPORT: // protocol not supported
|
||||||
case UV_ESOCKTNOSUPPORT: // socket type 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
|
// unsupported/unavailable
|
||||||
|
|
||||||
case UV_EAI_NODATA: // no address
|
case UV_EAI_NODATA: // no address
|
||||||
case UV_EDESTADDRREQ: // destination address required
|
case UV_EDESTADDRREQ: // destination address required
|
||||||
return GODOT_ERR_UNCONFIGURED;
|
return ERR_UNCONFIGURED;
|
||||||
|
|
||||||
case UV_EAI_AGAIN: // temporary failure
|
case UV_EAI_AGAIN: // temporary failure
|
||||||
case UV_EAI_CANCELED: // request canceled
|
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_ESPIPE: // invalid seek
|
||||||
case UV_UNKNOWN: // unknown error
|
case UV_UNKNOWN: // unknown error
|
||||||
default:
|
default:
|
||||||
return GODOT_FAILED; // Generic fail error
|
return FAILED; // Generic fail error
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef GODOT_XTERM_UV_UTILS_H
|
#ifndef GODOT_XTERM_UV_UTILS_H
|
||||||
#define GODOT_XTERM_UV_UTILS_H
|
#define GODOT_XTERM_UV_UTILS_H
|
||||||
|
|
||||||
#include <Godot.hpp>
|
#include <godot_cpp/classes/ref_counted.hpp>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
#define UV_ERR_PRINT(uv_err) \
|
#define UV_ERR_PRINT(uv_err) \
|
||||||
|
@ -21,25 +21,24 @@
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
|
|
||||||
class LibuvUtils : public Reference {
|
class LibuvUtils : public RefCounted {
|
||||||
GODOT_CLASS(LibuvUtils, Reference)
|
GDCLASS(LibuvUtils, RefCounted)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void _register_methods();
|
|
||||||
|
|
||||||
LibuvUtils();
|
LibuvUtils();
|
||||||
~LibuvUtils();
|
~LibuvUtils();
|
||||||
|
|
||||||
void _init();
|
static Dictionary get_os_environ();
|
||||||
|
static String get_os_release();
|
||||||
|
static String get_cwd();
|
||||||
|
|
||||||
Dictionary get_os_environ();
|
static Error kill(int pid, int signum);
|
||||||
String get_os_release();
|
|
||||||
String get_cwd();
|
|
||||||
|
|
||||||
godot_error kill(int pid, int signum);
|
|
||||||
|
|
||||||
public:
|
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
|
} // namespace godot
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "pty.h"
|
#include "pty.h"
|
||||||
#include "libuv_utils.h"
|
#include "libuv_utils.h"
|
||||||
#include <FuncRef.hpp>
|
#include <godot_cpp/variant/callable.hpp>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -89,7 +89,7 @@ using namespace godot;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct pty_baton {
|
struct pty_baton {
|
||||||
Ref<FuncRef> cb;
|
Callable cb;
|
||||||
int exit_code;
|
int exit_code;
|
||||||
int signal_code;
|
int signal_code;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
@ -119,11 +119,11 @@ static void pty_after_waitpid(uv_async_t *);
|
||||||
|
|
||||||
static void pty_after_close(uv_handle_t *);
|
static void pty_after_close(uv_handle_t *);
|
||||||
|
|
||||||
Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args,
|
Array PTYUnix::fork(String p_file, int _ignored, PackedStringArray p_args,
|
||||||
PoolStringArray p_env, String p_cwd, int p_cols, int p_rows,
|
PackedStringArray p_env, String p_cwd, int p_cols, int p_rows,
|
||||||
int p_uid, int p_gid, bool p_utf8, Ref<FuncRef> p_on_exit) {
|
int p_uid, int p_gid, bool p_utf8, Callable p_on_exit) {
|
||||||
// file
|
// file
|
||||||
char *file = p_file.alloc_c_string();
|
char *file = strdup(p_file.utf8().get_data());
|
||||||
|
|
||||||
// args
|
// args
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -133,7 +133,7 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args,
|
||||||
argv[0] = strdup(file);
|
argv[0] = strdup(file);
|
||||||
argv[argl - 1] = NULL;
|
argv[argl - 1] = NULL;
|
||||||
for (; i < argc; i++) {
|
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);
|
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];
|
char **env = new char *[envc + 1];
|
||||||
env[envc] = NULL;
|
env[envc] = NULL;
|
||||||
for (; i < envc; i++) {
|
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);
|
env[i] = strdup(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cwd
|
// cwd
|
||||||
char *cwd = strdup(p_cwd.alloc_c_string());
|
char *cwd = strdup(p_cwd.utf8().get_data());
|
||||||
|
|
||||||
// size
|
// size
|
||||||
struct winsize winp;
|
struct winsize winp;
|
||||||
|
@ -240,7 +240,7 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args,
|
||||||
switch (pid) {
|
switch (pid) {
|
||||||
case -1:
|
case -1:
|
||||||
ERR_PRINT("forkpty(3) failed.");
|
ERR_PRINT("forkpty(3) failed.");
|
||||||
return Array::make(GODOT_FAILED);
|
return Array::make(FAILED);
|
||||||
case 0:
|
case 0:
|
||||||
if (strlen(cwd)) {
|
if (strlen(cwd)) {
|
||||||
if (chdir(cwd) == -1) {
|
if (chdir(cwd) == -1) {
|
||||||
|
@ -267,10 +267,10 @@ Array PTYUnix::fork(String p_file, int _ignored, PoolStringArray p_args,
|
||||||
default:
|
default:
|
||||||
if (pty_nonblock(master) == -1) {
|
if (pty_nonblock(master) == -1) {
|
||||||
ERR_PRINT("Could not set master fd to nonblocking.");
|
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["fd"] = (int)master;
|
||||||
result["pid"] = (int)pid;
|
result["pid"] = (int)pid;
|
||||||
result["pty"] = ptsname(master);
|
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<void *>(baton));
|
uv_thread_create(&baton->tid, pty_waitpid, static_cast<void *>(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) {
|
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) {
|
if (ret == -1) {
|
||||||
ERR_PRINT("openpty(3) failed.");
|
ERR_PRINT("openpty(3) failed.");
|
||||||
return Array::make(GODOT_FAILED);
|
return Array::make(FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pty_nonblock(master) == -1) {
|
if (pty_nonblock(master) == -1) {
|
||||||
ERR_PRINT("Could not set master fd to nonblocking.");
|
ERR_PRINT("Could not set master fd to nonblocking.");
|
||||||
return Array::make(GODOT_FAILED);
|
return Array::make(FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pty_nonblock(slave) == -1) {
|
if (pty_nonblock(slave) == -1) {
|
||||||
ERR_PRINT("Could not set slave fd to nonblocking.");
|
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["master"] = master;
|
||||||
dict["slave"] = slave;
|
dict["slave"] = slave;
|
||||||
dict["pty"] = ptsname(master);
|
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;
|
int fd = p_fd;
|
||||||
|
|
||||||
struct winsize winp;
|
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);
|
RETURN_UV_ERR(UV_ENOTTY);
|
||||||
}
|
}
|
||||||
ERR_PRINT("ioctl(2) failed");
|
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) {
|
String PTYUnix::process(int p_fd, String p_tty) {
|
||||||
int fd = p_fd;
|
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);
|
char *name = pty_getproc(fd, tty);
|
||||||
std::free(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);
|
Array argv = Array::make(baton->exit_code, baton->signal_code);
|
||||||
|
|
||||||
if (baton->cb != nullptr && baton->cb->is_valid()) {
|
if (baton->cb != nullptr && baton->cb.is_valid()) {
|
||||||
baton->cb->call_funcv(argv);
|
baton->cb.callv(argv);
|
||||||
baton->cb = (Ref<FuncRef>)nullptr;
|
baton->cb = (Variant)nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_close((uv_handle_t *)async, pty_after_close);
|
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
|
* Init
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void PTYUnix::_register_methods() {
|
void PTYUnix::_bind_methods() {
|
||||||
register_method("_init", &PTYUnix::_init);
|
ClassDB::bind_method(D_METHOD("_init"), &PTYUnix::_init);
|
||||||
register_method("fork", &PTYUnix::fork);
|
ClassDB::bind_method(D_METHOD("fork"), &PTYUnix::fork);
|
||||||
register_method("open", &PTYUnix::open);
|
ClassDB::bind_method(D_METHOD("open"), &PTYUnix::open);
|
||||||
register_method("resize", &PTYUnix::resize);
|
ClassDB::bind_method(D_METHOD("resize"), &PTYUnix::resize);
|
||||||
register_method("process", &PTYUnix::process);
|
ClassDB::bind_method(D_METHOD("process"), &PTYUnix::process);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PTYUnix::_init() {}
|
void PTYUnix::_init() {}
|
|
@ -1,29 +1,32 @@
|
||||||
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef GODOT_XTERM_PTY_H
|
#ifndef GODOT_XTERM_PTY_H
|
||||||
#define GODOT_XTERM_PTY_H
|
#define GODOT_XTERM_PTY_H
|
||||||
|
|
||||||
#include <FuncRef.hpp>
|
#include <godot_cpp/classes/ref_counted.hpp>
|
||||||
#include <Godot.hpp>
|
#include <godot_cpp/variant/callable.hpp>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
|
|
||||||
class PTYUnix : public Reference {
|
class PTYUnix : public RefCounted {
|
||||||
GODOT_CLASS(PTYUnix, Reference)
|
GDCLASS(PTYUnix, RefCounted)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Array fork(String file,
|
Array fork(String file,
|
||||||
int _ignored, /* FIXME: For some reason Pipe throws
|
int _ignored, /* FIXME: For some reason Pipe throws
|
||||||
ENOTSOCK in read callback if args (or another non-empty,
|
ENOTSOCK in read callback if args (or another non-empty,
|
||||||
non-zero) value is in this position. */
|
non-zero) value is in this position. */
|
||||||
PoolStringArray args, PoolStringArray env, String cwd, int cols,
|
PackedStringArray args, PackedStringArray env, String cwd, int cols,
|
||||||
int rows, int uid, int gid, bool utf8, Ref<FuncRef> on_exit);
|
int rows, int uid, int gid, bool utf8, Callable on_exit);
|
||||||
Array open(int cols, int rows);
|
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);
|
String process(int fd, String tty);
|
||||||
|
|
||||||
void _init();
|
void _init();
|
||||||
static void _register_methods();
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
// SPDX-FileCopyrightText: 2021 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef GODOT_XTERM_CONPTY_H
|
#ifndef GODOT_XTERM_CONPTY_H
|
||||||
#define GODOT_XTERM_CONPTY_H
|
#define GODOT_XTERM_CONPTY_H
|
||||||
|
|
||||||
#include <FuncRef.hpp>
|
#include <godot_cpp/classes/ref_counted.hpp>
|
||||||
#include <Godot.hpp>
|
#include <godot_cpp/variant/callable.hpp>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
|
|
||||||
class ConPTY : public Reference {
|
class ConPTY : public RefCounted {
|
||||||
GODOT_CLASS(ConPTY, Reference)
|
GDCLASS(ConPTY, RefCounted)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Array fork(String file,
|
// Array fork(String file,
|
||||||
|
@ -25,7 +26,9 @@ public:
|
||||||
// String process(int fd, String tty);
|
// String process(int fd, String tty);
|
||||||
|
|
||||||
void _init();
|
void _init();
|
||||||
static void _register_methods();
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "libuv_utils.h"
|
#include "libuv_utils.h"
|
||||||
#include <Dictionary.hpp>
|
#include <godot_cpp/variant/dictionary.hpp>
|
||||||
#include <InputEventKey.hpp>
|
#include <godot_cpp/variant/packed_byte_array.hpp>
|
||||||
#include <OS.hpp>
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
#include <ResourceLoader.hpp>
|
#include <godot_cpp/classes/input_event_key.hpp>
|
||||||
#include <Theme.hpp>
|
#include <godot_cpp/classes/os.hpp>
|
||||||
#include <Timer.hpp>
|
#include <godot_cpp/classes/resource_loader.hpp>
|
||||||
|
#include <godot_cpp/classes/theme.hpp>
|
||||||
|
#include <godot_cpp/classes/timer.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -19,17 +21,16 @@
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
void Pipe::_register_methods() {
|
void Pipe::_bind_methods() {
|
||||||
register_method("_init", &Pipe::_init);
|
ClassDB::bind_method(D_METHOD("_init"), &Pipe::_init);
|
||||||
|
|
||||||
register_method("poll", &Pipe::_poll_connection);
|
ClassDB::bind_method(D_METHOD("poll"), &Pipe::_poll_connection);
|
||||||
register_method("open", &Pipe::open);
|
ClassDB::bind_method(D_METHOD("open"), &Pipe::open);
|
||||||
register_method("write", &Pipe::write);
|
ClassDB::bind_method(D_METHOD("write"), &Pipe::write);
|
||||||
register_method("get_status", &Pipe::get_status);
|
ClassDB::bind_method(D_METHOD("get_status"), &Pipe::get_status);
|
||||||
register_method("close", &Pipe::close);
|
ClassDB::bind_method(D_METHOD("close"), &Pipe::close);
|
||||||
|
|
||||||
register_signal<Pipe>("data_received", "data",
|
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
||||||
GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::Pipe() {}
|
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);
|
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));
|
RETURN_IF_UV_ERR(uv_pipe_init(uv_default_loop(), &handle, ipc));
|
||||||
|
|
||||||
handle.data = this;
|
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));
|
uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb));
|
||||||
|
|
||||||
status = 1;
|
status = 1;
|
||||||
return GODOT_OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pipe::close() {
|
void Pipe::close() {
|
||||||
|
@ -66,8 +67,8 @@ void Pipe::close() {
|
||||||
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
godot_error Pipe::write(PoolByteArray data) {
|
Error Pipe::write(PackedByteArray data) {
|
||||||
char *s = (char *)data.read().ptr();
|
char *s = (char *)data.ptr();
|
||||||
ULONG len = data.size();
|
ULONG len = data.size();
|
||||||
|
|
||||||
uv_buf_t buf;
|
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_write(req, (uv_stream_t *)&handle, &buf, 1, _write_cb);
|
||||||
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
||||||
|
|
||||||
return GODOT_OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Pipe::get_status() {
|
int Pipe::get_status() {
|
||||||
|
@ -116,9 +117,9 @@ void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolByteArray data;
|
PackedByteArray data;
|
||||||
data.resize(nread);
|
data.resize(nread);
|
||||||
{ memcpy(data.write().ptr(), buf->base, nread); }
|
{ memcpy(data.ptrw(), buf->base, nread); }
|
||||||
std::free((char *)buf->base);
|
std::free((char *)buf->base);
|
||||||
|
|
||||||
pipe->emit_signal("data_received", data);
|
pipe->emit_signal("data_received", data);
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef GODOT_XTERM_PIPE_H
|
#ifndef GODOT_XTERM_PIPE_H
|
||||||
#define GODOT_XTERM_PIPE_H
|
#define GODOT_XTERM_PIPE_H
|
||||||
|
|
||||||
#include <Godot.hpp>
|
#include <godot_cpp/classes/ref_counted.hpp>
|
||||||
#include <Reference.hpp>
|
#include <godot_cpp/variant/packed_byte_array.hpp>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
|
|
||||||
class Pipe : public Reference {
|
class Pipe : public RefCounted {
|
||||||
GODOT_CLASS(Pipe, Reference)
|
GDCLASS(Pipe, RefCounted)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uv_pipe_t handle;
|
uv_pipe_t handle;
|
||||||
|
|
||||||
static void _register_methods();
|
|
||||||
|
|
||||||
Pipe();
|
Pipe();
|
||||||
~Pipe();
|
~Pipe();
|
||||||
|
|
||||||
void _init();
|
void _init();
|
||||||
|
|
||||||
godot_error open(int fd, bool ipc);
|
Error open(int fd, bool ipc);
|
||||||
void close();
|
void close();
|
||||||
int get_status();
|
int get_status();
|
||||||
|
|
||||||
godot_error write(PoolByteArray data);
|
Error write(PackedByteArray data);
|
||||||
|
|
||||||
void pause();
|
void pause();
|
||||||
void resume();
|
void resume();
|
||||||
|
@ -34,10 +33,13 @@ public:
|
||||||
public:
|
public:
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _poll_connection();
|
void _poll_connection();
|
||||||
|
|
||||||
static godot_error _translate_error(int err);
|
static Error _translate_error(int err);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
60
addons/godot_xterm/native/src/register_types.cpp
Normal file
60
addons/godot_xterm/native/src/register_types.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2022, Leroy Hopson (MIT License).
|
||||||
|
|
||||||
|
#include "register_types.h"
|
||||||
|
|
||||||
|
#include <gdextension_interface.h>
|
||||||
|
|
||||||
|
#include <godot_cpp/core/class_db.hpp>
|
||||||
|
#include <godot_cpp/core/defs.hpp>
|
||||||
|
#include <godot_cpp/godot.hpp>
|
||||||
|
|
||||||
|
#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<Terminal>();
|
||||||
|
#if !defined(_PTY_DISABLED)
|
||||||
|
ClassDB::register_class<Pipe>();
|
||||||
|
ClassDB::register_class<LibuvUtils>();
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
ClassDB::register_class<PTYUnix>();
|
||||||
|
#endif
|
||||||
|
#if defined(__WIN32)
|
||||||
|
//ClassDB::register_class<ConPTY>();
|
||||||
|
#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();
|
||||||
|
}
|
11
addons/godot_xterm/native/src/register_types.h
Normal file
11
addons/godot_xterm/native/src/register_types.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef GODOT_XTERM_REGISTER_TYPES_H
|
||||||
|
#define GODOT_XTERM_REGISTER_TYPES_H
|
||||||
|
|
||||||
|
#include <godot_cpp/core/class_db.hpp>
|
||||||
|
|
||||||
|
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
|
|
@ -1,19 +1,19 @@
|
||||||
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
||||||
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include <Dictionary.hpp>
|
#include <godot_cpp/variant/dictionary.hpp>
|
||||||
#include <InputEventKey.hpp>
|
#include <godot_cpp/classes/input_event_key.hpp>
|
||||||
#include <InputEventMouseButton.hpp>
|
#include <godot_cpp/classes/input_event_mouse_button.hpp>
|
||||||
#include <OS.hpp>
|
#include <godot_cpp/classes/os.hpp>
|
||||||
#include <ResourceLoader.hpp>
|
#include <godot_cpp/classes/resource_loader.hpp>
|
||||||
#include <Theme.hpp>
|
#include <godot_cpp/classes/theme.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
|
||||||
// For _populate_key_list(), see below.
|
// For _populate_key_list(), see below.
|
||||||
#if !defined(__EMSCRIPTEN__) && !defined(__APPLE__)
|
#if !defined(__EMSCRIPTEN__) && !defined(__APPLE__)
|
||||||
#include <GlobalConstants.hpp>
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
@ -23,22 +23,8 @@ void Terminal::_populate_key_list() {
|
||||||
if (!_key_list.empty())
|
if (!_key_list.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// The following error is thrown on the javascript platform when using
|
// TODO: Remove GLOBAL_CONSTANT macro.
|
||||||
// GlobalConstants from the header: abort(Assertion failed: bad export type for
|
#define GLOBAL_CONSTANT(VAR) VAR
|
||||||
// `_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
|
|
||||||
|
|
||||||
// Godot does not have seperate scancodes for keypad keys when NumLock is off.
|
// 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
|
// 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) {
|
void *data) {
|
||||||
Terminal *term = static_cast<Terminal *>(data);
|
Terminal *term = static_cast<Terminal *>(data);
|
||||||
|
|
||||||
PoolByteArray bytes = PoolByteArray();
|
PackedByteArray bytes;
|
||||||
bytes.resize(len);
|
bytes.resize(len);
|
||||||
{ memcpy(bytes.write().ptr(), u8, len); }
|
{ memcpy(bytes.ptrw(), u8, len); }
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
if (term->input_event_key.is_valid()) {
|
if (term->input_event_key.is_valid()) {
|
||||||
|
@ -276,51 +262,53 @@ static void bell_cb(tsm_vte *_vte, void *data) {
|
||||||
terminal->emit_signal("bell");
|
terminal->emit_signal("bell");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::_register_methods() {
|
void Terminal::_bind_methods() {
|
||||||
register_method("_init", &Terminal::_init);
|
//ClassDB::bind_method(D_METHOD("_ready"), &Terminal::_ready);
|
||||||
register_method("_ready", &Terminal::_ready);
|
ClassDB::bind_method(D_METHOD("_notification"), &Terminal::_notification);
|
||||||
register_method("_notification", &Terminal::_notification);
|
ClassDB::bind_method(D_METHOD("__gui_input"), &Terminal::_gui_input);
|
||||||
register_method("_gui_input", &Terminal::_gui_input);
|
//ClassDB::bind_method(D_METHOD("_draw"), &Terminal::_draw);
|
||||||
register_method("_draw", &Terminal::_draw);
|
|
||||||
|
|
||||||
register_method("write", &Terminal::write);
|
ClassDB::bind_method(D_METHOD("write"), &Terminal::write);
|
||||||
|
|
||||||
register_method("sb_up", &Terminal::sb_up);
|
ClassDB::bind_method(D_METHOD("sb_up"), &Terminal::sb_up);
|
||||||
register_method("sb_down", &Terminal::sb_down);
|
ClassDB::bind_method(D_METHOD("sb_down"), &Terminal::sb_down);
|
||||||
register_method("sb_reset", &Terminal::sb_reset);
|
ClassDB::bind_method(D_METHOD("sb_reset"), &Terminal::sb_reset);
|
||||||
register_method("clear_sb", &Terminal::clear_sb);
|
ClassDB::bind_method(D_METHOD("clear_sb"), &Terminal::clear_sb);
|
||||||
|
|
||||||
register_method("start_selection", &Terminal::start_selection);
|
ClassDB::bind_method(D_METHOD("start_selection"), &Terminal::start_selection);
|
||||||
register_method("select_to_pointer", &Terminal::select_to_pointer);
|
ClassDB::bind_method(D_METHOD("select_to_pointer"), &Terminal::select_to_pointer);
|
||||||
register_method("reset_selection", &Terminal::reset_selection);
|
ClassDB::bind_method(D_METHOD("reset_selection"), &Terminal::reset_selection);
|
||||||
register_method("copy_selection", &Terminal::copy_selection);
|
ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection);
|
||||||
register_method("copy_all", &Terminal::copy_all);
|
ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all);
|
||||||
|
|
||||||
register_method("_update_theme", &Terminal::update_theme);
|
ClassDB::bind_method(D_METHOD("_update_theme"), &Terminal::update_theme);
|
||||||
register_method("_update_size", &Terminal::update_theme);
|
ClassDB::bind_method(D_METHOD("_update_size"), &Terminal::update_theme);
|
||||||
|
|
||||||
register_property<Terminal, Vector2>("cell_size", &Terminal::cell_size,
|
ClassDB::bind_method(D_METHOD("get_cell_size"), &Terminal::get_cell_size);
|
||||||
Vector2(0, 0));
|
ClassDB::bind_method(D_METHOD("get_rows"), &Terminal::get_rows);
|
||||||
register_property<Terminal, int>("rows", &Terminal::rows, 24);
|
ClassDB::bind_method(D_METHOD("get_cols"), &Terminal::get_cols);
|
||||||
register_property<Terminal, int>("cols", &Terminal::cols, 80);
|
ClassDB::bind_method(D_METHOD("get_update_mode"), &Terminal::get_update_mode);
|
||||||
register_property<Terminal, int>("update_mode", &Terminal::update_mode,
|
ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &Terminal::set_update_mode);
|
||||||
UpdateMode::AUTO);
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode"), "set_update_mode", "get_update_mode");
|
||||||
|
|
||||||
register_signal<Terminal>("data_sent", "data",
|
ADD_SIGNAL(MethodInfo("data_sent", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
||||||
GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
|
ADD_SIGNAL(MethodInfo("key_pressed", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"),
|
||||||
register_signal<Terminal>("key_pressed", "data",
|
PropertyInfo(Variant::OBJECT, "event")));
|
||||||
GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY, "event",
|
ADD_SIGNAL(MethodInfo("size_changed", PropertyInfo(Variant::VECTOR2, "new_size")));
|
||||||
GODOT_VARIANT_TYPE_OBJECT);
|
ADD_SIGNAL(MethodInfo("bell"));
|
||||||
register_signal<Terminal>("size_changed", "new_size",
|
|
||||||
GODOT_VARIANT_TYPE_VECTOR2);
|
|
||||||
register_signal<Terminal>("bell", Dictionary());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Terminal::Terminal() {}
|
|
||||||
|
|
||||||
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;
|
framebuffer_age = 0;
|
||||||
update_mode = UpdateMode::AUTO;
|
update_mode = UpdateMode::AUTO;
|
||||||
|
|
||||||
|
@ -358,16 +346,16 @@ void Terminal::_gui_input(Variant event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t scancode = k->get_scancode();
|
int64_t scancode = k->get_keycode();
|
||||||
int64_t unicode = k->get_unicode();
|
int64_t unicode = k->get_unicode();
|
||||||
uint32_t ascii = unicode <= 127 ? unicode : 0;
|
uint32_t ascii = unicode <= 127 ? unicode : 0;
|
||||||
|
|
||||||
unsigned int mods = 0;
|
unsigned int mods = 0;
|
||||||
if (k->get_alt())
|
if (k->is_alt_pressed())
|
||||||
mods |= TSM_ALT_MASK;
|
mods |= TSM_ALT_MASK;
|
||||||
if (k->get_control())
|
if (k->is_ctrl_pressed())
|
||||||
mods |= TSM_CONTROL_MASK;
|
mods |= TSM_CONTROL_MASK;
|
||||||
if (k->get_shift())
|
if (k->is_shift_pressed())
|
||||||
mods |= TSM_SHIFT_MASK;
|
mods |= TSM_SHIFT_MASK;
|
||||||
|
|
||||||
uint32_t keysym = mapkey({unicode, scancode});
|
uint32_t keysym = mapkey({unicode, scancode});
|
||||||
|
@ -418,8 +406,8 @@ void Terminal::update_theme() {
|
||||||
Color default_color) -> void {
|
Color default_color) -> void {
|
||||||
Color c;
|
Color c;
|
||||||
|
|
||||||
c = has_color(theme_color, "Terminal") ? get_color(theme_color, "Terminal")
|
c = has_theme_color(theme_color, "Terminal") ? get_theme_color(theme_color, "Terminal")
|
||||||
: has_color_override(theme_color) ? get_color(theme_color, "")
|
: has_theme_color_override(theme_color) ? get_theme_color(theme_color, "")
|
||||||
: (default_theme != nullptr &&
|
: (default_theme != nullptr &&
|
||||||
default_theme->has_color(theme_color, "Terminal"))
|
default_theme->has_color(theme_color, "Terminal"))
|
||||||
? default_theme->get_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 {
|
auto load_font = [this, default_theme](String font_style) -> void {
|
||||||
Ref<Font> fontref;
|
Ref<Font> fontref;
|
||||||
|
|
||||||
if (has_font(font_style, "Terminal")) {
|
if (has_theme_font(font_style, "Terminal")) {
|
||||||
fontref = get_font(font_style, "Terminal");
|
fontref = get_theme_font(font_style, "Terminal");
|
||||||
} else if (has_font_override(font_style)) {
|
} else if (has_theme_font_override(font_style)) {
|
||||||
fontref = get_font(font_style, "");
|
fontref = get_theme_font(font_style, "");
|
||||||
} else if (has_font("regular", "Terminal")) {
|
} else if (has_theme_font("regular", "Terminal")) {
|
||||||
fontref = get_font("regular", "Terminal");
|
fontref = get_theme_font("regular", "Terminal");
|
||||||
} else if (default_theme != nullptr &&
|
} else if (default_theme != nullptr &&
|
||||||
default_theme->has_font("regular", "Terminal")) {
|
default_theme->has_font("regular", "Terminal")) {
|
||||||
fontref = default_theme->get_font("regular", "Terminal");
|
fontref = default_theme->get_font("regular", "Terminal");
|
||||||
} else {
|
} else {
|
||||||
fontref = get_font("");
|
fontref = get_theme_font("");
|
||||||
}
|
}
|
||||||
|
|
||||||
fontmap.insert(std::pair<String, Ref<Font>>(font_style, fontref));
|
fontmap.insert(std::pair<String, Ref<Font>>(font_style, fontref));
|
||||||
|
@ -523,7 +511,7 @@ void Terminal::draw_foreground(int row, int col, char *ch,
|
||||||
const tsm_screen_attr *attr, Color fgcolor) {
|
const tsm_screen_attr *attr, Color fgcolor) {
|
||||||
/* Set the font */
|
/* Set the font */
|
||||||
|
|
||||||
Ref<Font> fontref = get_font("");
|
Ref<Font> fontref = get_theme_font("");
|
||||||
|
|
||||||
if (attr->bold && attr->italic) {
|
if (attr->bold && attr->italic) {
|
||||||
fontref = fontmap["bold_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();
|
int font_height = fontref.ptr()->get_height();
|
||||||
Vector2 foreground_pos =
|
Vector2 foreground_pos =
|
||||||
Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25);
|
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)
|
if (attr->underline)
|
||||||
draw_string(fontref, foreground_pos, "_", fgcolor);
|
draw_string(fontref, foreground_pos, "_", HORIZONTAL_ALIGNMENT_LEFT, -1, 16, fgcolor); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Color, Color> Terminal::get_cell_colors(const tsm_screen_attr *attr) {
|
std::pair<Color, Color> Terminal::get_cell_colors(const tsm_screen_attr *attr) {
|
||||||
|
@ -596,10 +584,10 @@ void Terminal::update_size() {
|
||||||
Ref<Font> fontref;
|
Ref<Font> fontref;
|
||||||
if (fontmap.count("regular"))
|
if (fontmap.count("regular"))
|
||||||
fontref = fontmap["regular"];
|
fontref = fontmap["regular"];
|
||||||
else if (has_font("regular", "Terminal"))
|
else if (has_theme_font("regular", "Terminal"))
|
||||||
fontref = get_font("regular", "Terminal");
|
fontref = get_theme_font("regular", "Terminal");
|
||||||
else
|
else
|
||||||
fontref = get_font("");
|
fontref = get_theme_font("");
|
||||||
|
|
||||||
cell_size = fontref->get_string_size("W");
|
cell_size = fontref->get_string_size("W");
|
||||||
|
|
||||||
|
@ -611,43 +599,43 @@ void Terminal::update_size() {
|
||||||
emit_signal("size_changed", Vector2(cols, rows));
|
emit_signal("size_changed", Vector2(cols, rows));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::write(PoolByteArray data) {
|
void Terminal::write(PackedByteArray data) {
|
||||||
tsm_vte_input(vte, (char *)data.read().ptr(), data.size());
|
tsm_vte_input(vte, (char *)data.ptr(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::sb_up(int num) {
|
void Terminal::sb_up(int num) {
|
||||||
tsm_screen_sb_up(screen, num);
|
tsm_screen_sb_up(screen, num);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::sb_down(int num) {
|
void Terminal::sb_down(int num) {
|
||||||
tsm_screen_sb_down(screen, num);
|
tsm_screen_sb_down(screen, num);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::sb_reset() {
|
void Terminal::sb_reset() {
|
||||||
tsm_screen_sb_reset(screen);
|
tsm_screen_sb_reset(screen);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::clear_sb() {
|
void Terminal::clear_sb() {
|
||||||
tsm_screen_clear_sb(screen);
|
tsm_screen_clear_sb(screen);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::start_selection(Vector2 position) {
|
void Terminal::start_selection(Vector2 position) {
|
||||||
tsm_screen_selection_start(screen, position.x, position.y);
|
tsm_screen_selection_start(screen, position.x, position.y);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::select_to_pointer(Vector2 position) {
|
void Terminal::select_to_pointer(Vector2 position) {
|
||||||
tsm_screen_selection_target(screen, position.x, position.y);
|
tsm_screen_selection_target(screen, position.x, position.y);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::reset_selection() {
|
void Terminal::reset_selection() {
|
||||||
tsm_screen_selection_reset(screen);
|
tsm_screen_selection_reset(screen);
|
||||||
update();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
String Terminal::copy_selection() {
|
String Terminal::copy_selection() {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Copyright (c) 2021, Leroy Hopson (MIT License).
|
// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef TERMINAL_H
|
#ifndef TERMINAL_H
|
||||||
#define TERMINAL_H
|
#define TERMINAL_H
|
||||||
|
|
||||||
#include <Control.hpp>
|
#include <godot_cpp/classes/control.hpp>
|
||||||
#include <Font.hpp>
|
#include <godot_cpp/classes/font.hpp>
|
||||||
#include <Godot.hpp>
|
#include <godot_cpp/classes/input_event_key.hpp>
|
||||||
|
#include <godot_cpp/variant/packed_byte_array.hpp>
|
||||||
#include <libtsm.h>
|
#include <libtsm.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -13,12 +15,14 @@
|
||||||
namespace godot {
|
namespace godot {
|
||||||
|
|
||||||
class Terminal : public Control {
|
class Terminal : public Control {
|
||||||
GODOT_CLASS(Terminal, Control)
|
GDCLASS(Terminal, Control)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ref<InputEventKey> input_event_key;
|
Ref<InputEventKey> input_event_key;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
tsm_screen *screen;
|
tsm_screen *screen;
|
||||||
tsm_vte *vte;
|
tsm_vte *vte;
|
||||||
|
|
||||||
|
@ -42,18 +46,15 @@ public:
|
||||||
Color fgcol);
|
Color fgcol);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void _register_methods();
|
|
||||||
|
|
||||||
Terminal();
|
Terminal();
|
||||||
~Terminal();
|
~Terminal();
|
||||||
|
|
||||||
void _init();
|
|
||||||
void _ready();
|
void _ready();
|
||||||
void _notification(int what);
|
void _notification(int what);
|
||||||
void _gui_input(Variant event);
|
void _gui_input(Variant event);
|
||||||
void _draw();
|
void _draw();
|
||||||
|
|
||||||
void write(PoolByteArray data);
|
void write(PackedByteArray data);
|
||||||
|
|
||||||
void sb_up(int num);
|
void sb_up(int num);
|
||||||
void sb_down(int num);
|
void sb_down(int num);
|
||||||
|
@ -72,11 +73,16 @@ public:
|
||||||
ALL,
|
ALL,
|
||||||
ALL_NEXT_FRAME,
|
ALL_NEXT_FRAME,
|
||||||
};
|
};
|
||||||
|
int update_mode = UpdateMode::AUTO;
|
||||||
|
int get_update_mode();
|
||||||
|
void set_update_mode(int update_mode);
|
||||||
|
|
||||||
Vector2 cell_size;
|
Vector2 cell_size = Vector2(0, 0);
|
||||||
int rows;
|
Vector2 get_cell_size();
|
||||||
int cols;
|
int rows = 24;
|
||||||
int update_mode;
|
int get_rows();
|
||||||
|
int cols = 80;
|
||||||
|
int get_cols();
|
||||||
|
|
||||||
uint8_t color_palette[TSM_COLOR_NUM][3];
|
uint8_t color_palette[TSM_COLOR_NUM][3];
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 867374da404887337909e8b7b9de5a8acbc47569
|
Subproject commit 3da6db4fe41b8f3d3aaeb0dc80f1556f83fd7fe6
|
|
@ -6,14 +6,12 @@ extends Object
|
||||||
# GDNative does not currently support registering static functions so we fake it.
|
# GDNative does not currently support registering static functions so we fake it.
|
||||||
# Only the static functions of this class should be called.
|
# Only the static functions of this class should be called.
|
||||||
|
|
||||||
const LibuvUtils = preload("./libuv_utils.gdns")
|
|
||||||
|
|
||||||
|
|
||||||
static func get_os_environ() -> Dictionary:
|
static func get_os_environ() -> Dictionary:
|
||||||
# While Godot has OS.get_environment(), I could see a way to get all environent
|
# 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
|
# variables, other than by OS.execute() which would require to much platform
|
||||||
# specific code. Easier to use libuv's utility function.
|
# 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:
|
static func get_cwd() -> String:
|
||||||
|
|
|
@ -6,10 +6,6 @@
|
||||||
@tool
|
@tool
|
||||||
extends "../pty_native.gd"
|
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_NAME := "xterm-256color"
|
||||||
const DEFAULT_COLS := 80
|
const DEFAULT_COLS := 80
|
||||||
const DEFAULT_ROWS := 24
|
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,
|
# 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.
|
# these are just the signals with numbers specified in the POSIX standard.
|
||||||
enum Signal {
|
enum IPCSignal {
|
||||||
SIGHUP = 1, # Hangup
|
SIGHUP = 1, # Hangup
|
||||||
SIGINT = 2, # Terminal interrupt signal
|
SIGINT = 2, # Terminal interrupt signal
|
||||||
SIGQUIT = 3, # Terminal quit signal
|
SIGQUIT = 3, # Terminal quit signal
|
||||||
|
@ -68,10 +64,7 @@ var _exit_cb: Callable
|
||||||
# Writes data to the socket.
|
# Writes data to the socket.
|
||||||
# data: The data to write.
|
# data: The data to write.
|
||||||
func write(data) -> void:
|
func write(data) -> void:
|
||||||
assert(
|
assert(data is PackedByteArray or data is String, "Invalid type for argument 'data'. Should be of type PackedByteArray or String")
|
||||||
data is PackedByteArray or data is String,
|
|
||||||
"Invalid type for argument 'data'. Should be of type PackedByteArray or String"
|
|
||||||
)
|
|
||||||
if _pipe:
|
if _pipe:
|
||||||
_pipe.write(data if data is PackedByteArray else data.to_utf8_buffer())
|
_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)
|
PTYUnix.new().resize(_fd, cols, rows)
|
||||||
|
|
||||||
|
|
||||||
func kill(signum: int = Signal.SIGHUP) -> void:
|
func kill(signum: int = IPCSignal.SIGHUP) -> void:
|
||||||
if _pipe:
|
if _pipe:
|
||||||
_pipe.close()
|
_pipe.close()
|
||||||
if _pid > 0:
|
if _pid > 0:
|
||||||
|
@ -133,13 +126,12 @@ func fork(
|
||||||
var parsed_env: PackedStringArray = _parse_env(final_env)
|
var parsed_env: PackedStringArray = _parse_env(final_env)
|
||||||
|
|
||||||
# Exit callback.
|
# Exit callback.
|
||||||
_exit_cb = Callable.new()
|
_exit_cb = Callable(self, "on_exit")
|
||||||
_exit_cb.set_instance(self)
|
|
||||||
_exit_cb.set_function("_on_exit")
|
|
||||||
|
|
||||||
# Actual fork.
|
# 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.
|
var result = PTYUnix.new().fork(
|
||||||
file, null, args, parsed_env, cwd, cols, rows, uid, gid, utf8, _exit_cb
|
# 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:
|
if result[0] != OK:
|
||||||
|
@ -154,7 +146,7 @@ func fork(
|
||||||
_pid = result[1].pid
|
_pid = result[1].pid
|
||||||
|
|
||||||
_pipe = Pipe.new()
|
_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.
|
# Must connect to signal AFTER opening, otherwise we will get error ENOTSOCK.
|
||||||
_pipe.connect("data_received",Callable(self,"_on_pipe_data_received"))
|
_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():
|
func _exit_tree():
|
||||||
_exit_cb = null
|
_exit_cb = null
|
||||||
if _pid > 1:
|
if _pid > 1:
|
||||||
LibuvUtils.kill(_pid, Signal.SIGHUP)
|
LibuvUtils.kill(_pid, IPCSignal.SIGHUP)
|
||||||
if _pipe:
|
if _pipe:
|
||||||
while _pipe.get_status() != 0:
|
while _pipe.get_status() != 0:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[gd_scene load_steps=2 format=3 uid="uid://c62updkby54f3"]
|
[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
|
script/source = "@tool
|
||||||
extends SubViewport
|
extends SubViewport
|
||||||
"
|
"
|
||||||
|
@ -11,12 +11,13 @@ handle_input_locally = false
|
||||||
gui_snap_controls_to_pixels = false
|
gui_snap_controls_to_pixels = false
|
||||||
size = Vector2i(2, 2)
|
size = Vector2i(2, 2)
|
||||||
render_target_clear_mode = 1
|
render_target_clear_mode = 1
|
||||||
script = SubResource("1")
|
script = SubResource("GDScript_d8lvm")
|
||||||
|
|
||||||
[node name="Terminal" type="Control" parent="."]
|
[node name="Terminal" type="Terminal" parent="."]
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_right = -2.0
|
offset_right = -2.0
|
||||||
offset_bottom = -2.0
|
offset_bottom = -2.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
|
@ -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,
|
# 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.
|
# 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 data_received(data)
|
||||||
signal exited(exit_code, signum)
|
signal exited(exit_code, signum)
|
||||||
|
@ -78,8 +78,8 @@ func _init():
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
if terminal_path and not _terminal:
|
if not (terminal_path.is_empty()) and not _terminal:
|
||||||
set_terminal_path(terminal_path)
|
self.terminal_path = terminal_path
|
||||||
|
|
||||||
|
|
||||||
func set_cols(value: int):
|
func set_cols(value: int):
|
||||||
|
@ -151,7 +151,7 @@ func resizev(size: Vector2) -> void:
|
||||||
# Kill the pty.
|
# Kill the pty.
|
||||||
# sigint: The signal to send. By default this is SIGHUP.
|
# sigint: The signal to send. By default this is SIGHUP.
|
||||||
# This is not supported on Windows.
|
# This is not supported on Windows.
|
||||||
func kill(signum: int = Signal.SIGHUP) -> void:
|
func kill(signum: int = IPCSignal.SIGHUP) -> void:
|
||||||
_pty_native.kill(signum)
|
_pty_native.kill(signum)
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ func _notification(what: int):
|
||||||
NOTIFICATION_PARENTED:
|
NOTIFICATION_PARENTED:
|
||||||
var parent = get_parent()
|
var parent = get_parent()
|
||||||
if parent is _Terminal:
|
if parent is _Terminal:
|
||||||
set_terminal_path(get_path_to(parent))
|
self.terminal_path = get_path_to(parent)
|
||||||
|
|
||||||
|
|
||||||
func fork(
|
func fork(
|
||||||
|
|
|
@ -27,8 +27,8 @@ enum SelectionMode {
|
||||||
@export var update_mode: UpdateMode = UpdateMode.AUTO :
|
@export var update_mode: UpdateMode = UpdateMode.AUTO :
|
||||||
get:
|
get:
|
||||||
return update_mode # TODOConverter40 Non existent get function
|
return update_mode # TODOConverter40 Non existent get function
|
||||||
set(mod_value):
|
set(p_update_mode):
|
||||||
mod_value # TODOConverter40 Copy here content of set_update_mode
|
set_update_mode(p_update_mode)
|
||||||
|
|
||||||
# If true, text in the terminal will be copied to the clipboard when selected.
|
# If true, text in the terminal will be copied to the clipboard when selected.
|
||||||
@export var copy_on_selection: bool
|
@export var copy_on_selection: bool
|
||||||
|
@ -90,7 +90,7 @@ func write(data) -> void:
|
||||||
func _flush():
|
func _flush():
|
||||||
for data in _buffer:
|
for data in _buffer:
|
||||||
_native_terminal.write(data if data is PackedByteArray else data.to_utf8_buffer())
|
_native_terminal.write(data if data is PackedByteArray else data.to_utf8_buffer())
|
||||||
_native_terminal.update()
|
_native_terminal.queue_redraw()
|
||||||
_buffer.clear()
|
_buffer.clear()
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,29 +170,30 @@ func _update_theme():
|
||||||
|
|
||||||
|
|
||||||
func _refresh():
|
func _refresh():
|
||||||
_screen.update()
|
_screen.queue_redraw()
|
||||||
if update_mode == UpdateMode.AUTO:
|
if update_mode == UpdateMode.AUTO:
|
||||||
_native_terminal.update_mode = UpdateMode.ALL_NEXT_FRAME
|
_native_terminal.update_mode = UpdateMode.ALL_NEXT_FRAME
|
||||||
|
|
||||||
|
|
||||||
func _gui_input(event):
|
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:
|
if event is InputEventKey and event.pressed:
|
||||||
# Return to bottom of scrollback buffer if we scrolled up. Ignore modifier
|
# Return to bottom of scrollback buffer if we scrolled up. Ignore modifier
|
||||||
# keys pressed in isolation or if Ctrl+Shift modifier keys are pressed.
|
# keys pressed in isolation or if Ctrl+Shift modifier keys are pressed.
|
||||||
if (
|
if (
|
||||||
not event.scancode in [KEY_ALT, KEY_SHIFT, KEY_CTRL, KEY_META, KEY_MASK_CMD_OR_CTRL]
|
not event.keycode in [KEY_ALT, KEY_SHIFT, KEY_CTRL, KEY_META, KEY_MASK_CMD_OR_CTRL]
|
||||||
and not (event.control and event.shift)
|
and not (event.ctrl_pressed and event.shift_pressed)
|
||||||
):
|
):
|
||||||
_native_terminal.sb_reset()
|
_native_terminal.sb_reset()
|
||||||
|
|
||||||
# Prevent focus changing to other inputs when pressing Tab or Arrow keys.
|
# 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()
|
accept_event()
|
||||||
|
|
||||||
_handle_mouse_wheel(event)
|
# FIXME
|
||||||
_handle_selection(event)
|
#_handle_mouse_wheel(event)
|
||||||
|
#_handle_selection(event)
|
||||||
|
|
||||||
|
|
||||||
func _handle_mouse_wheel(event: InputEventMouseButton):
|
func _handle_mouse_wheel(event: InputEventMouseButton):
|
||||||
|
|
|
@ -110,10 +110,10 @@ func draw_menu():
|
||||||
|
|
||||||
func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
|
func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
|
||||||
match data:
|
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))
|
selected_index = int(clamp(selected_index - 1, 0, menu_items.size() - 1))
|
||||||
draw_menu()
|
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))
|
selected_index = int(clamp(selected_index + 1, 0, menu_items.size() - 1))
|
||||||
draw_menu()
|
draw_menu()
|
||||||
"1":
|
"1":
|
||||||
|
@ -127,7 +127,7 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
|
||||||
draw_menu()
|
draw_menu()
|
||||||
|
|
||||||
# We can also match against the raw InputEventKey.
|
# We can also match against the raw InputEventKey.
|
||||||
if event.scancode == KEY_ENTER:
|
if event.keycode == KEY_ENTER:
|
||||||
var item = menu_items[selected_index]
|
var item = menu_items[selected_index]
|
||||||
|
|
||||||
match item.name:
|
match item.name:
|
||||||
|
@ -160,9 +160,11 @@ func _on_Terminal_key_pressed(data: String, event: InputEventKey) -> void:
|
||||||
$Terminal.grab_focus()
|
$Terminal.grab_focus()
|
||||||
scene.queue_free()
|
scene.queue_free()
|
||||||
"Exit":
|
"Exit":
|
||||||
if OS.has_feature("JavaScript"):
|
pass
|
||||||
JavaScript.eval("window.history.back() || window.close()")
|
# FIXME
|
||||||
get_tree().quit()
|
#if OS.has_feature("JavaScript"):
|
||||||
|
#JavaScript.eval("window.history.back() || window.close()")
|
||||||
|
#get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
func _on_Asciicast_key_pressed(
|
func _on_Asciicast_key_pressed(
|
||||||
|
|
|
@ -8,15 +8,17 @@ layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
script = ExtResource("2")
|
script = ExtResource("2")
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="Terminal" type="Control" parent="."]
|
[node name="Terminal" type="Control" parent="."]
|
||||||
layout_mode = 3
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
focus_mode = 1
|
focus_mode = 1
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
|
copy_on_selection = false
|
|
@ -29,10 +29,6 @@ config/icon="res://docs/media/icon.png"
|
||||||
|
|
||||||
window/vsync/use_vsync=false
|
window/vsync/use_vsync=false
|
||||||
|
|
||||||
[editor_plugins]
|
|
||||||
|
|
||||||
enabled=PackedStringArray()
|
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
quality/driver/driver_name="GLES2"
|
quality/driver/driver_name="GLES2"
|
||||||
|
|
16
shell.nix
Normal file
16
shell.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{ pkgs ? import <nixpkgs> { } }:
|
||||||
|
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
|
||||||
|
];
|
||||||
|
}
|
Loading…
Reference in a new issue