// SPDX-FileCopyrightText: 2021, 2023-2024 Leroy Hopson // SPDX-License-Identifier: MIT #include "pty.h" #include "terminal.h" #include #include using namespace godot; const char* Terminal::COLOR_NAMES[18] = { "ansi_0_color", "ansi_1_color", "ansi_2_color", "ansi_3_color", "ansi_4_color", "ansi_5_color", "ansi_6_color", "ansi_7_color", "ansi_8_color", "ansi_9_color", "ansi_10_color", "ansi_11_color", "ansi_12_color", "ansi_13_color", "ansi_14_color", "ansi_15_color", "foreground_color", "background_color", }; const char* Terminal::FONT_TYPES[4] = { "normal_font", "bold_font", "italics_font", "bold_italics_font", }; const Terminal::KeyMap Terminal::KEY_MAP = { // Godot does not have seperate scancodes for keypad keys when NumLock is // off. We can check the unicode value to determine whether it is off and // set the appropriate scancode. Based on the patch which adds support for // this to TextEdit/LineEdit: // https://github.com/godotengine/godot/pull/3269/files {{KEY_KP_0, '0'}, XKB_KEY_KP_0}, {{KEY_KP_0, '\0'}, XKB_KEY_KP_Insert}, {{KEY_KP_1, '1'}, XKB_KEY_KP_1}, {{KEY_KP_1, '\0'}, XKB_KEY_KP_End}, {{KEY_KP_2, '2'}, XKB_KEY_KP_2}, {{KEY_KP_2, '\0'}, XKB_KEY_KP_Down}, {{KEY_KP_3, '3'}, XKB_KEY_KP_3}, {{KEY_KP_3, '\0'}, XKB_KEY_KP_Page_Down}, {{KEY_KP_4, '4'}, XKB_KEY_KP_4}, {{KEY_KP_4, '\0'}, XKB_KEY_KP_Left}, {{KEY_KP_5, '5'}, XKB_KEY_KP_5}, {{KEY_KP_5, '\0'}, XKB_KEY_KP_Begin}, {{KEY_KP_6, '6'}, XKB_KEY_KP_6}, {{KEY_KP_6, '\0'}, XKB_KEY_KP_Right}, {{KEY_KP_7, '7'}, XKB_KEY_KP_7}, {{KEY_KP_7, '\0'}, XKB_KEY_KP_Home}, {{KEY_KP_8, '8'}, XKB_KEY_KP_8}, {{KEY_KP_8, '\0'}, XKB_KEY_KP_Up}, {{KEY_KP_9, '9'}, XKB_KEY_KP_9}, {{KEY_KP_9, '\0'}, XKB_KEY_KP_Page_Up}, {{KEY_KP_PERIOD, '.'}, XKB_KEY_KP_Decimal}, {{KEY_KP_PERIOD, '\0'}, XKB_KEY_KP_Delete}, {{KEY_KP_DIVIDE, '/'}, XKB_KEY_KP_Divide}, {{KEY_KP_MULTIPLY, '*'}, XKB_KEY_KP_Multiply}, {{KEY_KP_SUBTRACT, '-'}, XKB_KEY_KP_Subtract}, {{KEY_KP_ADD, '+'}, XKB_KEY_KP_Add}, {{KEY_KP_ENTER, '\0'}, XKB_KEY_KP_Enter}, //{{ , }, XKB_KEY_KP_Equal}, //{{ , }, XKB_KEY_KP_Separator}, //{{ , }, XKB_KEY_KP_Tab}, //{{ , }, XKB_KEY_KP_F1}, //{{ , }, XKB_KEY_KP_F2}, //{{ , }, XKB_KEY_KP_F3}, //{{ , }, XKB_KEY_KP_F4}, // Godot scancodes do not distinguish between uppercase and lowercase // letters, so we can check the unicode value to determine this. {{KEY_A, 'a'}, XKB_KEY_a}, {{KEY_A, 'A'}, XKB_KEY_A}, {{KEY_B, 'b'}, XKB_KEY_b}, {{KEY_B, 'B'}, XKB_KEY_B}, {{KEY_C, 'c'}, XKB_KEY_c}, {{KEY_C, 'C'}, XKB_KEY_C}, {{KEY_D, 'd'}, XKB_KEY_d}, {{KEY_D, 'D'}, XKB_KEY_D}, {{KEY_E, 'e'}, XKB_KEY_e}, {{KEY_E, 'E'}, XKB_KEY_E}, {{KEY_F, 'f'}, XKB_KEY_f}, {{KEY_F, 'F'}, XKB_KEY_F}, {{KEY_G, 'g'}, XKB_KEY_g}, {{KEY_G, 'G'}, XKB_KEY_G}, {{KEY_H, 'h'}, XKB_KEY_h}, {{KEY_H, 'H'}, XKB_KEY_H}, {{KEY_I, 'i'}, XKB_KEY_i}, {{KEY_I, 'I'}, XKB_KEY_I}, {{KEY_J, 'j'}, XKB_KEY_j}, {{KEY_J, 'J'}, XKB_KEY_J}, {{KEY_K, 'k'}, XKB_KEY_k}, {{KEY_K, 'K'}, XKB_KEY_K}, {{KEY_L, 'l'}, XKB_KEY_l}, {{KEY_L, 'L'}, XKB_KEY_L}, {{KEY_M, 'm'}, XKB_KEY_m}, {{KEY_M, 'M'}, XKB_KEY_M}, {{KEY_N, 'n'}, XKB_KEY_n}, {{KEY_N, 'N'}, XKB_KEY_N}, {{KEY_O, 'o'}, XKB_KEY_o}, {{KEY_O, 'O'}, XKB_KEY_O}, {{KEY_P, 'p'}, XKB_KEY_p}, {{KEY_P, 'P'}, XKB_KEY_P}, {{KEY_Q, 'q'}, XKB_KEY_q}, {{KEY_Q, 'Q'}, XKB_KEY_Q}, {{KEY_R, 'r'}, XKB_KEY_r}, {{KEY_R, 'R'}, XKB_KEY_R}, {{KEY_S, 's'}, XKB_KEY_s}, {{KEY_S, 'S'}, XKB_KEY_S}, {{KEY_T, 't'}, XKB_KEY_t}, {{KEY_T, 'T'}, XKB_KEY_T}, {{KEY_U, 'u'}, XKB_KEY_u}, {{KEY_U, 'U'}, XKB_KEY_U}, {{KEY_V, 'v'}, XKB_KEY_v}, {{KEY_V, 'V'}, XKB_KEY_V}, {{KEY_W, 'w'}, XKB_KEY_w}, {{KEY_W, 'W'}, XKB_KEY_W}, {{KEY_X, 'x'}, XKB_KEY_x}, {{KEY_X, 'X'}, XKB_KEY_X}, {{KEY_Y, 'y'}, XKB_KEY_y}, {{KEY_Y, 'Y'}, XKB_KEY_Y}, {{KEY_Z, 'z'}, XKB_KEY_z}, {{KEY_Z, 'Z'}, XKB_KEY_Z}, {{KEY_0, '0'}, XKB_KEY_0}, {{KEY_1, '1'}, XKB_KEY_1}, {{KEY_2, '2'}, XKB_KEY_2}, {{KEY_3, '3'}, XKB_KEY_3}, {{KEY_4, '4'}, XKB_KEY_4}, {{KEY_5, '5'}, XKB_KEY_5}, {{KEY_6, '6'}, XKB_KEY_6}, {{KEY_7, '7'}, XKB_KEY_7}, {{KEY_8, '8'}, XKB_KEY_8}, {{KEY_9, '9'}, XKB_KEY_9}, {{KEY_BRACKETLEFT, '['}, XKB_KEY_bracketleft}, {{KEY_BRACKETLEFT, ']'}, XKB_KEY_bracketright}, {{KEY_BRACELEFT, '{'}, XKB_KEY_braceleft}, {{KEY_BRACERIGHT, '}'}, XKB_KEY_braceright}, {{KEY_BACKSLASH, '\\'}, XKB_KEY_backslash}, {{KEY_BAR, '|'}, XKB_KEY_bar}, {{KEY_QUOTELEFT, '`'}, XKB_KEY_grave}, {{KEY_ASCIITILDE, '~'}, XKB_KEY_asciitilde}, {{KEY_SLASH, '/'}, XKB_KEY_slash}, {{KEY_QUESTION, '?'}, XKB_KEY_question}, {{KEY_HOME, '\0'}, XKB_KEY_Home}, {{KEY_BACKSPACE, '\0'}, XKB_KEY_BackSpace}, {{KEY_BACKTAB, '\0'}, XKB_KEY_ISO_Left_Tab}, {{KEY_CLEAR, '\0'}, XKB_KEY_Clear}, {{KEY_PAUSE, '\0'}, XKB_KEY_Pause}, {{KEY_SCROLLLOCK, '\0'}, XKB_KEY_Scroll_Lock}, {{KEY_SYSREQ, '\0'}, XKB_KEY_Sys_Req}, {{KEY_ESCAPE, '\0'}, XKB_KEY_Escape}, {{KEY_ENTER, '\0'}, XKB_KEY_Return}, {{KEY_INSERT, '\0'}, XKB_KEY_Insert}, {{KEY_DELETE, '\0'}, XKB_KEY_Delete}, {{KEY_PAGEUP, '\0'}, XKB_KEY_Page_Up}, {{KEY_PAGEDOWN, '\0'}, XKB_KEY_Page_Down}, {{KEY_UP, '\0'}, XKB_KEY_Up}, {{KEY_DOWN, '\0'}, XKB_KEY_Down}, {{KEY_RIGHT, '\0'}, XKB_KEY_Right}, {{KEY_LEFT, '\0'}, XKB_KEY_Left}, {{KEY_TAB, '\0'}, XKB_KEY_Tab}, //{{ , }, XKB_KEY_Linefeed}, //{{ , }, XKB_KEY_Find}, //{{ , }, XKB_KEY_Select}, {{KEY_F1, '\0'}, XKB_KEY_F1}, {{KEY_F2, '\0'}, XKB_KEY_F2}, {{KEY_F3, '\0'}, XKB_KEY_F3}, {{KEY_F4, '\0'}, XKB_KEY_F4}, {{KEY_F5, '\0'}, XKB_KEY_F5}, {{KEY_F6, '\0'}, XKB_KEY_F6}, {{KEY_F7, '\0'}, XKB_KEY_F7}, {{KEY_F8, '\0'}, XKB_KEY_F8}, {{KEY_F9, '\0'}, XKB_KEY_F9}, {{KEY_F10, '\0'}, XKB_KEY_F10}, {{KEY_F11, '\0'}, XKB_KEY_F11}, {{KEY_F12, '\0'}, XKB_KEY_F12}, {{KEY_F13, '\0'}, XKB_KEY_F13}, {{KEY_F14, '\0'}, XKB_KEY_F14}, {{KEY_F15, '\0'}, XKB_KEY_F15}, {{KEY_F16, '\0'}, XKB_KEY_F16}, {{KEY_F17, '\0'}, XKB_KEY_F17}, {{KEY_F18, '\0'}, XKB_KEY_F18}, {{KEY_F19, '\0'}, XKB_KEY_F19}, {{KEY_F20, '\0'}, XKB_KEY_F20}, {{KEY_F21, '\0'}, XKB_KEY_F21}, {{KEY_F22, '\0'}, XKB_KEY_F22}, {{KEY_F23, '\0'}, XKB_KEY_F23}, {{KEY_F24, '\0'}, XKB_KEY_F24}, {{KEY_F25, '\0'}, XKB_KEY_F25}, {{KEY_F26, '\0'}, XKB_KEY_F26}, {{KEY_F27, '\0'}, XKB_KEY_F27}, {{KEY_F28, '\0'}, XKB_KEY_F28}, {{KEY_F29, '\0'}, XKB_KEY_F29}, {{KEY_F30, '\0'}, XKB_KEY_F30}, {{KEY_F31, '\0'}, XKB_KEY_F31}, {{KEY_F32, '\0'}, XKB_KEY_F32}, {{KEY_F33, '\0'}, XKB_KEY_F33}, {{KEY_F34, '\0'}, XKB_KEY_F34}, {{KEY_F35, '\0'}, XKB_KEY_F35}, }; Error PTY::uv_err_to_godot_err(const 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 } }