#include "il2cpp-config.h" #if IL2CPP_TARGET_POSIX && !IL2CPP_USE_PLATFORM_SPECIFIC_CONSOLE #include "os/Console.h" #include "os/File.h" #include #include #include #include #include #include #include #include #include #include namespace il2cpp { namespace os { namespace Console { static bool setupComplete = false; static int32_t s_terminalSize; static struct termios s_initialAttr; static struct termios s_il2cppAttr; static std::string s_keypadXmit; static std::string s_teardown; static struct sigaction s_saveSigcont, s_saveSigint, s_saveSigwinch; static int32_t GetTerminalSize() { struct winsize ws; if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) return (ws.ws_col << 16) | ws.ws_row; return -1; } static bool SetProperty(int32_t property, bool value) { struct termios attr; bool callset = false; bool check; if (tcgetattr(STDIN_FILENO, &attr) == -1) return false; check = (attr.c_lflag & property) != 0; if ((value || check) && !(value && check)) { callset = true; if (value) attr.c_lflag |= property; else attr.c_lflag &= ~property; } if (!callset) return true; if (tcsetattr(STDIN_FILENO, TCSANOW, &attr) == -1) return true; s_il2cppAttr = attr; return true; } static void SetControlChars(uint8_t* control_chars, const uint8_t *cc) { // The index into the array comes from corlib/System/ControlCharacters.cs #ifdef VINTR control_chars[0] = cc[VINTR]; #endif #ifdef VQUIT control_chars[1] = cc[VQUIT]; #endif #ifdef VERASE control_chars[2] = cc[VERASE]; #endif #ifdef VKILL control_chars[3] = cc[VKILL]; #endif #ifdef VEOF control_chars[4] = cc[VEOF]; #endif #ifdef VTIME control_chars[5] = cc[VTIME]; #endif #ifdef VMIN control_chars[6] = cc[VMIN]; #endif #ifdef VSWTC control_chars[7] = cc[VSWTC]; #endif #ifdef VSTART control_chars[8] = cc[VSTART]; #endif #ifdef VSTOP control_chars[9] = cc[VSTOP]; #endif #ifdef VSUSP control_chars[10] = cc[VSUSP]; #endif #ifdef VEOL control_chars[11] = cc[VEOL]; #endif #ifdef VREPRINT control_chars[12] = cc[VREPRINT]; #endif #ifdef VDISCARD control_chars[13] = cc[VDISCARD]; #endif #ifdef VWERASE control_chars[14] = cc[VWERASE]; #endif #ifdef VLNEXT control_chars[15] = cc[VLNEXT]; #endif #ifdef VEOL2 control_chars[16] = cc[VEOL2]; #endif } static void CallDoConsoleCancelEvent() { // TODO: Call Console.cancel_handler delegate from another thread. } static void SigintHandler(int signo) { static bool insideSigint = false; if (insideSigint) return; insideSigint = true; CallDoConsoleCancelEvent(); insideSigint = false; } static void SigcontHandler(int signo, siginfo_t *the_siginfo, void *data) { // Ignore error, there is not much we can do in the sigcont handler. tcsetattr(STDIN_FILENO, TCSANOW, &s_il2cppAttr); if (!s_keypadXmit.empty()) write(STDOUT_FILENO, s_keypadXmit.c_str(), s_keypadXmit.length()); // Call previous handler if (s_saveSigcont.sa_sigaction != NULL && s_saveSigcont.sa_sigaction != (void*)SIG_DFL && s_saveSigcont.sa_sigaction != (void*)SIG_IGN) (*s_saveSigcont.sa_sigaction)(signo, the_siginfo, data); } static void SigwinchHandler(int signo, siginfo_t *the_siginfo, void *data) { const int32_t size = GetTerminalSize(); if (size != -1) s_terminalSize = size; // Call previous handler if (s_saveSigwinch.sa_sigaction != NULL && s_saveSigwinch.sa_sigaction != (void*)SIG_DFL && s_saveSigwinch.sa_sigaction != (void*)SIG_IGN) (*s_saveSigwinch.sa_sigaction)(signo, the_siginfo, data); } static void ConsoleSetupSignalHandler() { struct sigaction sigcont, sigint, sigwinch; memset(&sigcont, 0, sizeof(struct sigaction)); memset(&sigint, 0, sizeof(struct sigaction)); memset(&sigwinch, 0, sizeof(struct sigaction)); // Continuing sigcont.sa_sigaction = SigcontHandler; sigcont.sa_flags = SA_SIGINFO; sigemptyset(&sigcont.sa_mask); sigaction(SIGCONT, &sigcont, &s_saveSigcont); // Interrupt handler sigint.sa_handler = SigintHandler; sigint.sa_flags = 0; sigemptyset(&sigint.sa_mask); sigaction(SIGINT, &sigint, &s_saveSigint); // Window size changed sigwinch.sa_sigaction = SigwinchHandler; sigwinch.sa_flags = SA_SIGINFO; sigemptyset(&sigwinch.sa_mask); sigaction(SIGWINCH, &sigwinch, &s_saveSigwinch); } // Exists in Mono, but is unused. static void ConsoleRestoreSignalHandlers() { sigaction(SIGCONT, &s_saveSigcont, NULL); sigaction(SIGINT, &s_saveSigint, NULL); sigaction(SIGWINCH, &s_saveSigwinch, NULL); } int32_t InternalKeyAvailable(int32_t ms_timeout) { fd_set rfds; struct timeval tv; struct timeval *tvptr; div_t divvy; int32_t ret, nbytes; do { FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); if (ms_timeout >= 0) { divvy = div(ms_timeout, 1000); tv.tv_sec = divvy.quot; tv.tv_usec = divvy.rem; tvptr = &tv; } else { tvptr = NULL; } ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr); } while (ret == -1 && errno == EINTR); if (ret > 0) { nbytes = 0; ret = ioctl(STDIN_FILENO, FIONREAD, &nbytes); if (ret >= 0) ret = nbytes; } return (ret > 0) ? ret : 0; } bool SetBreak(bool wantBreak) { return SetProperty(IGNBRK, !wantBreak); } bool SetEcho(bool wantEcho) { return SetProperty(ECHO, wantEcho); } static void TtyShutdown() { if (!setupComplete) return; if (!s_teardown.empty()) write(STDOUT_FILENO, s_teardown.c_str(), s_teardown.length()); tcflush(STDIN_FILENO, TCIFLUSH); tcsetattr(STDIN_FILENO, TCSANOW, &s_initialAttr); SetProperty(ECHO, true); setupComplete = false; } bool TtySetup(const std::string& keypadXmit, const std::string& teardown, uint8_t* control_characters, int32_t** size) { s_terminalSize = GetTerminalSize(); if (s_terminalSize == -1) { int32_t cols = 0, rows = 0; const char *colsValue = getenv("COLUMNS"); if (colsValue != NULL) cols = atoi(colsValue); const char *linesValue = getenv("LINES"); if (linesValue != NULL) rows = atoi(linesValue); if (cols != 0 && rows != 0) s_terminalSize = (cols << 16) | rows; else s_terminalSize = -1; } *size = &s_terminalSize; if (tcgetattr(STDIN_FILENO, &s_initialAttr) == -1) return false; s_il2cppAttr = s_initialAttr; s_il2cppAttr.c_lflag &= ~(ICANON); s_il2cppAttr.c_iflag &= ~(IXON | IXOFF); s_il2cppAttr.c_cc[VMIN] = 1; s_il2cppAttr.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &s_il2cppAttr) == -1) return false; s_keypadXmit = keypadXmit; SetControlChars(control_characters, s_il2cppAttr.c_cc); if (setupComplete) return true; ConsoleSetupSignalHandler(); setupComplete = true; s_teardown = teardown; atexit(TtyShutdown); return true; } const char* NewLine() { return "\n"; } } } } #endif