diff --git a/addons/godot_xterm/native/src_old/.gdignore b/addons/godot_xterm/native/src_old/.gdignore deleted file mode 100644 index e69de29..0000000 diff --git a/addons/godot_xterm/native/src_old/libuv_utils.cpp b/addons/godot_xterm/native/src_old/libuv_utils.cpp deleted file mode 100644 index 47ee934..0000000 --- a/addons/godot_xterm/native/src_old/libuv_utils.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2023 Leroy Hopson -// SPDX-License-Identifier: MIT - -#include "libuv_utils.h" -#include -#include - -using namespace godot; - -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); - - ClassDB::bind_static_method("LibuvUtils", D_METHOD("kill", "pid", "signum"), - &LibuvUtils::kill); -} - -LibuvUtils::LibuvUtils() {} -LibuvUtils::~LibuvUtils() {} - -Dictionary LibuvUtils::get_os_environ() { - Dictionary result; - - uv_env_item_t *env; - int count; - uv_os_environ(&env, &count); - - for (int i = 0; i < count; i++) { - result[String(env[i].name)] = String(env[i].value); - } - - uv_os_free_environ(env, count); - - return result; -} - -String LibuvUtils::get_os_release() { return "TODO"; } - -String LibuvUtils::get_cwd() { -#ifndef PATH_MAX -#define PATH_MAX MAX_PATH -#endif - size_t size = PATH_MAX; - char *buffer = (char *)malloc(size * sizeof(char)); - int err; - - err = uv_cwd(buffer, &size); - - if (err == UV_ENOBUFS) { - // Buffer was too small. `size` has been set to the required length, so - // resize buffer and try again. - buffer = (char *)realloc(buffer, size * sizeof(char)); - err = uv_cwd(buffer, &size); - } - - if (err < 0) { - UV_ERR_PRINT(err); - return ""; - } - - String result = String(buffer); - std::free(buffer); - return result; -} - -Error LibuvUtils::kill(int pid, int signum) { - RETURN_UV_ERR(uv_kill(pid, signum)); -} - -Error LibuvUtils::translate_uv_errno(int uv_err) { - if (uv_err >= 0) - return OK; - - // Rough translation of libuv error to godot error. - // Not necessarily accurate. - - switch (uv_err) { - case UV_EEXIST: // file already exists - return ERR_ALREADY_EXISTS; - - case UV_EADDRINUSE: // address already in use - return ERR_ALREADY_IN_USE; - - case UV_EBUSY: // resource busy or locked - case UV_ETXTBSY: // text file is busy - return ERR_BUSY; - - case UV_ECONNREFUSED: // connection refused - 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 ERR_CONNECTION_ERROR; - - case UV_ENODEV: // no such device - case UV_ENXIO: // no such device or address - case UV_ESRCH: // no such process - return ERR_DOES_NOT_EXIST; - - case UV_EROFS: // read-only file system - return ERR_FILE_CANT_WRITE; - - case UV_EOF: // end of file - return ERR_FILE_EOF; - - case UV_ENOENT: // no such file or directory - return ERR_FILE_NOT_FOUND; - - case UV_EAI_BADFLAGS: // bad ai_flags value - case UV_EAI_BADHINTS: // invalid value for hints - case UV_EFAULT: // bad address in system call argument - case UV_EFTYPE: // inappropriate file type or format - case UV_EINVAL: // invalid argument - case UV_ENOTTY: // inappropriate ioctl for device - case UV_EPROTOTYPE: // protocol wrong type for socket - return ERR_INVALID_PARAMETER; // Parameter passed is invalid - - case UV_ENOSYS: // function not implemented - return ERR_METHOD_NOT_FOUND; - - case UV_EAI_MEMORY: // out of memory - return ERR_OUT_OF_MEMORY; - - case UV_E2BIG: // argument list too long - case UV_EFBIG: // file too large - case UV_EMSGSIZE: // message too long - case UV_ENAMETOOLONG: // name too long - case UV_EOVERFLOW: // value too large for defined data type - case UV_ERANGE: // result too large - return ERR_PARAMETER_RANGE_ERROR; // Parameter given out of range - - case UV_ETIMEDOUT: - 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 ERR_UNAUTHORIZED; - - case UV_EADDRNOTAVAIL: // address not available - case UV_EAFNOSUPPORT: // address family not supported - case UV_EAGAIN: // resource temporarily unavailable - case UV_EAI_ADDRFAMILY: // address family not supported - case UV_EAI_FAMILY: // ai_family not supported - case UV_EAI_SERVICE: // service not available for socket type - case UV_EAI_SOCKTYPE: // socket type not supported - case UV_ENOPROTOOPT: // protocol not available - case UV_ENOTSUP: // operation not supported on socket - case UV_EPROTONOSUPPORT: // protocol not supported - case UV_ESOCKTNOSUPPORT: // socket type not supported - return ERR_UNAVAILABLE; // What is requested is - // unsupported/unavailable - - case UV_EAI_NODATA: // no address - case UV_EDESTADDRREQ: // destination address required - return ERR_UNCONFIGURED; - - case UV_EAI_AGAIN: // temporary failure - case UV_EAI_CANCELED: // request canceled - case UV_EAI_FAIL: // permanent failure - case UV_EAI_NONAME: // unknown node or service - case UV_EAI_OVERFLOW: // argument buffer overflow - case UV_EAI_PROTOCOL: // resolved protocol is unknown - case UV_EALREADY: // connection already in progress - case UV_EBADF: // bad file descriptor - case UV_ECANCELED: // operation canceled - case UV_ECHARSET: // invalid Unicode character - case UV_EHOSTUNREACH: // host is unreachable - case UV_EIO: // i/o error - case UV_EILSEQ: // illegal byte sequence - case UV_EISDIR: // illegal operation on a directory - case UV_ELOOP: // too many symbolic links encountered - case UV_EMFILE: // too many open files - case UV_ENETDOWN: // network is down - case UV_ENETUNREACH: // network is unreachable - case UV_ENFILE: // file table overflow - case UV_ENOBUFS: // no buffer space available - case UV_ENOMEM: // not enough memory - case UV_ESHUTDOWN: // cannot send after transport endpoint shutdown - case UV_EINTR: // interrupted system call - case UV_EMLINK: // too many links - case UV_ENONET: // machine is not on the network - case UV_ENOSPC: // no space left on device - case UV_ENOTDIR: // not a directory - case UV_ENOTEMPTY: // directory not empty - case UV_ENOTSOCK: // socket operation on non-socket - case UV_EPIPE: // broken pipe - case UV_EPROTO: // protocol error - case UV_ESPIPE: // invalid seek - case UV_UNKNOWN: // unknown error - default: - return FAILED; // Generic fail error - } -} diff --git a/addons/godot_xterm/native/src_old/libuv_utils.h b/addons/godot_xterm/native/src_old/libuv_utils.h deleted file mode 100644 index b15703c..0000000 --- a/addons/godot_xterm/native/src_old/libuv_utils.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef GODOT_XTERM_UV_UTILS_H -#define GODOT_XTERM_UV_UTILS_H - -#include -#include - -#define UV_ERR_PRINT(uv_err) \ - ERR_PRINT(String(uv_err_name(uv_err)) + String(": ") + \ - String(uv_strerror(uv_err))); - -#define RETURN_UV_ERR(uv_err) \ - if (uv_err < 0) { \ - UV_ERR_PRINT(uv_err); \ - } \ - return LibuvUtils::translate_uv_errno(uv_err); - -#define RETURN_IF_UV_ERR(uv_err) \ - if (uv_err < 0) { \ - RETURN_UV_ERR(uv_err); \ - } - -namespace godot { - -class LibuvUtils : public RefCounted { - GDCLASS(LibuvUtils, RefCounted) - -public: - LibuvUtils(); - ~LibuvUtils(); - - static Dictionary get_os_environ(); - static String get_os_release(); - static String get_cwd(); - - static Error kill(int pid, int signum); - -public: - static Error translate_uv_errno(int uv_err); - -protected: - static void _bind_methods(); -}; - -} // namespace godot - -#endif // GODOT_XTERM_UV_UTILS_H diff --git a/addons/godot_xterm/native/src_old/node_pty/LICENSE.md b/addons/godot_xterm/native/src_old/node_pty/LICENSE.md deleted file mode 100644 index e9c903a..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/LICENSE.md +++ /dev/null @@ -1,6 +0,0 @@ -The code under the `node_pty` directory is taken from the [node-pty project](https://github.com/microsoft/node-pty), which in turn contains code from the [Tmux project](http://tmux.sourceforge.net/). - -The code has been modified to remove references to node/V8 and make it compatible with GDNative. Any copyrightable modifications are released under the [same license](/addons/godot_xterm/LICENSE) as the rest of the GodotXterm project. - -The text of the node-pty license can be found in [LICENSE_node-pty](./LICENSE_node-pty) -The text of the tmux license can be found in [LICENSE_tmux](./LICENSE_tmux) diff --git a/addons/godot_xterm/native/src_old/node_pty/LICENSE_node-pty b/addons/godot_xterm/native/src_old/node_pty/LICENSE_node-pty deleted file mode 100644 index 22f780d..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/LICENSE_node-pty +++ /dev/null @@ -1,69 +0,0 @@ -Copyright (c) 2012-2015, Christopher Jeffrey (https://github.com/chjj/) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -The MIT License (MIT) - -Copyright (c) 2016, Daniel Imms (http://www.growingwiththeweb.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - -MIT License - -Copyright (c) 2018 - present Microsoft Corporation - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/addons/godot_xterm/native/src_old/node_pty/LICENSE_tmux b/addons/godot_xterm/native/src_old/node_pty/LICENSE_tmux deleted file mode 100644 index fcc5f51..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/LICENSE_tmux +++ /dev/null @@ -1,15 +0,0 @@ -Copyright (c) 2009 Nicholas Marriott -Copyright (c) 2009 Joshua Elsasser -Copyright (c) 2009 Todd Carson - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER -IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/addons/godot_xterm/native/src_old/node_pty/win/conpty.cc b/addons/godot_xterm/native/src_old/node_pty/win/conpty.cc deleted file mode 100644 index 1fedd61..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/conpty.cc +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Copyright (c) 2013-2015, Christopher Jeffrey, Peter Sunde (MIT License) - * Copyright (c) 2016, Daniel Imms (MIT License). - * Copyright (c) 2018, Microsoft Corporation (MIT License). - * Copyright (c) 2021, Leroy Hopson (MIT License). - * - * pty.cc: - * This file is responsible for starting processes - * with pseudo-terminal file descriptors. - */ - -// node versions lower than 10 define this as 0x502 which disables many of the -// definitions needed to compile -#include -#if NODE_MODULE_VERSION <= 57 -#define _WIN32_WINNT 0x600 -#endif - -#include "path_util.h" -#include // PathCombine, PathIsRelative -#include -#include -#include -#include -#include -#include -#include - -extern "C" void init(v8::Local); - -// Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= -// 17134 -#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE -#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE \ - ProcThreadAttributeValue(22, FALSE, TRUE, FALSE) - -typedef VOID *HPCON; -typedef HRESULT(__stdcall *PFNCREATEPSEUDOCONSOLE)(COORD c, HANDLE hIn, - HANDLE hOut, DWORD dwFlags, - HPCON *phpcon); -typedef HRESULT(__stdcall *PFNRESIZEPSEUDOCONSOLE)(HPCON hpc, COORD newSize); -typedef void(__stdcall *PFNCLOSEPSEUDOCONSOLE)(HPCON hpc); - -#endif - -struct pty_baton { - int id; - HANDLE hIn; - HANDLE hOut; - HPCON hpc; - - HANDLE hShell; - HANDLE hWait; - Nan::Callback cb; - uv_async_t async; - uv_thread_t tid; - - pty_baton(int _id, HANDLE _hIn, HANDLE _hOut, HPCON _hpc) - : id(_id), hIn(_hIn), hOut(_hOut), hpc(_hpc){}; -}; - -static std::vector ptyHandles; -static volatile LONG ptyCounter; - -static pty_baton *get_pty_baton(int id) { - for (size_t i = 0; i < ptyHandles.size(); ++i) { - pty_baton *ptyHandle = ptyHandles[i]; - if (ptyHandle->id == id) { - return ptyHandle; - } - } - return nullptr; -} - -template -std::vector vectorFromString(const std::basic_string &str) { - return std::vector(str.begin(), str.end()); -} - -void throwNanError(const Nan::FunctionCallbackInfo *info, - const char *text, const bool getLastError) { - std::stringstream errorText; - errorText << text; - if (getLastError) { - errorText << ", error code: " << GetLastError(); - } - Nan::ThrowError(errorText.str().c_str()); - (*info).GetReturnValue().SetUndefined(); -} - -// Returns a new server named pipe. It has not yet been connected. -bool createDataServerPipe(bool write, std::wstring kind, HANDLE *hServer, - std::wstring &name, const std::wstring &pipeName) { - *hServer = INVALID_HANDLE_VALUE; - - name = L"\\\\.\\pipe\\" + pipeName + L"-" + kind; - - const DWORD winOpenMode = - PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND | - FILE_FLAG_FIRST_PIPE_INSTANCE /* | FILE_FLAG_OVERLAPPED */; - - SECURITY_ATTRIBUTES sa = {}; - sa.nLength = sizeof(sa); - - *hServer = CreateNamedPipeW(name.c_str(), - /*dwOpenMode=*/winOpenMode, - /*dwPipeMode=*/PIPE_TYPE_BYTE | - PIPE_READMODE_BYTE | PIPE_WAIT, - /*nMaxInstances=*/1, - /*nOutBufferSize=*/0, - /*nInBufferSize=*/0, - /*nDefaultTimeOut=*/30000, &sa); - - return *hServer != INVALID_HANDLE_VALUE; -} - -HRESULT CreateNamedPipesAndPseudoConsole(COORD size, DWORD dwFlags, - HANDLE *phInput, HANDLE *phOutput, - HPCON *phPC, std::wstring &inName, - std::wstring &outName, - const std::wstring &pipeName) { - HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0); - bool fLoadedDll = hLibrary != nullptr; - if (fLoadedDll) { - PFNCREATEPSEUDOCONSOLE const pfnCreate = - (PFNCREATEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, - "CreatePseudoConsole"); - if (pfnCreate) { - if (phPC == NULL || phInput == NULL || phOutput == NULL) { - return E_INVALIDARG; - } - - bool success = - createDataServerPipe(true, L"in", phInput, inName, pipeName); - if (!success) { - return HRESULT_FROM_WIN32(GetLastError()); - } - success = - createDataServerPipe(false, L"out", phOutput, outName, pipeName); - if (!success) { - return HRESULT_FROM_WIN32(GetLastError()); - } - return pfnCreate(size, *phInput, *phOutput, dwFlags, phPC); - } else { - // Failed to find CreatePseudoConsole in kernel32. This is likely because - // the user is not running a build of Windows that supports that API. - // We should fall back to winpty in this case. - return HRESULT_FROM_WIN32(GetLastError()); - } - } - - // Failed to find kernel32. This is really unlikely - honestly no idea how - // this is even possible to hit. But if it does happen, fall back to - // winpty. - return HRESULT_FROM_WIN32(GetLastError()); -} - -static NAN_METHOD(PtyStartProcess) { - Nan::HandleScope scope; - - v8::Local marshal; - std::wstring inName, outName; - BOOL fSuccess = FALSE; - std::unique_ptr mutableCommandline; - PROCESS_INFORMATION _piClient{}; - - if (info.Length() != 6 || !info[0]->IsString() || !info[1]->IsNumber() || - !info[2]->IsNumber() || !info[3]->IsBoolean() || !info[4]->IsString() || - !info[5]->IsBoolean()) { - Nan::ThrowError("Usage: pty.startProcess(file, cols, rows, debug, " - "pipeName, inheritCursor)"); - return; - } - - const std::wstring filename(path_util::to_wstring(Nan::Utf8String(info[0]))); - const SHORT cols = info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust(); - const SHORT rows = info[2]->Uint32Value(Nan::GetCurrentContext()).FromJust(); - const bool debug = Nan::To(info[3]).FromJust(); - const std::wstring pipeName(path_util::to_wstring(Nan::Utf8String(info[4]))); - const bool inheritCursor = Nan::To(info[5]).FromJust(); - - // use environment 'Path' variable to determine location of - // the relative path that we have received (e.g cmd.exe) - std::wstring shellpath; - if (::PathIsRelativeW(filename.c_str())) { - shellpath = path_util::get_shell_path(filename.c_str()); - } else { - shellpath = filename; - } - - std::string shellpath_(shellpath.begin(), shellpath.end()); - - if (shellpath.empty() || !path_util::file_exists(shellpath)) { - std::stringstream why; - why << "File not found: " << shellpath_; - Nan::ThrowError(why.str().c_str()); - return; - } - - HANDLE hIn, hOut; - HPCON hpc; - HRESULT hr = CreateNamedPipesAndPseudoConsole( - {cols, rows}, inheritCursor ? 1 /*PSEUDOCONSOLE_INHERIT_CURSOR*/ : 0, - &hIn, &hOut, &hpc, inName, outName, pipeName); - - // Restore default handling of ctrl+c - SetConsoleCtrlHandler(NULL, FALSE); - - // Set return values - marshal = Nan::New(); - - if (SUCCEEDED(hr)) { - // We were able to instantiate a conpty - const int ptyId = InterlockedIncrement(&ptyCounter); - Nan::Set(marshal, Nan::New("pty").ToLocalChecked(), - Nan::New(ptyId)); - ptyHandles.insert(ptyHandles.end(), new pty_baton(ptyId, hIn, hOut, hpc)); - } else { - Nan::ThrowError("Cannot launch conpty"); - return; - } - - Nan::Set(marshal, Nan::New("fd").ToLocalChecked(), - Nan::New(-1)); - { - std::string coninPipeNameStr(inName.begin(), inName.end()); - Nan::Set(marshal, Nan::New("conin").ToLocalChecked(), - Nan::New(coninPipeNameStr).ToLocalChecked()); - - std::string conoutPipeNameStr(outName.begin(), outName.end()); - Nan::Set(marshal, Nan::New("conout").ToLocalChecked(), - Nan::New(conoutPipeNameStr).ToLocalChecked()); - } - info.GetReturnValue().Set(marshal); -} - -VOID CALLBACK OnProcessExitWinEvent(_In_ PVOID context, - _In_ BOOLEAN TimerOrWaitFired) { - pty_baton *baton = static_cast(context); - - // Fire OnProcessExit - uv_async_send(&baton->async); -} - -static void OnProcessExit(uv_async_t *async) { - Nan::HandleScope scope; - pty_baton *baton = static_cast(async->data); - - UnregisterWait(baton->hWait); - - // Get exit code - DWORD exitCode = 0; - GetExitCodeProcess(baton->hShell, &exitCode); - - // Call function - v8::Local args[1] = {Nan::New(exitCode)}; - - Nan::AsyncResource asyncResource("node-pty.callback"); - baton->cb.Call(1, args, &asyncResource); - // Clean up - baton->cb.Reset(); -} - -static NAN_METHOD(PtyConnect) { - Nan::HandleScope scope; - - // If we're working with conpty's we need to call ConnectNamedPipe here AFTER - // the Socket has attempted to connect to the other end, then actually - // spawn the process here. - - std::stringstream errorText; - BOOL fSuccess = FALSE; - - if (info.Length() != 5 || !info[0]->IsNumber() || !info[1]->IsString() || - !info[2]->IsString() || !info[3]->IsArray() || !info[4]->IsFunction()) { - Nan::ThrowError("Usage: pty.connect(id, cmdline, cwd, env, exitCallback)"); - return; - } - - const int id = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); - const std::wstring cmdline(path_util::to_wstring(Nan::Utf8String(info[1]))); - const std::wstring cwd(path_util::to_wstring(Nan::Utf8String(info[2]))); - const v8::Local envValues = info[3].As(); - const v8::Local exitCallback = - v8::Local::Cast(info[4]); - - // Prepare command line - std::unique_ptr mutableCommandline = - std::make_unique(cmdline.length() + 1); - HRESULT hr = StringCchCopyW(mutableCommandline.get(), cmdline.length() + 1, - cmdline.c_str()); - - // Prepare cwd - std::unique_ptr mutableCwd = - std::make_unique(cwd.length() + 1); - hr = StringCchCopyW(mutableCwd.get(), cwd.length() + 1, cwd.c_str()); - - // Prepare environment - std::wstring env; - if (!envValues.IsEmpty()) { - std::wstringstream envBlock; - for (uint32_t i = 0; i < envValues->Length(); i++) { - std::wstring envValue(path_util::to_wstring( - Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked()))); - envBlock << envValue << L'\0'; - } - envBlock << L'\0'; - env = envBlock.str(); - } - auto envV = vectorFromString(env); - LPWSTR envArg = envV.empty() ? nullptr : envV.data(); - - // Fetch pty handle from ID and start process - pty_baton *handle = get_pty_baton(id); - - BOOL success = ConnectNamedPipe(handle->hIn, nullptr); - success = ConnectNamedPipe(handle->hOut, nullptr); - - // Attach the pseudoconsole to the client application we're creating - STARTUPINFOEXW siEx{0}; - siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW); - siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; - siEx.StartupInfo.hStdError = nullptr; - siEx.StartupInfo.hStdInput = nullptr; - siEx.StartupInfo.hStdOutput = nullptr; - - SIZE_T size = 0; - InitializeProcThreadAttributeList(NULL, 1, 0, &size); - BYTE *attrList = new BYTE[size]; - siEx.lpAttributeList = - reinterpret_cast(attrList); - - fSuccess = - InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size); - if (!fSuccess) { - return throwNanError(&info, "InitializeProcThreadAttributeList failed", - true); - } - fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList, 0, - PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, - handle->hpc, sizeof(HPCON), NULL, NULL); - if (!fSuccess) { - return throwNanError(&info, "UpdateProcThreadAttribute failed", true); - } - - PROCESS_INFORMATION piClient{}; - fSuccess = !!CreateProcessW( - nullptr, mutableCommandline.get(), - nullptr, // lpProcessAttributes - nullptr, // lpThreadAttributes - false, // bInheritHandles VERY IMPORTANT that this is false - EXTENDED_STARTUPINFO_PRESENT | - CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags - envArg, // lpEnvironment - mutableCwd.get(), // lpCurrentDirectory - &siEx.StartupInfo, // lpStartupInfo - &piClient // lpProcessInformation - ); - if (!fSuccess) { - return throwNanError(&info, "Cannot create process", true); - } - - // Update handle - handle->hShell = piClient.hProcess; - handle->cb.Reset(exitCallback); - handle->async.data = handle; - - // Setup OnProcessExit callback - uv_async_init(uv_default_loop(), &handle->async, OnProcessExit); - - // Setup Windows wait for process exit event - RegisterWaitForSingleObject(&handle->hWait, piClient.hProcess, - OnProcessExitWinEvent, (PVOID)handle, INFINITE, - WT_EXECUTEONLYONCE); - - // Return - v8::Local marshal = Nan::New(); - Nan::Set(marshal, Nan::New("pid").ToLocalChecked(), - Nan::New(piClient.dwProcessId)); - info.GetReturnValue().Set(marshal); -} - -void ConPTY::resize(int id, int cols, int rows) { - // SHORT cols = info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust(); - // SHORT rows = info[2]->Uint32Value(Nan::GetCurrentContext()).FromJust(); - - const pty_baton *handle = get_pty_baton(id); - - HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0); - bool fLoadedDll = hLibrary != nullptr; - if (fLoadedDll) { - PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = - (PFNRESIZEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, - "ResizePseudoConsole"); - if (pfnResizePseudoConsole) { - COORD size = {cols, rows}; - pfnResizePseudoConsole(handle->hpc, size); - } - } -} - -void ConPTY::kill(int id) { - const pty_baton *handle = get_pty_baton(id); - - HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0); - bool fLoadedDll = hLibrary != nullptr; - if (fLoadedDll) { - PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = - (PFNCLOSEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, - "ClosePseudoConsole"); - if (pfnClosePseudoConsole) { - pfnClosePseudoConsole(handle->hpc); - } - } - - CloseHandle(handle->hShell); - - return info.GetReturnValue().SetUndefined(); -} - -/** - * Init - */ - -void ConPTY::_register_methods() { - register_method("_init", &ConPTY::_init); - register_method("start_process", &ConPTY::start_process); - register_method("connect_to_named_pipe", &ConPTY::connect_to_named_pipe); - register_method("resize", &ConPTY::resize); - register_method("kill", &ConPTY::kill); -}; - -void ConPTY::_init() {} diff --git a/addons/godot_xterm/native/src_old/node_pty/win/conpty.h b/addons/godot_xterm/native/src_old/node_pty/win/conpty.h deleted file mode 100644 index 9f6f433..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/conpty.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Leroy Hopson -// SPDX-License-Identifier: MIT - -#ifndef GODOT_XTERM_CONPTY_H -#define GODOT_XTERM_CONPTY_H - -#include -#include - -namespace godot { - -class ConPTY : public RefCounted { - GDCLASS(ConPTY, 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); - // Array open(int cols, int rows); - void resize(int id, int cols, int rows); - void kill(int id); - // String process(int fd, String tty); - - void _init(); - -protected: - static void _bind_methods(); -}; - -} // namespace godot - -#endif // GODOT_XTERM_CONPTY_H diff --git a/addons/godot_xterm/native/src_old/node_pty/win/conpty_console_list.cc b/addons/godot_xterm/native/src_old/node_pty/win/conpty_console_list.cc deleted file mode 100644 index 889cff4..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/conpty_console_list.cc +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2019, Microsoft Corporation (MIT License). - */ - -#include -#include - -static NAN_METHOD(ApiConsoleProcessList) { - if (info.Length() != 1 || !info[0]->IsNumber()) { - Nan::ThrowError("Usage: getConsoleProcessList(shellPid)"); - return; - } - - const SHORT pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust(); - - if (!FreeConsole()) { - Nan::ThrowError("FreeConsole failed"); - } - if (!AttachConsole(pid)) { - Nan::ThrowError("AttachConsole failed"); - } - auto processList = std::vector(64); - auto processCount = - GetConsoleProcessList(&processList[0], processList.size()); - if (processList.size() < processCount) { - processList.resize(processCount); - processCount = GetConsoleProcessList(&processList[0], processList.size()); - } - FreeConsole(); - - v8::Local result = Nan::New(); - for (DWORD i = 0; i < processCount; i++) { - Nan::Set(result, i, Nan::New(processList[i])); - } - info.GetReturnValue().Set(result); -} - -extern "C" void init(v8::Local target) { - Nan::HandleScope scope; - Nan::SetMethod(target, "getConsoleProcessList", ApiConsoleProcessList); -}; - -NODE_MODULE(pty, init); diff --git a/addons/godot_xterm/native/src_old/node_pty/win/path_util.cc b/addons/godot_xterm/native/src_old/node_pty/win/path_util.cc deleted file mode 100644 index 128af22..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/path_util.cc +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2013-2015, Christopher Jeffrey, Peter Sunde (MIT License) - * Copyright (c) 2016, Daniel Imms (MIT License). - * Copyright (c) 2018, Microsoft Corporation (MIT License). - */ - -#include // PathCombine -#include - -#include "path_util.h" - -namespace path_util { - -const wchar_t *to_wstring(const Nan::Utf8String &str) { - const char *bytes = *str; - unsigned int sizeOfStr = MultiByteToWideChar(CP_UTF8, 0, bytes, -1, NULL, 0); - wchar_t *output = new wchar_t[sizeOfStr]; - MultiByteToWideChar(CP_UTF8, 0, bytes, -1, output, sizeOfStr); - return output; -} - -bool file_exists(std::wstring filename) { - DWORD attr = ::GetFileAttributesW(filename.c_str()); - if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY)) { - return false; - } - return true; -} - -// cmd.exe -> C:\Windows\system32\cmd.exe -std::wstring get_shell_path(std::wstring filename) { - std::wstring shellpath; - - if (file_exists(filename)) { - return shellpath; - } - - wchar_t buffer_[MAX_ENV]; - int read = ::GetEnvironmentVariableW(L"Path", buffer_, MAX_ENV); - if (!read) { - return shellpath; - } - - std::wstring delimiter = L";"; - size_t pos = 0; - std::vector paths; - std::wstring buffer(buffer_); - while ((pos = buffer.find(delimiter)) != std::wstring::npos) { - paths.push_back(buffer.substr(0, pos)); - buffer.erase(0, pos + delimiter.length()); - } - - const wchar_t *filename_ = filename.c_str(); - - for (int i = 0; i < paths.size(); ++i) { - std::wstring path = paths[i]; - wchar_t searchPath[MAX_PATH]; - ::PathCombineW(searchPath, const_cast(path.c_str()), filename_); - - if (searchPath == NULL) { - continue; - } - - if (file_exists(searchPath)) { - shellpath = searchPath; - break; - } - } - - return shellpath; -} - -} // namespace path_util diff --git a/addons/godot_xterm/native/src_old/node_pty/win/path_util.h b/addons/godot_xterm/native/src_old/node_pty/win/path_util.h deleted file mode 100644 index db8d3cd..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/path_util.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2013-2015, Christopher Jeffrey, Peter Sunde (MIT License) - * Copyright (c) 2016, Daniel Imms (MIT License). - * Copyright (c) 2018, Microsoft Corporation (MIT License). - */ - -#ifndef NODE_PTY_PATH_UTIL_H_ -#define NODE_PTY_PATH_UTIL_H_ - -#include - -#define MAX_ENV 65536 - -namespace path_util { - -const wchar_t *to_wstring(const Nan::Utf8String &str); -bool file_exists(std::wstring filename); -std::wstring get_shell_path(std::wstring filename); - -} // namespace path_util - -#endif // NODE_PTY_PATH_UTIL_H_ diff --git a/addons/godot_xterm/native/src_old/node_pty/win/winpty.cc b/addons/godot_xterm/native/src_old/node_pty/win/winpty.cc deleted file mode 100644 index 900f0af..0000000 --- a/addons/godot_xterm/native/src_old/node_pty/win/winpty.cc +++ /dev/null @@ -1,320 +0,0 @@ -/** - * Copyright (c) 2013-2015, Christopher Jeffrey, Peter Sunde (MIT License) - * Copyright (c) 2016, Daniel Imms (MIT License). - * Copyright (c) 2018, Microsoft Corporation (MIT License). - * - * pty.cc: - * This file is responsible for starting processes - * with pseudo-terminal file descriptors. - */ - -#include // PathCombine, PathIsRelative -#include -#include -#include -#include -#include -#include -#include -#include - -#include "path_util.h" - -/** - * Misc - */ -extern "C" void init(v8::Local); - -#define WINPTY_DBG_VARIABLE TEXT("WINPTYDBG") - -/** - * winpty - */ -static std::vector ptyHandles; -static volatile LONG ptyCounter; - -/** - * Helpers - */ - -static winpty_t *get_pipe_handle(int handle) { - for (size_t i = 0; i < ptyHandles.size(); ++i) { - winpty_t *ptyHandle = ptyHandles[i]; - int current = (int)winpty_agent_process(ptyHandle); - if (current == handle) { - return ptyHandle; - } - } - return nullptr; -} - -static bool remove_pipe_handle(int handle) { - for (size_t i = 0; i < ptyHandles.size(); ++i) { - winpty_t *ptyHandle = ptyHandles[i]; - if ((int)winpty_agent_process(ptyHandle) == handle) { - winpty_free(ptyHandle); - ptyHandles.erase(ptyHandles.begin() + i); - ptyHandle = nullptr; - return true; - } - } - return false; -} - -void throw_winpty_error(const char *generalMsg, winpty_error_ptr_t error_ptr) { - std::stringstream why; - std::wstring msg(winpty_error_msg(error_ptr)); - std::string msg_(msg.begin(), msg.end()); - why << generalMsg << ": " << msg_; - Nan::ThrowError(why.str().c_str()); - winpty_error_free(error_ptr); -} - -static NAN_METHOD(PtyGetExitCode) { - Nan::HandleScope scope; - - if (info.Length() != 1 || !info[0]->IsNumber()) { - Nan::ThrowError("Usage: pty.getExitCode(pidHandle)"); - return; - } - - DWORD exitCode = 0; - GetExitCodeProcess( - (HANDLE)info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(), - &exitCode); - - info.GetReturnValue().Set(Nan::New(exitCode)); -} - -static NAN_METHOD(PtyGetProcessList) { - Nan::HandleScope scope; - - if (info.Length() != 1 || !info[0]->IsNumber()) { - Nan::ThrowError("Usage: pty.getProcessList(pid)"); - return; - } - - int pid = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); - - winpty_t *pc = get_pipe_handle(pid); - if (pc == nullptr) { - info.GetReturnValue().Set(Nan::New(0)); - return; - } - int processList[64]; - const int processCount = 64; - int actualCount = - winpty_get_console_process_list(pc, processList, processCount, nullptr); - - v8::Local result = Nan::New(actualCount); - for (uint32_t i = 0; i < actualCount; i++) { - Nan::Set(result, i, Nan::New(processList[i])); - } - info.GetReturnValue().Set(result); -} - -static NAN_METHOD(PtyStartProcess) { - Nan::HandleScope scope; - - if (info.Length() != 7 || !info[0]->IsString() || !info[1]->IsString() || - !info[2]->IsArray() || !info[3]->IsString() || !info[4]->IsNumber() || - !info[5]->IsNumber() || !info[6]->IsBoolean()) { - Nan::ThrowError( - "Usage: pty.startProcess(file, cmdline, env, cwd, cols, rows, debug)"); - return; - } - - std::stringstream why; - - const wchar_t *filename = path_util::to_wstring(Nan::Utf8String(info[0])); - const wchar_t *cmdline = path_util::to_wstring(Nan::Utf8String(info[1])); - const wchar_t *cwd = path_util::to_wstring(Nan::Utf8String(info[3])); - - // create environment block - std::wstring env; - const v8::Local envValues = v8::Local::Cast(info[2]); - if (!envValues.IsEmpty()) { - - std::wstringstream envBlock; - - for (uint32_t i = 0; i < envValues->Length(); i++) { - std::wstring envValue(path_util::to_wstring( - Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked()))); - envBlock << envValue << L'\0'; - } - - env = envBlock.str(); - } - - // use environment 'Path' variable to determine location of - // the relative path that we have received (e.g cmd.exe) - std::wstring shellpath; - if (::PathIsRelativeW(filename)) { - shellpath = path_util::get_shell_path(filename); - } else { - shellpath = filename; - } - - std::string shellpath_(shellpath.begin(), shellpath.end()); - - if (shellpath.empty() || !path_util::file_exists(shellpath)) { - why << "File not found: " << shellpath_; - Nan::ThrowError(why.str().c_str()); - goto cleanup; - } - - int cols = info[4]->Int32Value(Nan::GetCurrentContext()).FromJust(); - int rows = info[5]->Int32Value(Nan::GetCurrentContext()).FromJust(); - bool debug = Nan::To(info[6]).FromJust(); - - // Enable/disable debugging - SetEnvironmentVariable(WINPTY_DBG_VARIABLE, - debug ? "1" : NULL); // NULL = deletes variable - - // Create winpty config - winpty_error_ptr_t error_ptr = nullptr; - winpty_config_t *winpty_config = winpty_config_new(0, &error_ptr); - if (winpty_config == nullptr) { - throw_winpty_error("Error creating WinPTY config", error_ptr); - goto cleanup; - } - winpty_error_free(error_ptr); - - // Set pty size on config - winpty_config_set_initial_size(winpty_config, cols, rows); - - // Start the pty agent - winpty_t *pc = winpty_open(winpty_config, &error_ptr); - winpty_config_free(winpty_config); - if (pc == nullptr) { - throw_winpty_error("Error launching WinPTY agent", error_ptr); - goto cleanup; - } - winpty_error_free(error_ptr); - - // Save pty struct for later use - ptyHandles.insert(ptyHandles.end(), pc); - - // Create winpty spawn config - winpty_spawn_config_t *config = winpty_spawn_config_new( - WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline, cwd, - env.c_str(), &error_ptr); - if (config == nullptr) { - throw_winpty_error("Error creating WinPTY spawn config", error_ptr); - goto cleanup; - } - winpty_error_free(error_ptr); - - // Spawn the new process - HANDLE handle = nullptr; - BOOL spawnSuccess = - winpty_spawn(pc, config, &handle, nullptr, nullptr, &error_ptr); - winpty_spawn_config_free(config); - if (!spawnSuccess) { - throw_winpty_error("Unable to start terminal process", error_ptr); - goto cleanup; - } - winpty_error_free(error_ptr); - - // Set return values - v8::Local marshal = Nan::New(); - Nan::Set(marshal, Nan::New("innerPid").ToLocalChecked(), - Nan::New((int)GetProcessId(handle))); - Nan::Set(marshal, Nan::New("innerPidHandle").ToLocalChecked(), - Nan::New((int)handle)); - Nan::Set(marshal, Nan::New("pid").ToLocalChecked(), - Nan::New((int)winpty_agent_process(pc))); - Nan::Set(marshal, Nan::New("pty").ToLocalChecked(), - Nan::New(InterlockedIncrement(&ptyCounter))); - Nan::Set(marshal, Nan::New("fd").ToLocalChecked(), - Nan::New(-1)); - { - LPCWSTR coninPipeName = winpty_conin_name(pc); - std::wstring coninPipeNameWStr(coninPipeName); - std::string coninPipeNameStr(coninPipeNameWStr.begin(), - coninPipeNameWStr.end()); - Nan::Set(marshal, Nan::New("conin").ToLocalChecked(), - Nan::New(coninPipeNameStr).ToLocalChecked()); - LPCWSTR conoutPipeName = winpty_conout_name(pc); - std::wstring conoutPipeNameWStr(conoutPipeName); - std::string conoutPipeNameStr(conoutPipeNameWStr.begin(), - conoutPipeNameWStr.end()); - Nan::Set(marshal, Nan::New("conout").ToLocalChecked(), - Nan::New(conoutPipeNameStr).ToLocalChecked()); - } - info.GetReturnValue().Set(marshal); - - goto cleanup; - -cleanup: - delete filename; - delete cmdline; - delete cwd; -} - -static NAN_METHOD(PtyResize) { - Nan::HandleScope scope; - - if (info.Length() != 3 || !info[0]->IsNumber() || !info[1]->IsNumber() || - !info[2]->IsNumber()) { - Nan::ThrowError("Usage: pty.resize(pid, cols, rows)"); - return; - } - - int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); - int cols = info[1]->Int32Value(Nan::GetCurrentContext()).FromJust(); - int rows = info[2]->Int32Value(Nan::GetCurrentContext()).FromJust(); - - winpty_t *pc = get_pipe_handle(handle); - - if (pc == nullptr) { - Nan::ThrowError("The pty doesn't appear to exist"); - return; - } - BOOL success = winpty_set_size(pc, cols, rows, nullptr); - if (!success) { - Nan::ThrowError("The pty could not be resized"); - return; - } - - return info.GetReturnValue().SetUndefined(); -} - -static NAN_METHOD(PtyKill) { - Nan::HandleScope scope; - - if (info.Length() != 2 || !info[0]->IsNumber() || !info[1]->IsNumber()) { - Nan::ThrowError("Usage: pty.kill(pid, innerPidHandle)"); - return; - } - - int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); - HANDLE innerPidHandle = - (HANDLE)info[1]->Int32Value(Nan::GetCurrentContext()).FromJust(); - - winpty_t *pc = get_pipe_handle(handle); - if (pc == nullptr) { - Nan::ThrowError("Pty seems to have been killed already"); - return; - } - - assert(remove_pipe_handle(handle)); - CloseHandle(innerPidHandle); - - return info.GetReturnValue().SetUndefined(); -} - -/** - * Init - */ - -extern "C" void init(v8::Local target) { - Nan::HandleScope scope; - Nan::SetMethod(target, "startProcess", PtyStartProcess); - Nan::SetMethod(target, "resize", PtyResize); - Nan::SetMethod(target, "kill", PtyKill); - Nan::SetMethod(target, "getExitCode", PtyGetExitCode); - Nan::SetMethod(target, "getProcessList", PtyGetProcessList); -}; - -NODE_MODULE(pty, init); diff --git a/addons/godot_xterm/native/src_old/pipe.cpp b/addons/godot_xterm/native/src_old/pipe.cpp deleted file mode 100644 index d2ae6ba..0000000 --- a/addons/godot_xterm/native/src_old/pipe.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2023 Leroy Hopson -// SDPX-License-Identifier: MIT - -#include "pipe.h" -#include "libuv_utils.h" - -#ifndef ULONG -#define ULONG size_t -#endif - -using namespace godot; - -void Pipe::_bind_methods() { - ClassDB::bind_method(D_METHOD("_init"), &Pipe::_init); - - ClassDB::bind_method(D_METHOD("poll"), &Pipe::_poll_connection); - ClassDB::bind_method(D_METHOD("open", "fd", "ipc"), &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); - - ADD_SIGNAL(MethodInfo("data_received", - PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); -} - -Pipe::Pipe() {} -Pipe::~Pipe() { close(); } - -void Pipe::_init() {} - -void _poll_connection(); - -void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); - -void _close_cb(uv_handle_t *handle); - -void _write_cb(uv_write_t *req, int status); - -void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); - -Error Pipe::open(int fd, bool ipc = false) { - RETURN_IF_UV_ERR(uv_pipe_init(uv_default_loop(), &handle, ipc)); - - handle.data = this; - - RETURN_IF_UV_ERR(uv_pipe_open(&handle, fd)); - RETURN_IF_UV_ERR(uv_stream_set_blocking((uv_stream_t *)&handle, false)); - RETURN_IF_UV_ERR( - uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb)); - - status = 1; - return OK; -} - -void Pipe::close() { - uv_close((uv_handle_t *)&handle, _close_cb); - uv_run(uv_default_loop(), UV_RUN_NOWAIT); -} - -Error Pipe::write(PackedByteArray data) { - char *s = (char *)data.ptr(); - ULONG len = data.size(); - - uv_buf_t buf; - uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t)); - - buf.base = s; - buf.len = len; - req->data = (void *)buf.base; - - uv_write(req, (uv_stream_t *)&handle, &buf, 1, _write_cb); - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - - return OK; -} - -int Pipe::get_status() { - if (!uv_is_active((uv_handle_t *)&handle)) - status = 0; - _poll_connection(); - return status; -} - -void Pipe::_poll_connection() { - if (status == 1 && !uv_is_active((uv_handle_t *)&handle)) - uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb); - - uv_run(uv_default_loop(), UV_RUN_NOWAIT); -} - -void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { - Pipe *pipe = static_cast(handle->data); - - if (nread < 0) { - switch (nread) { - case UV_EOF: - // Normal after shell exits. - case UV_EIO: - // Can happen when the process exits. - // As long as PTY has caught it, we should be fine. - uv_read_stop(handle); - pipe->status = 0; - return; - default: - UV_ERR_PRINT(nread); - } - return; - } - - PackedByteArray data; - data.resize(nread); - { memcpy(data.ptrw(), buf->base, nread); } - std::free((char *)buf->base); - - pipe->emit_signal("data_received", data); - - // Stop reading until the next poll, otherwise _read_cb could be called - // repeatedly, blocking Godot, and eventually resulting in a memory pool - // allocation error. This can be triggered with the command `cat /dev/urandom` - // if reading is not stopped. - uv_read_stop(handle); -} - -void _write_cb(uv_write_t *req, int status) { std::free(req); } - -void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { - buf->base = (char *)malloc(suggested_size); - buf->len = suggested_size; -} - -void _close_cb(uv_handle_t *handle) { - Pipe *pipe = static_cast(handle->data); - pipe->status = 0; -} diff --git a/addons/godot_xterm/native/src_old/pipe.h b/addons/godot_xterm/native/src_old/pipe.h deleted file mode 100644 index 343a8d5..0000000 --- a/addons/godot_xterm/native/src_old/pipe.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2022 Leroy Hopson -// SPDX-License-Identifier: MIT - -#ifndef GODOT_XTERM_PIPE_H -#define GODOT_XTERM_PIPE_H - -#include -#include -#include - -namespace godot { - -class Pipe : public RefCounted { - GDCLASS(Pipe, RefCounted) - -public: - uv_pipe_t handle; - - Pipe(); - ~Pipe(); - - void _init(); - - Error open(int fd, bool ipc); - void close(); - int get_status(); - - Error write(PackedByteArray data); - - void pause(); - void resume(); - -public: - int status; - -protected: - static void _bind_methods(); - -private: - void _poll_connection(); - - static Error _translate_error(int err); -}; - -} // namespace godot - -#endif // GODOT_XTERM_PIPE_H diff --git a/addons/godot_xterm/native/src_old/pty_unix.cpp b/addons/godot_xterm/native/src_old/pty_unix.cpp deleted file mode 100644 index 6ab34f2..0000000 --- a/addons/godot_xterm/native/src_old/pty_unix.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/** - * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License) - * Copyright (c) 2017, Daniel Imms (MIT License) - * Copyright (c) 2021, 2024 Leroy Hopson (MIT License) - * - * SPDX-License-Identifier: MIT - * - * pty.cc: - * This file is responsible for starting processes - * with pseudo-terminal file descriptors. - * - * See: - * man pty - * man tty_ioctl - * man termios - * man forkpty - */ - -/** - * Includes - */ - -#if !defined(_WIN32) && !defined(_PTY_DISABLED) - -#include "pty_unix.h" -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* forkpty */ -/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */ -#if defined(__GLIBC__) || defined(__CYGWIN__) -#include -#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#elif defined(__FreeBSD__) -#include -#elif defined(__sun) -#include /* for I_PUSH */ -#else -#include -#endif - -#include /* tcgetattr, tty_ioctl */ - -/* Some platforms name VWERASE and VDISCARD differently */ -#if !defined(VWERASE) && defined(VWERSE) -#define VWERASE VWERSE -#endif -#if !defined(VDISCARD) && defined(VDISCRD) -#define VDISCARD VDISCRD -#endif - -/* environ for execvpe */ -/* node/src/node_child_process.cc */ -#if defined(__APPLE__) && !TARGET_OS_IPHONE -#include -#define environ (*_NSGetEnviron()) -#else -extern char **environ; -#endif - -/* for pty_getproc */ -#if defined(__linux__) -#include -#include -#elif defined(__APPLE__) -#include -#include -#endif - -/* NSIG - macro for highest signal + 1, should be defined */ -#ifndef NSIG -#define NSIG 32 -#endif - -using namespace godot; - -/** - * Structs - */ - -struct pty_baton { - Callable cb; - int exit_code; - int signal_code; - pid_t pid; - uv_async_t async; - uv_thread_t tid; -}; - -/** - * Functions - */ - -static int pty_execvpe(const char *, char **, char **); - -static int pty_nonblock(int); - -static char *pty_getproc(int, char *); - -static int pty_openpty(int *, int *, char *, const struct termios *, - const struct winsize *); - -static pid_t pty_forkpty(int *, char *, const struct termios *, - const struct winsize *); - -static void pty_waitpid(void *); - -static void pty_after_waitpid(uv_async_t *); - -static void pty_after_close(uv_handle_t *); - -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 = strdup(p_file.utf8().get_data()); - - // args - int i = 0; - int argc = p_args.size(); - int argl = argc + 1 + 1; - char **argv = new char *[argl]; - argv[0] = strdup(file); - argv[argl - 1] = NULL; - for (; i < argc; i++) { - char *arg = strdup(p_args[i].utf8().get_data()); - argv[i + 1] = strdup(arg); - } - - // env - i = 0; - int envc = p_env.size(); - char **env = new char *[envc + 1]; - env[envc] = NULL; - for (; i < envc; i++) { - char *pairs = strdup(p_env[i].utf8().get_data()); - env[i] = strdup(pairs); - } - - // cwd - char *cwd = strdup(p_cwd.utf8().get_data()); - - // size - struct winsize winp; - winp.ws_col = p_cols; - winp.ws_row = p_rows; - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - // termios - struct termios t = termios(); - struct termios *term = &t; - term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT; - if (p_utf8) { -#if defined(IUTF8) - term->c_iflag |= IUTF8; -#endif - } - term->c_oflag = OPOST | ONLCR; - term->c_cflag = CREAD | CS8 | HUPCL; - term->c_lflag = - ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL; - - term->c_cc[VEOF] = 4; - term->c_cc[VEOL] = -1; - term->c_cc[VEOL2] = -1; - term->c_cc[VERASE] = 0x7f; - term->c_cc[VWERASE] = 23; - term->c_cc[VKILL] = 21; - term->c_cc[VREPRINT] = 18; - term->c_cc[VINTR] = 3; - term->c_cc[VQUIT] = 0x1c; - term->c_cc[VSUSP] = 26; - term->c_cc[VSTART] = 17; - term->c_cc[VSTOP] = 19; - term->c_cc[VLNEXT] = 22; - term->c_cc[VDISCARD] = 15; - term->c_cc[VMIN] = 1; - term->c_cc[VTIME] = 0; - -#if (__APPLE__) - term->c_cc[VDSUSP] = 25; - term->c_cc[VSTATUS] = 20; -#endif - - cfsetispeed(term, B38400); - cfsetospeed(term, B38400); - - // uid / gid - int uid = p_uid; - int gid = p_gid; - - // fork the pty - int master = -1; - - sigset_t newmask, oldmask; - struct sigaction sig_action; - - // temporarily block all signals - // this is needed due to a race condition in openpty - // and to avoid running signal handlers in the child - // before exec* happened - sigfillset(&newmask); - pthread_sigmask(SIG_SETMASK, &newmask, &oldmask); - - pid_t pid = pty_forkpty(&master, nullptr, term, &winp); - - if (!pid) { - // remove all signal handler from child - sig_action.sa_handler = SIG_DFL; - sig_action.sa_flags = 0; - sigemptyset(&sig_action.sa_mask); - for (int i = 0; i < NSIG; i++) { // NSIG is a macro for all signals + 1 - sigaction(i, &sig_action, NULL); - } - } - // re-enable signals - pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - - if (pid) { - for (i = 0; i < argl; i++) - std::free(argv[i]); - delete[] argv; - for (i = 0; i < envc; i++) - std::free(env[i]); - delete[] env; - std::free(cwd); - } - - switch (pid) { - case -1: - ERR_PRINT("forkpty(3) failed."); - return Array::make(FAILED); - case 0: - if (strlen(cwd)) { - if (chdir(cwd) == -1) { - perror("chdir(2) failed."); - _exit(1); - } - } - - if (uid != -1 && gid != -1) { - if (setgid(gid) == -1) { - perror("setgid(2) failed."); - _exit(1); - } - if (setuid(uid) == -1) { - perror("setuid(2) failed."); - _exit(1); - } - } - - pty_execvpe(argv[0], argv, env); - - perror("execvp(3) failed."); - _exit(1); - default: - if (pty_nonblock(master) == -1) { - ERR_PRINT("Could not set master fd to nonblocking."); - return Array::make(FAILED); - } - - Dictionary result; - result["fd"] = (int)master; - result["pid"] = (int)pid; - result["pty"] = ptsname(master); - - pty_baton *baton = new pty_baton(); - baton->exit_code = 0; - baton->signal_code = 0; - baton->cb = p_on_exit; - baton->pid = pid; - baton->async.data = baton; - - uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid); - - uv_thread_create(&baton->tid, pty_waitpid, static_cast(baton)); - - return Array::make(OK, result); - } - - return Array::make(FAILED); -} - -Array PTYUnix::open(int p_cols, int p_rows) { - // size - struct winsize winp; - winp.ws_col = p_cols; - winp.ws_row = p_rows; - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - // pty - int master, slave; - int ret = pty_openpty(&master, &slave, nullptr, NULL, &winp); - - if (ret == -1) { - ERR_PRINT("openpty(3) failed."); - return Array::make(FAILED); - } - - if (pty_nonblock(master) == -1) { - ERR_PRINT("Could not set master fd to nonblocking."); - return Array::make(FAILED); - } - - if (pty_nonblock(slave) == -1) { - ERR_PRINT("Could not set slave fd to nonblocking."); - return Array::make(FAILED); - } - - Dictionary dict; - dict["master"] = master; - dict["slave"] = slave; - dict["pty"] = ptsname(master); - - return Array::make(OK, dict); -} - -Error PTYUnix::resize(int p_fd, int p_cols, int p_rows) { - int fd = p_fd; - - struct winsize winp; - winp.ws_col = p_cols; - winp.ws_row = p_rows; - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - if (ioctl(fd, TIOCSWINSZ, &winp) == -1) { - switch (errno) { - // TODO: Fixme! - //case EBADF: - // RETURN_UV_ERR(UV_EBADF) - //case EFAULT: - // RETURN_UV_ERR(UV_EFAULT) - //case EINVAL: - // RETURN_UV_ERR(UV_EINVAL); - //case ENOTTY: - // RETURN_UV_ERR(UV_ENOTTY); - } - ERR_PRINT("ioctl(2) failed"); - return FAILED; - } - - return OK; -} - -/** - * Foreground Process Name - */ -String PTYUnix::process(int p_fd, String p_tty) { - int fd = p_fd; - - char *tty = strdup(p_tty.utf8().get_data()); - char *name = pty_getproc(fd, tty); - std::free(tty); - - if (name == NULL) { - return ""; - } - - String name_ = String(name); - std::free(name); - return name_; -} - -/** - * execvpe - */ - -// execvpe(3) is not portable. -// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html -static int pty_execvpe(const char *file, char **argv, char **envp) { - char **old = environ; - environ = envp; - int ret = execvp(file, argv); - environ = old; - return ret; -} - -/** - * Nonblocking FD - */ - -static int pty_nonblock(int fd) { - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) - return -1; - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - -/** - * pty_waitpid - * Wait for SIGCHLD to read exit status. - */ - -static void pty_waitpid(void *data) { - int ret; - int stat_loc; - - pty_baton *baton = static_cast(data); - - errno = 0; - - if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) { - if (ret == -1 && errno == EINTR) { - return pty_waitpid(baton); - } - if (ret == -1 && errno == ECHILD) { - // XXX node v0.8.x seems to have this problem. - // waitpid is already handled elsewhere. - ; - } else { - // assert(false); - } - } - - if (WIFEXITED(stat_loc)) { - baton->exit_code = WEXITSTATUS(stat_loc); // errno? - } - - if (WIFSIGNALED(stat_loc)) { - baton->signal_code = WTERMSIG(stat_loc); - } - - uv_async_send(&baton->async); -} - -/** - * pty_after_waitpid - * Callback after exit status has been read. - */ - -static void pty_after_waitpid(uv_async_t *async) { - pty_baton *baton = static_cast(async->data); - - Array argv = Array::make(baton->exit_code, baton->signal_code); - - if (baton->cb.is_valid()) { - baton->cb.callv(argv); - baton->cb = (Variant) nullptr; - } - - uv_close((uv_handle_t *)async, pty_after_close); -} - -/** - * pty_after_close - * uv_close() callback - free handle data - */ - -static void pty_after_close(uv_handle_t *handle) { - uv_async_t *async = (uv_async_t *)handle; - pty_baton *baton = static_cast(async->data); - delete baton; -} - -/** - * pty_getproc - * Taken from tmux. - */ - -// Taken from: tmux (http://tmux.sourceforge.net/) -// Copyright (c) 2009 Nicholas Marriott -// Copyright (c) 2009 Joshua Elsasser -// Copyright (c) 2009 Todd Carson -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER -// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -#if defined(__linux__) - -static char *pty_getproc(int fd, char *tty) { - FILE *f; - char *path, *buf; - size_t len; - int ch; - pid_t pgrp; - int r; - - if ((pgrp = tcgetpgrp(fd)) == -1) { - return NULL; - } - - r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp); - if (r == -1 || path == NULL) - return NULL; - - if ((f = fopen(path, "r")) == NULL) { - free(path); - return NULL; - } - - free(path); - - len = 0; - buf = NULL; - while ((ch = fgetc(f)) != EOF) { - if (ch == '\0') - break; - buf = (char *)realloc(buf, len + 2); - if (buf == NULL) - return NULL; - buf[len++] = ch; - } - - if (buf != NULL) { - buf[len] = '\0'; - } - - fclose(f); - return buf; -} - -#elif defined(__APPLE__) - -static char *pty_getproc(int fd, char *tty) { - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, 0}; - size_t size; - struct kinfo_proc kp; - - if ((mib[3] = tcgetpgrp(fd)) == -1) { - return NULL; - } - - size = sizeof kp; - if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) { - return NULL; - } - - if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') { - return NULL; - } - - return strdup(kp.kp_proc.p_comm); -} - -#else - -static char *pty_getproc(int fd, char *tty) { return NULL; } - -#endif - -/** - * openpty(3) / forkpty(3) - */ - -static int pty_openpty(int *amaster, int *aslave, char *name, - const struct termios *termp, - const struct winsize *winp) { -#if defined(__sun) - char *slave_name; - int slave; - int master = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if (master == -1) - return -1; - if (amaster) - *amaster = master; - - if (grantpt(master) == -1) - goto err; - if (unlockpt(master) == -1) - goto err; - - slave_name = ptsname(master); - if (slave_name == NULL) - goto err; - if (name) - strcpy(name, slave_name); - - slave = open(slave_name, O_RDWR | O_NOCTTY); - if (slave == -1) - goto err; - if (aslave) - *aslave = slave; - - ioctl(slave, I_PUSH, "ptem"); - ioctl(slave, I_PUSH, "ldterm"); - ioctl(slave, I_PUSH, "ttcompat"); - - if (termp) - tcsetattr(slave, TCSAFLUSH, termp); - if (winp) - ioctl(slave, TIOCSWINSZ, winp); - - return 0; - -err: - close(master); - return -1; -#else - return openpty(amaster, aslave, name, (termios *)termp, (winsize *)winp); -#endif -} - -static pid_t pty_forkpty(int *amaster, char *name, const struct termios *termp, - const struct winsize *winp) { -#if defined(__sun) - int master, slave; - - int ret = pty_openpty(&master, &slave, name, termp, winp); - if (ret == -1) - return -1; - if (amaster) - *amaster = master; - - pid_t pid = fork(); - - switch (pid) { - case -1: // error in fork, we are still in parent - close(master); - close(slave); - return -1; - case 0: // we are in the child process - close(master); - setsid(); - -#if defined(TIOCSCTTY) - // glibc does this - if (ioctl(slave, TIOCSCTTY, NULL) == -1) { - _exit(1); - } -#endif - - dup2(slave, 0); - dup2(slave, 1); - dup2(slave, 2); - - if (slave > 2) - close(slave); - - return 0; - default: // we are in the parent process - close(slave); - return pid; - } - - return -1; -#else - return forkpty(amaster, name, (termios *)termp, (winsize *)winp); -#endif -} - -/** - * Init - */ - -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() {} - -#endif diff --git a/addons/godot_xterm/native/src_old/pty_unix.h b/addons/godot_xterm/native/src_old/pty_unix.h deleted file mode 100644 index cc8b8e1..0000000 --- a/addons/godot_xterm/native/src_old/pty_unix.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2022, 2024 Leroy Hopson -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -namespace godot { - -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. */ - 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); - Error resize(int fd, int cols, int rows); - String process(int fd, String tty); - - void _init(); - -protected: - static void _bind_methods(); -}; - -} // namespace godot diff --git a/addons/godot_xterm/native/src_old/pty_unix_new.cpp b/addons/godot_xterm/native/src_old/pty_unix_new.cpp deleted file mode 100644 index 1043205..0000000 --- a/addons/godot_xterm/native/src_old/pty_unix_new.cpp +++ /dev/null @@ -1,827 +0,0 @@ -/** - * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License) - * Copyright (c) 2017, Daniel Imms (MIT License) - * Copyright (c) 2024, Leroy Hopson (MIT License) - * - * SPDX-License-Identifier: MIT - * - * pty.cc: - * This file is responsible for starting processes - * with pseudo-terminal file descriptors. - * - * See: - * man pty - * man tty_ioctl - * man termios - * man forkpty - */ - -/** - * Includes - */ - -using namespace godot; - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* forkpty */ -/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */ -#if defined(__linux__) -#include -#elif defined(__APPLE__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -/* Some platforms name VWERASE and VDISCARD differently */ -#if !defined(VWERASE) && defined(VWERSE) -#define VWERASE VWERSE -#endif -#if !defined(VDISCARD) && defined(VDISCRD) -#define VDISCARD VDISCRD -#endif - -/* for pty_getproc */ -#if defined(__linux__) -#include -#include -#elif defined(__APPLE__) -#include -#include -#include -#include -#include -#include -#endif - -/* NSIG - macro for highest signal + 1, should be defined */ -#ifndef NSIG -#define NSIG 32 -#endif - -/* macOS 10.14 back does not define this constant */ -#ifndef POSIX_SPAWN_SETSID - #define POSIX_SPAWN_SETSID 1024 -#endif - -/* environ for execvpe */ -/* node/src/node_child_process.cc */ -#if !defined(__APPLE__) -extern char **environ; -#endif - -#if defined(__APPLE__) -extern "C" { -// Changes the current thread's directory to a path or directory file -// descriptor. libpthread only exposes a syscall wrapper starting in -// macOS 10.12, but the system call dates back to macOS 10.5. On older OSes, -// the syscall is issued directly. -int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12)); -int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12)); -} - -#define HANDLE_EINTR(x) ({ \ - int eintr_wrapper_counter = 0; \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR && \ - eintr_wrapper_counter++ < 100); \ - eintr_wrapper_result; \ -}) -#endif - -/** - * Structs - */ - -struct pty_baton { - Nan::Persistent cb; - int exit_code; - int signal_code; - pid_t pid; - uv_async_t async; - uv_thread_t tid; -}; - -/** - * Methods - */ - -NAN_METHOD(PtyFork); -NAN_METHOD(PtyOpen); -NAN_METHOD(PtyResize); -NAN_METHOD(PtyGetProc); - -/** - * Functions - */ - -static int -pty_nonblock(int); - -#if defined(__APPLE__) -static char * -pty_getproc(int); -#else -static char * -pty_getproc(int, char *); -#endif - -static void -pty_waitpid(void *); - -static void -pty_after_waitpid(uv_async_t *); - -static void -pty_after_close(uv_handle_t *); - -#if defined(__APPLE__) || defined(__OpenBSD__) -static void -pty_posix_spawn(char** argv, char** env, - const struct termios *termp, - const struct winsize *winp, - int* master, - pid_t* pid, - int* err); -#endif - -Dictionary fork( - const String &p_file, - const PackedStringArray &p_args, - const PackedStringArray &p_env, - const String &p_cwd, - const int p_cols, - const int p_rows, - const int p_uid, - const int p_gid, - const bool p_utf8, - const String &p_helper_path, - const Callable &p_on_exit -) { - // file - std::string file = p_file.utf8().get_data(); - - // args - v8::Local argv_ = v8::Local::Cast(info[1]); - - // env - v8::Local env_ = v8::Local::Cast(info[2]); - int envc = env_->Length(); - char **env = new char*[envc+1]; - env[envc] = NULL; - for (int i = 0; i < envc; i++) { - Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked()); - env[i] = strdup(*pair); - } - - // cwd - Nan::Utf8String cwd_(info[3]); - - // size - struct winsize winp; - winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - -#if !defined(__APPLE__) - // uid / gid - int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust(); -#endif - - // termios - struct termios t = termios(); - struct termios *term = &t; - term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT; - if (Nan::To(info[8]).FromJust()) { -#if defined(IUTF8) - term->c_iflag |= IUTF8; -#endif - } - term->c_oflag = OPOST | ONLCR; - term->c_cflag = CREAD | CS8 | HUPCL; - term->c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL; - - term->c_cc[VEOF] = 4; - term->c_cc[VEOL] = -1; - term->c_cc[VEOL2] = -1; - term->c_cc[VERASE] = 0x7f; - term->c_cc[VWERASE] = 23; - term->c_cc[VKILL] = 21; - term->c_cc[VREPRINT] = 18; - term->c_cc[VINTR] = 3; - term->c_cc[VQUIT] = 0x1c; - term->c_cc[VSUSP] = 26; - term->c_cc[VSTART] = 17; - term->c_cc[VSTOP] = 19; - term->c_cc[VLNEXT] = 22; - term->c_cc[VDISCARD] = 15; - term->c_cc[VMIN] = 1; - term->c_cc[VTIME] = 0; - - #if (__APPLE__) - term->c_cc[VDSUSP] = 25; - term->c_cc[VSTATUS] = 20; - #endif - - cfsetispeed(term, B38400); - cfsetospeed(term, B38400); - - // helperPath - Nan::Utf8String helper_path(info[9]); - - pid_t pid; - int master; -#if defined(__APPLE__) - int argc = argv_->Length(); - int argl = argc + 4; - char **argv = new char*[argl]; - argv[0] = strdup(*helper_path); - argv[1] = strdup(*cwd_); - argv[2] = strdup(*file); - argv[argl - 1] = NULL; - for (int i = 0; i < argc; i++) { - Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked()); - argv[i + 3] = strdup(*arg); - } - - int err = -1; - pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err); - if (err != 0) { - Nan::ThrowError("posix_spawnp failed."); - goto done; - } - if (pty_nonblock(master) == -1) { - Nan::ThrowError("Could not set master fd to nonblocking."); - goto done; - } -#else - int argc = argv_->Length(); - int argl = argc + 2; - char **argv = new char*[argl]; - argv[0] = strdup(*file); - argv[argl - 1] = NULL; - for (int i = 0; i < argc; i++) { - Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked()); - argv[i + 1] = strdup(*arg); - } - - char* cwd = strdup(*cwd_); - sigset_t newmask, oldmask; - struct sigaction sig_action; - // temporarily block all signals - // this is needed due to a race condition in openpty - // and to avoid running signal handlers in the child - // before exec* happened - sigfillset(&newmask); - pthread_sigmask(SIG_SETMASK, &newmask, &oldmask); - - pid = forkpty(&master, nullptr, static_cast(term), static_cast(&winp)); - - if (!pid) { - // remove all signal handler from child - sig_action.sa_handler = SIG_DFL; - sig_action.sa_flags = 0; - sigemptyset(&sig_action.sa_mask); - for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1 - sigaction(i, &sig_action, NULL); - } - } else { - for (int i = 0; i < argl; i++) free(argv[i]); - delete[] argv; - - for (int i = 0; i < envc; i++) free(env[i]); - delete[] env; - - free(cwd); - } - - // re-enable signals - pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - - switch (pid) { - case -1: - Nan::ThrowError("forkpty(3) failed."); - goto done; - case 0: - if (strlen(cwd)) { - if (chdir(cwd) == -1) { - perror("chdir(2) failed."); - _exit(1); - } - } - - if (uid != -1 && gid != -1) { - if (setgid(gid) == -1) { - perror("setgid(2) failed."); - _exit(1); - } - if (setuid(uid) == -1) { - perror("setuid(2) failed."); - _exit(1); - } - } - - { - char **old = environ; - environ = env; - execvp(argv[0], argv); - environ = old; - perror("execvp(3) failed."); - _exit(1); - } - default: - if (pty_nonblock(master) == -1) { - Nan::ThrowError("Could not set master fd to nonblocking."); - goto done; - } - } -#endif - - { - v8::Local obj = Nan::New(); - Nan::Set(obj, - Nan::New("fd").ToLocalChecked(), - Nan::New(master)); - Nan::Set(obj, - Nan::New("pid").ToLocalChecked(), - Nan::New(pid)); - Nan::Set(obj, - Nan::New("pty").ToLocalChecked(), - Nan::New(ptsname(master)).ToLocalChecked()); - - pty_baton *baton = new pty_baton(); - baton->exit_code = 0; - baton->signal_code = 0; - baton->cb.Reset(v8::Local::Cast(info[10])); - baton->pid = pid; - baton->async.data = baton; - - uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid); - - uv_thread_create(&baton->tid, pty_waitpid, static_cast(baton)); - - return info.GetReturnValue().Set(obj); - } - -done: -#if defined(__APPLE__) - for (int i = 0; i < argl; i++) free(argv[i]); - delete[] argv; - - for (int i = 0; i < envc; i++) free(env[i]); - delete[] env; -#endif - return info.GetReturnValue().SetUndefined(); -} - -NAN_METHOD(PtyOpen) { - Nan::HandleScope scope; - - if (info.Length() != 2 || - !info[0]->IsNumber() || - !info[1]->IsNumber()) { - return Nan::ThrowError("Usage: pty.open(cols, rows)"); - } - - // size - struct winsize winp; - winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - // pty - int master, slave; - int ret = openpty(&master, &slave, nullptr, NULL, static_cast(&winp)); - - if (ret == -1) { - return Nan::ThrowError("openpty(3) failed."); - } - - if (pty_nonblock(master) == -1) { - return Nan::ThrowError("Could not set master fd to nonblocking."); - } - - if (pty_nonblock(slave) == -1) { - return Nan::ThrowError("Could not set slave fd to nonblocking."); - } - - v8::Local obj = Nan::New(); - Nan::Set(obj, - Nan::New("master").ToLocalChecked(), - Nan::New(master)); - Nan::Set(obj, - Nan::New("slave").ToLocalChecked(), - Nan::New(slave)); - Nan::Set(obj, - Nan::New("pty").ToLocalChecked(), - Nan::New(ptsname(master)).ToLocalChecked()); - - return info.GetReturnValue().Set(obj); -} - -NAN_METHOD(PtyResize) { - Nan::HandleScope scope; - - if (info.Length() != 3 || - !info[0]->IsNumber() || - !info[1]->IsNumber() || - !info[2]->IsNumber()) { - return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)"); - } - - int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - - struct winsize winp; - winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - if (ioctl(fd, TIOCSWINSZ, &winp) == -1) { - switch (errno) { - case EBADF: return Nan::ThrowError("ioctl(2) failed, EBADF"); - case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT"); - case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL"); - case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY"); - } - return Nan::ThrowError("ioctl(2) failed"); - } - - return info.GetReturnValue().SetUndefined(); -} - -/** - * Foreground Process Name - */ -NAN_METHOD(PtyGetProc) { - Nan::HandleScope scope; - -#if defined(__APPLE__) - if (info.Length() != 1 || - !info[0]->IsNumber()) { - return Nan::ThrowError("Usage: pty.process(pid)"); - } - - int pid = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - char *name = pty_getproc(pid); -#else - if (info.Length() != 2 || - !info[0]->IsNumber() || - !info[1]->IsString()) { - return Nan::ThrowError("Usage: pty.process(fd, tty)"); - } - - int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(); - - Nan::Utf8String tty_(info[1]); - char *tty = strdup(*tty_); - char *name = pty_getproc(fd, tty); - free(tty); -#endif - - if (name == NULL) { - return info.GetReturnValue().SetUndefined(); - } - - v8::Local name_ = Nan::New(name).ToLocalChecked(); - free(name); - return info.GetReturnValue().Set(name_); -} - -/** - * Nonblocking FD - */ - -static int -pty_nonblock(int fd) { - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) return -1; - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - -/** - * pty_waitpid - * Wait for SIGCHLD to read exit status. - */ - -static void -pty_waitpid(void *data) { - int ret; - int stat_loc; - pty_baton *baton = static_cast(data); - errno = 0; -#if defined(__APPLE__) - // Based on - // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69? - int kq = HANDLE_EINTR(kqueue()); - struct kevent change = {0}; - EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); - if (ret == -1) { - if (errno == ESRCH) { - // At this point, one of the following has occurred: - // 1. The process has died but has not yet been reaped. - // 2. The process has died and has already been reaped. - // 3. The process is in the process of dying. It's no longer - // kqueueable, but it may not be waitable yet either. Mark calls - // this case the "zombie death race". - ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG)); - if (ret == 0) { - ret = kill(baton->pid, SIGKILL); - if (ret != -1) { - HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0)); - } - } - } - } else { - struct kevent event = {0}; - ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL)); - if (ret == 1) { - if ((event.fflags & NOTE_EXIT) && - (event.ident == static_cast(baton->pid))) { - // The process is dead or dying. This won't block for long, if at - // all. - HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0)); - } - } - } -#else - if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) { - if (ret == -1 && errno == EINTR) { - return pty_waitpid(baton); - } - if (ret == -1 && errno == ECHILD) { - // XXX node v0.8.x seems to have this problem. - // waitpid is already handled elsewhere. - ; - } else { - assert(false); - } - } -#endif - - if (WIFEXITED(stat_loc)) { - baton->exit_code = WEXITSTATUS(stat_loc); // errno? - } - - if (WIFSIGNALED(stat_loc)) { - baton->signal_code = WTERMSIG(stat_loc); - } - - uv_async_send(&baton->async); -} - -/** - * pty_after_waitpid - * Callback after exit status has been read. - */ - -static void -pty_after_waitpid(uv_async_t *async) { - Nan::HandleScope scope; - pty_baton *baton = static_cast(async->data); - - v8::Local argv[] = { - Nan::New(baton->exit_code), - Nan::New(baton->signal_code), - }; - - v8::Local cb = Nan::New(baton->cb); - baton->cb.Reset(); - memset(&baton->cb, -1, sizeof(baton->cb)); - Nan::AsyncResource resource("pty_after_waitpid"); - resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv); - - uv_close((uv_handle_t *)async, pty_after_close); -} - -/** - * pty_after_close - * uv_close() callback - free handle data - */ - -static void -pty_after_close(uv_handle_t *handle) { - uv_async_t *async = (uv_async_t *)handle; - pty_baton *baton = static_cast(async->data); - delete baton; -} - -/** - * pty_getproc - * Taken from tmux. - */ - -// Taken from: tmux (http://tmux.sourceforge.net/) -// Copyright (c) 2009 Nicholas Marriott -// Copyright (c) 2009 Joshua Elsasser -// Copyright (c) 2009 Todd Carson -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER -// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -#if defined(__linux__) - -static char * -pty_getproc(int fd, char *tty) { - FILE *f; - char *path, *buf; - size_t len; - int ch; - pid_t pgrp; - int r; - - if ((pgrp = tcgetpgrp(fd)) == -1) { - return NULL; - } - - r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp); - if (r == -1 || path == NULL) return NULL; - - if ((f = fopen(path, "r")) == NULL) { - free(path); - return NULL; - } - - free(path); - - len = 0; - buf = NULL; - while ((ch = fgetc(f)) != EOF) { - if (ch == '\0') break; - buf = (char *)realloc(buf, len + 2); - if (buf == NULL) return NULL; - buf[len++] = ch; - } - - if (buf != NULL) { - buf[len] = '\0'; - } - - fclose(f); - return buf; -} - -#elif defined(__APPLE__) - -static char * -pty_getproc(int pid) { - char pname[MAXCOMLEN + 1]; - if (!proc_name(pid, pname, sizeof(pname))) { - return NULL; - } - - return strdup(pname); -} - -#else - -static char * -pty_getproc(int fd, char *tty) { - return NULL; -} - -#endif - -#if defined(__APPLE__) -static void -pty_posix_spawn(char** argv, char** env, - const struct termios *termp, - const struct winsize *winp, - int* master, - pid_t* pid, - int* err) { - int low_fds[3]; - size_t count = 0; - - for (; count < 3; count++) { - low_fds[count] = posix_openpt(O_RDWR); - if (low_fds[count] >= STDERR_FILENO) - break; - } - - int flags = POSIX_SPAWN_CLOEXEC_DEFAULT | - POSIX_SPAWN_SETSIGDEF | - POSIX_SPAWN_SETSIGMASK | - POSIX_SPAWN_SETSID; - *master = posix_openpt(O_RDWR); - if (*master == -1) { - return; - } - - int res = grantpt(*master) || unlockpt(*master); - if (res == -1) { - return; - } - - // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems. - int slave; - char slave_pty_name[128]; - res = ioctl(*master, TIOCPTYGNAME, slave_pty_name); - if (res == -1) { - return; - } - - slave = open(slave_pty_name, O_RDWR | O_NOCTTY); - if (slave == -1) { - return; - } - - if (termp) { - res = tcsetattr(slave, TCSANOW, termp); - if (res == -1) { - return; - }; - } - - if (winp) { - res = ioctl(slave, TIOCSWINSZ, winp); - if (res == -1) { - return; - } - } - - posix_spawn_file_actions_t acts; - posix_spawn_file_actions_init(&acts); - posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO); - posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO); - posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO); - posix_spawn_file_actions_addclose(&acts, slave); - posix_spawn_file_actions_addclose(&acts, *master); - - posix_spawnattr_t attrs; - posix_spawnattr_init(&attrs); - *err = posix_spawnattr_setflags(&attrs, flags); - if (*err != 0) { - goto done; - } - - sigset_t signal_set; - /* Reset all signal the child to their default behavior */ - sigfillset(&signal_set); - *err = posix_spawnattr_setsigdefault(&attrs, &signal_set); - if (*err != 0) { - goto done; - } - - /* Reset the signal mask for all signals */ - sigemptyset(&signal_set); - *err = posix_spawnattr_setsigmask(&attrs, &signal_set); - if (*err != 0) { - goto done; - } - - do - *err = posix_spawn(pid, argv[0], &acts, &attrs, argv, env); - while (*err == EINTR); -done: - posix_spawn_file_actions_destroy(&acts); - posix_spawnattr_destroy(&attrs); - - for (; count > 0; count--) { - close(low_fds[count]); - } -} -#endif - -/** - * Init - */ - -NAN_MODULE_INIT(init) { - Nan::HandleScope scope; - Nan::Export(target, "fork", PtyFork); - Nan::Export(target, "open", PtyOpen); - Nan::Export(target, "resize", PtyResize); - Nan::Export(target, "process", PtyGetProc); -} - -NODE_MODULE(pty, init) diff --git a/addons/godot_xterm/native/src_old/pty_unix_original.cpp b/addons/godot_xterm/native/src_old/pty_unix_original.cpp deleted file mode 100644 index 1637d76..0000000 --- a/addons/godot_xterm/native/src_old/pty_unix_original.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/** - * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License) - * Copyright (c) 2017, Daniel Imms (MIT License) - * Copyright (c) 2024, Leroy Hopson (MIT License) - * - * SPDX-License-Identifier: MIT - * - * pty.cc: - * This file is responsible for starting processes - * with pseudo-terminal file descriptors. - * - * See: - * man pty - * man tty_ioctl - * man termios - * man forkpty - */ - -/** - * Includes - */ - -#if !defined(_WIN32) && !defined(_PTY_DISABLED) - -#define NODE_ADDON_API_DISABLE_DEPRECATED -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* forkpty */ -/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */ -#if defined(__linux__) -#include -#elif defined(__APPLE__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -/* Some platforms name VWERASE and VDISCARD differently */ -#if !defined(VWERASE) && defined(VWERSE) -#define VWERASE VWERSE -#endif -#if !defined(VDISCARD) && defined(VDISCRD) -#define VDISCARD VDISCRD -#endif - -/* for pty_getproc */ -#if defined(__linux__) -#include -#include -#elif defined(__APPLE__) -#include -#include -#include -#include -#include -#include -#include -#endif - -/* NSIG - macro for highest signal + 1, should be defined */ -#ifndef NSIG -#define NSIG 32 -#endif - -/* macOS 10.14 back does not define this constant */ -#ifndef POSIX_SPAWN_SETSID - #define POSIX_SPAWN_SETSID 1024 -#endif - -/* environ for execvpe */ -/* node/src/node_child_process.cc */ -#if !defined(__APPLE__) -extern char **environ; -#endif - -#if defined(__APPLE__) -extern "C" { -// Changes the current thread's directory to a path or directory file -// descriptor. libpthread only exposes a syscall wrapper starting in -// macOS 10.12, but the system call dates back to macOS 10.5. On older OSes, -// the syscall is issued directly. -int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12)); -int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12)); -} - -#define HANDLE_EINTR(x) ({ \ - int eintr_wrapper_counter = 0; \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR && \ - eintr_wrapper_counter++ < 100); \ - eintr_wrapper_result; \ -}) -#endif - -struct ExitEvent { - int exit_code = 0, signal_code = 0; -}; - -void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) { - std::thread *th = new std::thread; - // Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE. - auto tsfn = Napi::ThreadSafeFunction::New( - env, - cb, // JavaScript function called asynchronously - "SetupExitCallback_resource", // Name - 0, // Unlimited queue - 1, // Only one thread will use this initially - [th](Napi::Env) { // Finalizer used to clean threads up - th->join(); - delete th; - }); - *th = std::thread([tsfn = std::move(tsfn), pid] { - auto callback = [](Napi::Env env, Napi::Function cb, ExitEvent *exit_event) { - cb.Call({Napi::Number::New(env, exit_event->exit_code), - Napi::Number::New(env, exit_event->signal_code)}); - delete exit_event; - }; - - int ret; - int stat_loc; -#if defined(__APPLE__) - // Based on - // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69? - int kq = HANDLE_EINTR(kqueue()); - struct kevent change = {0}; - EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); - if (ret == -1) { - if (errno == ESRCH) { - // At this point, one of the following has occurred: - // 1. The process has died but has not yet been reaped. - // 2. The process has died and has already been reaped. - // 3. The process is in the process of dying. It's no longer - // kqueueable, but it may not be waitable yet either. Mark calls - // this case the "zombie death race". - ret = HANDLE_EINTR(waitpid(pid, &stat_loc, WNOHANG)); - if (ret == 0) { - ret = kill(pid, SIGKILL); - if (ret != -1) { - HANDLE_EINTR(waitpid(pid, &stat_loc, 0)); - } - } - } - } else { - struct kevent event = {0}; - ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL)); - if (ret == 1) { - if ((event.fflags & NOTE_EXIT) && - (event.ident == static_cast(pid))) { - // The process is dead or dying. This won't block for long, if at - // all. - HANDLE_EINTR(waitpid(pid, &stat_loc, 0)); - } - } - } -#else - while (true) { - errno = 0; - if ((ret = waitpid(pid, &stat_loc, 0)) != pid) { - if (ret == -1 && errno == EINTR) { - continue; - } - if (ret == -1 && errno == ECHILD) { - // XXX node v0.8.x seems to have this problem. - // waitpid is already handled elsewhere. - ; - } else { - assert(false); - } - } - break; - } -#endif - ExitEvent *exit_event = new ExitEvent; - if (WIFEXITED(stat_loc)) { - exit_event->exit_code = WEXITSTATUS(stat_loc); // errno? - } - if (WIFSIGNALED(stat_loc)) { - exit_event->signal_code = WTERMSIG(stat_loc); - } - auto status = tsfn.BlockingCall(exit_event, callback); // In main thread - assert(status == napi_ok); - - tsfn.Release(); - }); -} - -/** - * Methods - */ - -Napi::Value PtyFork(const Napi::CallbackInfo& info); -Napi::Value PtyOpen(const Napi::CallbackInfo& info); -Napi::Value PtyResize(const Napi::CallbackInfo& info); -Napi::Value PtyGetProc(const Napi::CallbackInfo& info); - -/** - * Functions - */ - -static int -pty_nonblock(int); - -#if defined(__APPLE__) -static char * -pty_getproc(int); -#else -static char * -pty_getproc(int, char *); -#endif - -#if defined(__APPLE__) || defined(__OpenBSD__) -static void -pty_posix_spawn(char** argv, char** env, - const struct termios *termp, - const struct winsize *winp, - int* master, - pid_t* pid, - int* err); -#endif - -struct DelBuf { - int len; - DelBuf(int len) : len(len) {} - void operator()(char **p) { - if (p == nullptr) - return; - for (int i = 0; i < len; i++) - free(p[i]); - delete[] p; - } -}; - -Napi::Value PtyFork(const Napi::CallbackInfo& info) { - Napi::Env napiEnv(info.Env()); - Napi::HandleScope scope(napiEnv); - - if (info.Length() != 11 || - !info[0].IsString() || - !info[1].IsArray() || - !info[2].IsArray() || - !info[3].IsString() || - !info[4].IsNumber() || - !info[5].IsNumber() || - !info[6].IsNumber() || - !info[7].IsNumber() || - !info[8].IsBoolean() || - !info[9].IsString() || - !info[10].IsFunction()) { - throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)"); - } - - // file - std::string file = info[0].As(); - - // args - Napi::Array argv_ = info[1].As(); - - // env - Napi::Array env_ = info[2].As(); - int envc = env_.Length(); - std::unique_ptr env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1)); - char **env = env_unique_ptr.get(); - env[envc] = NULL; - for (int i = 0; i < envc; i++) { - std::string pair = env_.Get(i).As(); - env[i] = strdup(pair.c_str()); - } - - // cwd - std::string cwd_ = info[3].As(); - - // size - struct winsize winp; - winp.ws_col = info[4].As().Int32Value(); - winp.ws_row = info[5].As().Int32Value(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - -#if !defined(__APPLE__) - // uid / gid - int uid = info[6].As().Int32Value(); - int gid = info[7].As().Int32Value(); -#endif - - // termios - struct termios t = termios(); - struct termios *term = &t; - term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT; - if (info[8].As().Value()) { -#if defined(IUTF8) - term->c_iflag |= IUTF8; -#endif - } - term->c_oflag = OPOST | ONLCR; - term->c_cflag = CREAD | CS8 | HUPCL; - term->c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL; - - term->c_cc[VEOF] = 4; - term->c_cc[VEOL] = -1; - term->c_cc[VEOL2] = -1; - term->c_cc[VERASE] = 0x7f; - term->c_cc[VWERASE] = 23; - term->c_cc[VKILL] = 21; - term->c_cc[VREPRINT] = 18; - term->c_cc[VINTR] = 3; - term->c_cc[VQUIT] = 0x1c; - term->c_cc[VSUSP] = 26; - term->c_cc[VSTART] = 17; - term->c_cc[VSTOP] = 19; - term->c_cc[VLNEXT] = 22; - term->c_cc[VDISCARD] = 15; - term->c_cc[VMIN] = 1; - term->c_cc[VTIME] = 0; - - #if (__APPLE__) - term->c_cc[VDSUSP] = 25; - term->c_cc[VSTATUS] = 20; - #endif - - cfsetispeed(term, B38400); - cfsetospeed(term, B38400); - - // helperPath - std::string helper_path = info[9].As(); - - pid_t pid; - int master; -#if defined(__APPLE__) - int argc = argv_.Length(); - int argl = argc + 4; - std::unique_ptr argv_unique_ptr(new char *[argl], DelBuf(argl)); - char **argv = argv_unique_ptr.get(); - argv[0] = strdup(helper_path.c_str()); - argv[1] = strdup(cwd_.c_str()); - argv[2] = strdup(file.c_str()); - argv[argl - 1] = NULL; - for (int i = 0; i < argc; i++) { - std::string arg = argv_.Get(i).As(); - argv[i + 3] = strdup(arg.c_str()); - } - - int err = -1; - pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err); - if (err != 0) { - throw Napi::Error::New(napiEnv, "posix_spawnp failed."); - } - if (pty_nonblock(master) == -1) { - throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking."); - } -#else - int argc = argv_.Length(); - int argl = argc + 2; - std::unique_ptr argv_unique_ptr(new char *[argl], DelBuf(argl)); - char** argv = argv_unique_ptr.get(); - argv[0] = strdup(file.c_str()); - argv[argl - 1] = NULL; - for (int i = 0; i < argc; i++) { - std::string arg = argv_.Get(i).As(); - argv[i + 1] = strdup(arg.c_str()); - } - - sigset_t newmask, oldmask; - struct sigaction sig_action; - // temporarily block all signals - // this is needed due to a race condition in openpty - // and to avoid running signal handlers in the child - // before exec* happened - sigfillset(&newmask); - pthread_sigmask(SIG_SETMASK, &newmask, &oldmask); - - pid = forkpty(&master, nullptr, static_cast(term), static_cast(&winp)); - - if (!pid) { - // remove all signal handler from child - sig_action.sa_handler = SIG_DFL; - sig_action.sa_flags = 0; - sigemptyset(&sig_action.sa_mask); - for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1 - sigaction(i, &sig_action, NULL); - } - } - - // re-enable signals - pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - - switch (pid) { - case -1: - throw Napi::Error::New(napiEnv, "forkpty(3) failed."); - case 0: - if (strlen(cwd_.c_str())) { - if (chdir(cwd_.c_str()) == -1) { - perror("chdir(2) failed."); - _exit(1); - } - } - - if (uid != -1 && gid != -1) { - if (setgid(gid) == -1) { - perror("setgid(2) failed."); - _exit(1); - } - if (setuid(uid) == -1) { - perror("setuid(2) failed."); - _exit(1); - } - } - - { - char **old = environ; - environ = env; - execvp(argv[0], argv); - environ = old; - perror("execvp(3) failed."); - _exit(1); - } - default: - if (pty_nonblock(master) == -1) { - throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking."); - } - } -#endif - - Napi::Object obj = Napi::Object::New(napiEnv); - obj.Set("fd", Napi::Number::New(napiEnv, master)); - obj.Set("pid", Napi::Number::New(napiEnv, pid)); - obj.Set("pty", Napi::String::New(napiEnv, ptsname(master))); - - // Set up process exit callback. - Napi::Function cb = info[10].As(); - SetupExitCallback(napiEnv, cb, pid); - return obj; -} - -Napi::Value PtyOpen(const Napi::CallbackInfo& info) { - Napi::Env env(info.Env()); - Napi::HandleScope scope(env); - - if (info.Length() != 2 || - !info[0].IsNumber() || - !info[1].IsNumber()) { - throw Napi::Error::New(env, "Usage: pty.open(cols, rows)"); - } - - // size - struct winsize winp; - winp.ws_col = info[0].As().Int32Value(); - winp.ws_row = info[1].As().Int32Value(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - // pty - int master, slave; - int ret = openpty(&master, &slave, nullptr, NULL, static_cast(&winp)); - - if (ret == -1) { - throw Napi::Error::New(env, "openpty(3) failed."); - } - - if (pty_nonblock(master) == -1) { - throw Napi::Error::New(env, "Could not set master fd to nonblocking."); - } - - if (pty_nonblock(slave) == -1) { - throw Napi::Error::New(env, "Could not set slave fd to nonblocking."); - } - - Napi::Object obj = Napi::Object::New(env); - obj.Set("master", Napi::Number::New(env, master)); - obj.Set("slave", Napi::Number::New(env, slave)); - obj.Set("pty", Napi::String::New(env, ptsname(master))); - - return obj; -} - -Napi::Value PtyResize(const Napi::CallbackInfo& info) { - Napi::Env env(info.Env()); - Napi::HandleScope scope(env); - - if (info.Length() != 3 || - !info[0].IsNumber() || - !info[1].IsNumber() || - !info[2].IsNumber()) { - throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)"); - } - - int fd = info[0].As().Int32Value(); - - struct winsize winp; - winp.ws_col = info[1].As().Int32Value(); - winp.ws_row = info[2].As().Int32Value(); - winp.ws_xpixel = 0; - winp.ws_ypixel = 0; - - if (ioctl(fd, TIOCSWINSZ, &winp) == -1) { - switch (errno) { - case EBADF: - throw Napi::Error::New(env, "ioctl(2) failed, EBADF"); - case EFAULT: - throw Napi::Error::New(env, "ioctl(2) failed, EFAULT"); - case EINVAL: - throw Napi::Error::New(env, "ioctl(2) failed, EINVAL"); - case ENOTTY: - throw Napi::Error::New(env, "ioctl(2) failed, ENOTTY"); - } - throw Napi::Error::New(env, "ioctl(2) failed"); - } - - return env.Undefined(); -} - -/** - * Foreground Process Name - */ -Napi::Value PtyGetProc(const Napi::CallbackInfo& info) { - Napi::Env env(info.Env()); - Napi::HandleScope scope(env); - -#if defined(__APPLE__) - if (info.Length() != 1 || - !info[0].IsNumber()) { - throw Napi::Error::New(env, "Usage: pty.process(pid)"); - } - - int fd = info[0].As().Int32Value(); - char *name = pty_getproc(fd); -#else - if (info.Length() != 2 || - !info[0].IsNumber() || - !info[1].IsString()) { - throw Napi::Error::New(env, "Usage: pty.process(fd, tty)"); - } - - int fd = info[0].As().Int32Value(); - - std::string tty_ = info[1].As(); - char *tty = strdup(tty_.c_str()); - char *name = pty_getproc(fd, tty); - free(tty); -#endif - - if (name == NULL) { - return env.Undefined(); - } - - Napi::String name_ = Napi::String::New(env, name); - free(name); - return name_; -} - -/** - * Nonblocking FD - */ - -static int -pty_nonblock(int fd) { - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) return -1; - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - -/** - * pty_getproc - * Taken from tmux. - */ - -// Taken from: tmux (http://tmux.sourceforge.net/) -// Copyright (c) 2009 Nicholas Marriott -// Copyright (c) 2009 Joshua Elsasser -// Copyright (c) 2009 Todd Carson -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER -// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -#if defined(__linux__) - -static char * -pty_getproc(int fd, char *tty) { - FILE *f; - char *path, *buf; - size_t len; - int ch; - pid_t pgrp; - int r; - - if ((pgrp = tcgetpgrp(fd)) == -1) { - return NULL; - } - - r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp); - if (r == -1 || path == NULL) return NULL; - - if ((f = fopen(path, "r")) == NULL) { - free(path); - return NULL; - } - - free(path); - - len = 0; - buf = NULL; - while ((ch = fgetc(f)) != EOF) { - if (ch == '\0') break; - buf = (char *)realloc(buf, len + 2); - if (buf == NULL) return NULL; - buf[len++] = ch; - } - - if (buf != NULL) { - buf[len] = '\0'; - } - - fclose(f); - return buf; -} - -#elif defined(__APPLE__) - -static char * -pty_getproc(int fd) { - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; - size_t size; - struct kinfo_proc kp; - - if ((mib[3] = tcgetpgrp(fd)) == -1) { - return NULL; - } - - size = sizeof kp; - if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) { - return NULL; - } - - if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') { - return NULL; - } - - return strdup(kp.kp_proc.p_comm); -} - -#else - -static char * -pty_getproc(int fd, char *tty) { - return NULL; -} - -#endif - -#if defined(__APPLE__) -static void -pty_posix_spawn(char** argv, char** env, - const struct termios *termp, - const struct winsize *winp, - int* master, - pid_t* pid, - int* err) { - int low_fds[3]; - size_t count = 0; - - for (; count < 3; count++) { - low_fds[count] = posix_openpt(O_RDWR); - if (low_fds[count] >= STDERR_FILENO) - break; - } - - int flags = POSIX_SPAWN_CLOEXEC_DEFAULT | - POSIX_SPAWN_SETSIGDEF | - POSIX_SPAWN_SETSIGMASK | - POSIX_SPAWN_SETSID; - *master = posix_openpt(O_RDWR); - if (*master == -1) { - return; - } - - int res = grantpt(*master) || unlockpt(*master); - if (res == -1) { - return; - } - - // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems. - int slave; - char slave_pty_name[128]; - res = ioctl(*master, TIOCPTYGNAME, slave_pty_name); - if (res == -1) { - return; - } - - slave = open(slave_pty_name, O_RDWR | O_NOCTTY); - if (slave == -1) { - return; - } - - if (termp) { - res = tcsetattr(slave, TCSANOW, termp); - if (res == -1) { - return; - }; - } - - if (winp) { - res = ioctl(slave, TIOCSWINSZ, winp); - if (res == -1) { - return; - } - } - - posix_spawn_file_actions_t acts; - posix_spawn_file_actions_init(&acts); - posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO); - posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO); - posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO); - posix_spawn_file_actions_addclose(&acts, slave); - posix_spawn_file_actions_addclose(&acts, *master); - - posix_spawnattr_t attrs; - posix_spawnattr_init(&attrs); - *err = posix_spawnattr_setflags(&attrs, flags); - if (*err != 0) { - goto done; - } - - sigset_t signal_set; - /* Reset all signal the child to their default behavior */ - sigfillset(&signal_set); - *err = posix_spawnattr_setsigdefault(&attrs, &signal_set); - if (*err != 0) { - goto done; - } - - /* Reset the signal mask for all signals */ - sigemptyset(&signal_set); - *err = posix_spawnattr_setsigmask(&attrs, &signal_set); - if (*err != 0) { - goto done; - } - - do - *err = posix_spawn(pid, argv[0], &acts, &attrs, argv, env); - while (*err == EINTR); -done: - posix_spawn_file_actions_destroy(&acts); - posix_spawnattr_destroy(&attrs); - - for (; count > 0; count--) { - close(low_fds[count]); - } -} -#endif - -/** - * Init - */ - -Napi::Object init(Napi::Env env, Napi::Object exports) { - exports.Set("fork", Napi::Function::New(env, PtyFork)); - exports.Set("open", Napi::Function::New(env, PtyOpen)); - exports.Set("resize", Napi::Function::New(env, PtyResize)); - exports.Set("process", Napi::Function::New(env, PtyGetProc)); - return exports; -} - -NODE_API_MODULE(NODE_GYP_MODULE_NAME, init) - -#endif diff --git a/addons/godot_xterm/native/src_old/terminal.cpp b/addons/godot_xterm/native/src_old/terminal.cpp deleted file mode 100644 index 0957089..0000000 --- a/addons/godot_xterm/native/src_old/terminal.cpp +++ /dev/null @@ -1,654 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2023 Leroy Hopson -// SPDX-License-Identifier: MIT - -#include "terminal.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define UNICODE_MAX 0x10FFFF - -using namespace godot; - -Terminal::Terminal() { - // Ensure we write to terminal before the frame is drawn. Otherwise, the - // terminal state may be updated but not drawn until it is updated again, - // which may not happen for some time. - RenderingServer::get_singleton()->connect("frame_pre_draw", - Callable(this, "_flush")); - - // Override default focus mode. - set_focus_mode(FOCUS_ALL); - - // Name our nodes for easier debugging. - back_buffer->set_name("BackBuffer"); - sub_viewport->set_name("SubViewport"); - front_buffer->set_name("FrontBuffer"); - - // Ensure buffers always have correct size. - back_buffer->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT); - front_buffer->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT); - - // Setup back buffer. - back_buffer->connect("draw", Callable(this, "_on_back_buffer_draw")); - - // Setup sub viewport. - sub_viewport->set_handle_input_locally(false); - sub_viewport->set_transparent_background(true); - sub_viewport->set_snap_controls_to_pixels(false); - sub_viewport->set_update_mode(SubViewport::UPDATE_WHEN_PARENT_VISIBLE); - sub_viewport->set_clear_mode(SubViewport::CLEAR_MODE_NEVER); - sub_viewport->add_child(back_buffer); - add_child(sub_viewport); - - // Setup bell timer. - bell_timer->set_name("BellTimer"); - bell_timer->set_one_shot(true); - add_child(bell_timer); - - // Setup blink timer. - blink_timer->set_name("BlinkTimer"); - blink_timer->set_one_shot(true); - blink_timer->connect("timeout", Callable(this, "_toggle_blink")); - add_child(blink_timer); - - // Setup selection timer. - selection_timer->set_name("SelectionTimer"); - selection_timer->set_wait_time(0.05); - selection_timer->connect("timeout", Callable(this, "_on_selection_held")); - add_child(selection_timer); - - // Setup front buffer. - front_buffer->set_texture(sub_viewport->get_texture()); - add_child(front_buffer); - - framebuffer_age = 0; - update_mode = UpdateMode::AUTO; - - if (tsm_screen_new(&screen, NULL, NULL)) { - ERR_PRINT("Error creating new tsm screen."); - } - tsm_screen_set_max_sb(screen, 1000); - - if (tsm_vte_new(&vte, screen, &Terminal::_write_cb, this, NULL, NULL)) { - ERR_PRINT("Error creating new tsm vte."); - } - - tsm_vte_set_bell_cb(vte, &Terminal::_bell_cb, this); - - _update_theme_item_cache(); -} - -Terminal::~Terminal() { - back_buffer->queue_free(); - sub_viewport->queue_free(); - front_buffer->queue_free(); -} - -void Terminal::set_copy_on_selection(bool value) { copy_on_selection = value; } - -bool Terminal::get_copy_on_selection() { return copy_on_selection; } - -void Terminal::set_update_mode(Terminal::UpdateMode value) { - update_mode = value; -}; - -Terminal::UpdateMode Terminal::get_update_mode() { return update_mode; } - -void Terminal::set_bell_cooldown(double value) { bell_cooldown = value; } - -double Terminal::get_bell_cooldown() { return bell_cooldown; } - -void Terminal::set_bell_muted(bool value) { bell_muted = value; } - -bool Terminal::get_bell_muted() { return bell_muted; } - -void Terminal::set_blink_enabled(bool value) { - blink_enabled = value; - - if (!blink_enabled) - blink_timer->stop(); - - _refresh(); -} - -bool Terminal::get_blink_enabled() { return blink_enabled; } - -void Terminal::set_blink_time_off(double value) { - blink_time_off = value; - - if (!blink_on && !blink_timer->is_stopped()) { - double time_left = blink_timer->get_time_left(); - blink_timer->start(std::min(blink_time_off, time_left)); - } -} - -double Terminal::get_blink_time_off() { return blink_time_off; } - -void Terminal::set_blink_time_on(double value) { - blink_time_on = value; - - if (blink_on && !blink_timer->is_stopped()) { - double time_left = blink_timer->get_time_left(); - blink_timer->start(std::min(blink_time_on, time_left)); - } -} - -double Terminal::get_blink_time_on() { return blink_time_on; } - -void Terminal::clear() { - Vector2 initial_size = get_size(); - set_size(Vector2(initial_size.x, cell_size.y)); - tsm_screen_clear_sb(screen); - set_size(initial_size); - back_buffer->queue_redraw(); -} - -String Terminal::copy_all() { - char *out = nullptr; - int len = tsm_screen_copy_all(screen, &out); - String result = String(out); - std::free(out); - return result; -} - -String Terminal::copy_selection() { - char *out = nullptr; - int len = tsm_screen_selection_copy(screen, &out); - String result = String(out); - std::free(out); - return result; -} - -int Terminal::get_cols() { return cols; } -int Terminal::get_rows() { return rows; } - -void Terminal::write(Variant data) { - switch (data.get_type()) { - case Variant::PACKED_BYTE_ARRAY: - break; - case Variant::STRING: - data = ((String)data).to_utf8_buffer(); - break; - default: - ERR_PRINT("Expected data to be a String or PackedByteArray."); - } - - write_buffer.push_back(data); - - queue_redraw(); -} - -void Terminal::_gui_input(Ref event) { - _handle_key_input(event); - _handle_selection(event); - _handle_mouse_wheel(event); -} - -void Terminal::_notification(int what) { - switch (what) { - case NOTIFICATION_RESIZED: - _recalculate_size(); - sub_viewport->set_size(get_size()); - _refresh(); - break; - case NOTIFICATION_THEME_CHANGED: - _update_theme_item_cache(); - _refresh(); - break; - } -} - -void Terminal::_flush() { - if (write_buffer.is_empty()) - return; - - for (int i = 0; i < write_buffer.size(); i++) { - PackedByteArray data = static_cast(write_buffer[i]); - tsm_vte_input(vte, (char *)data.ptr(), data.size()); - } - - write_buffer.clear(); - - back_buffer->queue_redraw(); -} - -void Terminal::_on_back_buffer_draw() { - if (update_mode == UpdateMode::DISABLED) { - return; - } - - if ((update_mode > UpdateMode::AUTO) || framebuffer_age == 0) { - Color background_color = palette[TSM_COLOR_BACKGROUND]; - back_buffer->draw_rect(back_buffer->get_rect(), background_color); - } - - int prev_framebuffer_age = framebuffer_age; - framebuffer_age = tsm_screen_draw(screen, &Terminal::_text_draw_cb, this); - - if (update_mode == UpdateMode::ALL_NEXT_FRAME && prev_framebuffer_age != 0) - update_mode = UpdateMode::AUTO; -} - -void Terminal::_on_selection_held() { - if (!(Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) || - selection_mode == SelectionMode::NONE) { - if (copy_on_selection) - DisplayServer::get_singleton()->clipboard_set_primary(copy_selection()); - selection_timer->stop(); - return; - } - - Vector2 target = get_local_mouse_position() / cell_size; - tsm_screen_selection_target(screen, target.x, target.y); - back_buffer->queue_redraw(); - selection_timer->start(); -} - -void Terminal::_toggle_blink() { - if (blink_enabled) { - blink_on = !blink_on; - _refresh(); - } -} - -void Terminal::_bind_methods() { - // Properties. - ClassDB::bind_method(D_METHOD("set_copy_on_selection", "value"), - &Terminal::set_copy_on_selection); - ClassDB::bind_method(D_METHOD("get_copy_on_selection"), - &Terminal::get_copy_on_selection); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "copy_on_selection"), - "set_copy_on_selection", "get_copy_on_selection"); - ClassDB::bind_method(D_METHOD("set_update_mode", "value"), - &Terminal::set_update_mode); - ClassDB::bind_method(D_METHOD("get_update_mode"), &Terminal::get_update_mode); - ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, - "Disabled,Auto,All,All Next Frame"), - "set_update_mode", "get_update_mode"); - - ADD_GROUP("Bell", "bell_"); - ClassDB::bind_method(D_METHOD("set_bell_cooldown", "value"), - &Terminal::set_bell_cooldown); - ClassDB::bind_method(D_METHOD("get_bell_cooldown"), - &Terminal::get_bell_cooldown); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bell_cooldown"), - "set_bell_cooldown", "get_bell_cooldown"); - ClassDB::bind_method(D_METHOD("set_bell_muted", "value"), - &Terminal::set_bell_muted); - ClassDB::bind_method(D_METHOD("get_bell_muted"), &Terminal::get_bell_muted); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bell_muted"), "set_bell_muted", - "get_bell_muted"); - - ADD_GROUP("Blink", "blink_"); - ClassDB::bind_method(D_METHOD("set_blink_enabled", "value"), - &Terminal::set_blink_enabled); - ClassDB::bind_method(D_METHOD("get_blink_enabled"), - &Terminal::get_blink_enabled); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blink_enabled"), - "set_blink_enabled", "get_blink_enabled"); - ClassDB::bind_method(D_METHOD("get_blink_time_off"), - &Terminal::get_blink_time_off); - ClassDB::bind_method(D_METHOD("set_blink_time_off", "value"), - &Terminal::set_blink_time_off); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "blink_time_off"), - "set_blink_time_off", "get_blink_time_off"); - ClassDB::bind_method(D_METHOD("set_blink_time_on", "value"), - &Terminal::set_blink_time_on); - ClassDB::bind_method(D_METHOD("get_blink_time_on"), - &Terminal::get_blink_time_on); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "blink_time_on"), - "set_blink_time_on", "get_blink_time_on"); - - // Methods. - ClassDB::bind_method(D_METHOD("clear"), &Terminal::clear); - ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all); - ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection); - ClassDB::bind_method(D_METHOD("get_cols"), &Terminal::get_cols); - ClassDB::bind_method(D_METHOD("get_rows"), &Terminal::get_rows); - ClassDB::bind_method(D_METHOD("write", "data"), &Terminal::write); - - // Signals. - ADD_SIGNAL(MethodInfo("bell")); - 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"))); - - // Enumerations. - BIND_ENUM_CONSTANT(UPDATE_MODE_DISABLED); - BIND_ENUM_CONSTANT(UPDATE_MODE_AUTO); - BIND_ENUM_CONSTANT(UPDATE_MODE_ALL); - BIND_ENUM_CONSTANT(UPDATE_MODE_ALL_NEXT_FRAME); - - // Private methods (must be exposed as they are connected to signals). - ClassDB::bind_method(D_METHOD("_flush"), &Terminal::_flush); - ClassDB::bind_method(D_METHOD("_on_back_buffer_draw"), - &Terminal::_on_back_buffer_draw); - ClassDB::bind_method(D_METHOD("_on_selection_held"), - &Terminal::_on_selection_held); - ClassDB::bind_method(D_METHOD("_toggle_blink"), &Terminal::_toggle_blink); -} - -void Terminal::_bell_cb(tsm_vte *vte, void *data) { - Terminal *term = static_cast(data); - - if (!term->bell_muted && term->bell_cooldown == 0 || - term->bell_timer->get_time_left() == 0) { - term->emit_signal("bell"); - if (term->bell_cooldown > 0) - term->bell_timer->start(term->bell_cooldown); - } -} - -int Terminal::_text_draw_cb(tsm_screen *con, uint64_t id, const uint32_t *ch, - size_t len, unsigned int width, unsigned int col, - unsigned int row, const tsm_screen_attr *attr, - tsm_age_t age, void *data) { - Terminal *term = static_cast(data); - if (term->update_mode == Terminal::UpdateMode::AUTO && age != 0 && - age <= term->framebuffer_age) { - return 0; - } - - if (width < 1) { // No foreground or background to draw. - return 0; - } - - ColorPair color_pair = term->_get_cell_colors(attr); - term->_draw_background(row, col, color_pair.first, width); - - if (len < 1) // No foreground to draw. - return 0; - - size_t ulen = 0; - char buf[5] = {0}; - - char *utf8 = tsm_ucs4_to_utf8_alloc(ch, len, &ulen); - memcpy(buf, utf8, ulen); - term->_draw_foreground(row, col, buf, attr, color_pair.second); - - return 0; -} - -void Terminal::_write_cb(tsm_vte *vte, const char *u8, size_t len, void *data) { - Terminal *term = static_cast(data); - - PackedByteArray bytes; - bytes.resize(len); - { memcpy(bytes.ptrw(), u8, len); } - - if (len > 0) { - if (term->last_input_event_key.is_valid()) { - // The callback was fired from a key press event so emit the "key_pressed" - // signal. - term->emit_signal("key_pressed", bytes.get_string_from_utf8(), - term->last_input_event_key); - term->last_input_event_key.unref(); - } - - term->emit_signal("data_sent", bytes); - } -} - -void Terminal::_draw_background(int row, int col, Color bgcolor, - int width = 1) { - /* Draw the background */ - Vector2 background_pos = Vector2(col * cell_size.x, row * cell_size.y); - Rect2 background_rect = Rect2(background_pos, cell_size * Vector2(width, 1)); - back_buffer->draw_rect(background_rect, bgcolor); -} - -void Terminal::_draw_foreground(int row, int col, char *ch, - const tsm_screen_attr *attr, Color fgcolor) { - Ref font; - - if (attr->bold && attr->italic) { - font = theme_cache.fonts["bold_italics_font"]; - } else if (attr->bold) { - font = theme_cache.fonts["bold_font"]; - } else if (attr->italic) { - font = theme_cache.fonts["italics_font"]; - } else { - font = theme_cache.fonts["normal_font"]; - } - - if (attr->blink && blink_enabled) { - if (blink_timer->is_stopped()) - blink_timer->start(blink_on ? blink_time_on : blink_time_off); - - if (!blink_on) - return; - } - - int font_height = font->get_height(theme_cache.font_size); - Vector2 foreground_pos = - Vector2(col * cell_size.x, row * cell_size.y + font_height / 1.25); - back_buffer->draw_string(font, foreground_pos, ch, HORIZONTAL_ALIGNMENT_LEFT, - -1, theme_cache.font_size, fgcolor); - - if (attr->underline) - back_buffer->draw_string(font, foreground_pos, "_", - HORIZONTAL_ALIGNMENT_LEFT, -1, - theme_cache.font_size, fgcolor); -} - -Terminal::ColorPair Terminal::_get_cell_colors(const tsm_screen_attr *attr) { - Color fgcol, bgcol; - int8_t fccode = attr->fccode; - int8_t bccode = attr->bccode; - - // Get foreground color. - if (fccode && palette.count(fccode)) { - fgcol = palette[fccode]; - } else { - fgcol = Color(attr->fr / 255.0f, attr->fg / 255.0f, attr->fb / 255.0f); - - if (fccode != -1) - palette.insert({fccode, fgcol}); - } - - // Get background color. - if (bccode && palette.count(bccode)) { - bgcol = palette[bccode]; - } else { - bgcol = Color(attr->br / 255.0f, attr->bg / 255.0f, attr->bb / 255.0f); - - if (bccode != -1) - palette.insert({bccode, bgcol}); - } - - if (attr->inverse) - std::swap(bgcol, fgcol); - - return std::make_pair(bgcol, fgcol); -} - -void Terminal::_handle_key_input(Ref event) { - if (!event.is_valid() || !event->is_pressed()) - return; - - const Key keycode = event->get_keycode(); - char32_t unicode = event->get_unicode(); - uint32_t ascii = unicode <= 127 ? unicode : 0; - - unsigned int mods = 0; - if (event->is_alt_pressed()) - mods |= TSM_ALT_MASK; - if (event->is_ctrl_pressed()) - mods |= TSM_CONTROL_MASK; - if (event->is_shift_pressed()) - mods |= TSM_SHIFT_MASK; - - std::pair key = {keycode, unicode}; - uint32_t keysym = - (KEY_MAP.count(key) > 0) ? KEY_MAP.at(key) : XKB_KEY_NoSymbol; - - last_input_event_key = event; - tsm_vte_handle_keyboard(vte, keysym, ascii, mods, - unicode ? unicode : TSM_VTE_INVALID); - - // Return to the bottom of the scrollback buffer if we scrolled up. Ignore - // modifier keys pressed in isolation or if Ctrl+Shift modifier keys are - // pressed. - std::set mod_keys = {KEY_ALT, KEY_SHIFT, KEY_CTRL, KEY_META}; - if (mod_keys.find(keycode) == mod_keys.end() && - !(event->is_ctrl_pressed() && event->is_shift_pressed())) { - tsm_screen_sb_reset(screen); - back_buffer->queue_redraw(); - } - - // Prevent focus changing to other inputs when pressing Tab or Arrow keys. - std::set tab_arrow_keys = {KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, - KEY_TAB}; - if (tab_arrow_keys.find(keycode) != tab_arrow_keys.end()) - accept_event(); -} - -void Terminal::_handle_mouse_wheel(Ref event) { - if (!event.is_valid() || !event->is_pressed()) - return; - - void (*scroll_func)(tsm_screen *, unsigned int) = nullptr; - - switch (event->get_button_index()) { - case MOUSE_BUTTON_WHEEL_UP: - scroll_func = &tsm_screen_sb_up; - break; - case MOUSE_BUTTON_WHEEL_DOWN: - scroll_func = &tsm_screen_sb_down; - break; - }; - - if (scroll_func != nullptr) { - // Scroll 5 times as fast as normal if alt is pressed (like TextEdit). - // Otherwise, just scroll 3 lines. - int speed = event->is_alt_pressed() ? 15 : 3; - double factor = event->get_factor(); - (*scroll_func)(screen, speed * factor); - back_buffer->queue_redraw(); - } -} - -void Terminal::_handle_selection(Ref event) { - if (!event.is_valid()) - return; - - Ref mb = event; - if (mb.is_valid()) { - if (!mb->is_pressed() || !mb->get_button_index() == MOUSE_BUTTON_LEFT) - return; - - if (selecting) { - selecting = false; - selection_mode = SelectionMode::NONE; - tsm_screen_selection_reset(screen); - back_buffer->queue_redraw(); - } - - selecting = false; - selection_mode = SelectionMode::POINTER; - - return; - } - - Ref mm = event; - if (mm.is_valid()) { - if ((mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) && - selection_mode != SelectionMode::NONE && !selecting) { - selecting = true; - Vector2 start = event->get_position() / cell_size; - tsm_screen_selection_start(screen, start.x, start.y); - back_buffer->queue_redraw(); - selection_timer->start(); - } - return; - } -} - -void Terminal::_recalculate_size() { - Vector2 size = get_size(); - - cell_size = theme_cache.fonts["normal_font"]->get_string_size( - "W", HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size); - - rows = std::max(1, (int)floor(size.y / cell_size.y)); - cols = std::max(1, (int)floor(size.x / cell_size.x)); - - tsm_screen_resize(screen, cols, rows); - sub_viewport->set_size(size); - - emit_signal("size_changed", Vector2(cols, rows)); -} - -void Terminal::_refresh() { - back_buffer->queue_redraw(); - front_buffer->queue_redraw(); - - if (update_mode == UpdateMode::AUTO) - update_mode = UpdateMode::ALL_NEXT_FRAME; -} - -void Terminal::_update_theme_item_cache() { - // Fonts. - for (std::map::const_iterator iter = - Terminal::FONTS.begin(); - iter != Terminal::FONTS.end(); ++iter) { - String name = iter->first; - - Ref font = has_theme_font_override(name) ? get_theme_font(name) - : has_theme_font(name, "Terminal") - ? get_theme_font(name, "Terminal") - : ThemeDB::get_singleton()->get_fallback_font(); - - theme_cache.fonts[name] = font; - } - - // Font size. - theme_cache.font_size = - has_theme_font_size_override("font_size") - ? get_theme_font_size("font_size") - : has_theme_font_size("font_size", "Terminal") - ? get_theme_font_size("font_size", "Terminal") - : ThemeDB::get_singleton()->get_fallback_font_size(); - - // Colors. - uint8_t custom_palette[TSM_COLOR_NUM][3]; - - for (ColorMap::const_iterator iter = Terminal::COLORS.begin(); - iter != Terminal::COLORS.end(); ++iter) { - String name = iter->first; - - Color color = has_theme_color_override(name) ? get_theme_color(name) - : has_theme_color(name, "Terminal") - ? get_theme_color(name, "Terminal") - : color = Color::html(iter->second.default_color); - - theme_cache.colors[name] = color; - palette[iter->second.tsm_color] = color; - custom_palette[iter->second.tsm_color][0] = color.get_r8(); - custom_palette[iter->second.tsm_color][1] = color.get_g8(); - custom_palette[iter->second.tsm_color][2] = color.get_b8(); - } - - if (tsm_vte_set_custom_palette(vte, custom_palette)) - ERR_PRINT("Error setting custom palette."); - if (tsm_vte_set_palette(vte, "custom")) - ERR_PRINT("Error setting palette to custom palette."); - - _recalculate_size(); -} diff --git a/addons/godot_xterm/native/src_old/terminal.h b/addons/godot_xterm/native/src_old/terminal.h deleted file mode 100644 index d893818..0000000 --- a/addons/godot_xterm/native/src_old/terminal.h +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2023 Leroy Hopson -// SPDX-License-Identifier: MIT - -#ifndef GODOT_XTERM_TERMINAL_H -#define GODOT_XTERM_TERMINAL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace godot; - -class Terminal : public Control { - GDCLASS(Terminal, Control) - -public: - Terminal(); - ~Terminal(); - - enum UpdateMode { - DISABLED, - AUTO, - ALL, - ALL_NEXT_FRAME, - }; - - static const UpdateMode UPDATE_MODE_DISABLED = UpdateMode::DISABLED; - static const UpdateMode UPDATE_MODE_AUTO = UpdateMode::AUTO; - static const UpdateMode UPDATE_MODE_ALL = UpdateMode::ALL; - static const UpdateMode UPDATE_MODE_ALL_NEXT_FRAME = - UpdateMode::ALL_NEXT_FRAME; - - bool copy_on_selection = false; - void set_copy_on_selection(bool value); - bool get_copy_on_selection(); - - UpdateMode update_mode = UPDATE_MODE_AUTO; - void set_update_mode(UpdateMode value); - UpdateMode get_update_mode(); - - double bell_cooldown = 0.1f; - void set_bell_cooldown(double value); - double get_bell_cooldown(); - - bool bell_muted = false; - void set_bell_muted(bool value); - bool get_bell_muted(); - - bool blink_enabled = true; - void set_blink_enabled(bool value); - bool get_blink_enabled(); - - double blink_time_off = 0.3; - void set_blink_time_off(double value); - double get_blink_time_off(); - - double blink_time_on = 0.6; - void set_blink_time_on(double value); - double get_blink_time_on(); - - void clear(); - String copy_all(); - String copy_selection(); - int get_cols(); - int get_rows(); - void write(Variant data); - - void _gui_input(Ref event); - void _notification(int what); - - void _flush(); - void _on_back_buffer_draw(); - void _on_selection_held(); - void _toggle_blink(); - -protected: - static void _bind_methods(); - -private: - struct ColorDef { - const char *default_color; - tsm_vte_color tsm_color; - }; - - struct ThemeCache { - int font_size = 0; - std::map> fonts = std::map>{}; - std::map colors = std::map{}; - } theme_cache; - - typedef std::map ColorMap; - typedef std::pair ColorPair; - typedef std::map FontMap; - typedef std::map, uint32_t> KeyMap; - - static const KeyMap KEY_MAP; - static const ColorMap COLORS; - static const FontMap FONTS; - - enum SelectionMode { NONE, POINTER }; - - Control *back_buffer = new Control(); - SubViewport *sub_viewport = new SubViewport(); - TextureRect *front_buffer = new TextureRect(); - - Timer *bell_timer = new Timer(); - Timer *blink_timer = new Timer(); - Timer *selection_timer = new Timer(); - - tsm_screen *screen; - tsm_vte *vte; - - Array write_buffer = Array(); - - int cols = 80; - int rows = 24; - - // Whether blinking characters are visible. Not whether blinking is enabled - // which is determined by `blink_enabled`. - bool blink_on = true; - - Vector2 cell_size = Vector2(0, 0); - - std::map palette = {}; - - tsm_age_t framebuffer_age; - - Ref last_input_event_key; - - bool selecting = false; - SelectionMode selection_mode = SelectionMode::NONE; - - static void _bell_cb(tsm_vte *vte, void *data); - static int _text_draw_cb(tsm_screen *con, uint64_t id, const uint32_t *ch, - size_t len, unsigned int width, unsigned int col, - unsigned int row, const tsm_screen_attr *attr, - tsm_age_t age, void *data); - static void _write_cb(tsm_vte *vte, const char *u8, size_t len, void *data); - - void _draw_background(int row, int col, Color bgcol, int width); - void _draw_foreground(int row, int col, char *ch, const tsm_screen_attr *attr, - Color fgcol); - ColorPair _get_cell_colors(const tsm_screen_attr *attr); - void _handle_key_input(Ref event); - void _handle_mouse_wheel(Ref event); - void _handle_selection(Ref event); - void _recalculate_size(); - void _refresh(); - void _update_theme_item_cache(); -}; - -VARIANT_ENUM_CAST(Terminal::UpdateMode); - -#endif // GODOT_XTERM_TERMINAL_H