diff --git a/src/xtd.core.native.macos/src/xtd/native/macos/console.mm b/src/xtd.core.native.macos/src/xtd/native/macos/console.mm index afb48f845f99..20cdcf049b0a 100644 --- a/src/xtd.core.native.macos/src/xtd/native/macos/console.mm +++ b/src/xtd.core.native.macos/src/xtd/native/macos/console.mm @@ -41,28 +41,32 @@ static void signal_handler(int_least32_t signal) { } // The SIGINT signal catcher conflicts with with xtd::environment::cancel_interrupt signal... - inline static std::map signal_keys_ {{SIGQUIT, CONSOLE_SPECIAL_KEY_CTRL_BS}, {SIGTSTP, CONSOLE_SPECIAL_KEY_CTRL_Z}/*, {SIGINT, CONSOLE_SPECIAL_KEY_CTRL_C}*/}; + inline static std::map signal_keys_ {{SIGQUIT, CONSOLE_SPECIAL_KEY_CTRL_BS}, {SIGTSTP, CONSOLE_SPECIAL_KEY_CTRL_Z}/*, {SIGINT, CONSOLE_SPECIAL_KEY_CTRL_C}*/}; static console_intercept_signals console_intercept_signals_; }; console_intercept_signals console_intercept_signals::console_intercept_signals_; class terminal final { - private: - terminal() { - auto termioAttributes = termios {}; - tcgetattr(0, &termioAttributes); - backupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~ECHO; - tcsetattr(0, TCSANOW, &termioAttributes); - } - ~terminal() { - tcsetattr(0, TCSANOW, &backupedTermioAttributes); - if (is_ansi_supported()) - std::cout << "\x1b]0;\x7" << std::flush; + public: + bool echo(bool on) { + auto status = termios {}; + tcgetattr(0, &status); + if (on) status.c_lflag |= ECHO; + else status.c_lflag &= ~ECHO; + return tcsetattr(0, TCSANOW, &status) == 0; } - public: + bool icanon(bool on) { + auto status = termios {}; + tcgetattr(0, &status); + if (on) status.c_lflag |= ICANON; + else status.c_lflag &= ~ICANON; + status.c_cc[VMIN] = on ? 1 : 0; + status.c_cc[VTIME] = 0; // Can be discarded. + return tcsetattr(0, TCSANOW, &status) == 0; + } + int_least32_t getch() { if (peek_character != -1) { auto character = peek_character; @@ -70,19 +74,14 @@ int_least32_t getch() { return character; } - auto termioAttributes = termios {}; - tcgetattr(0, &termioAttributes); - auto localeBackupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~(ICANON | ECHO); - termioAttributes.c_cc[VTIME] = 0; - termioAttributes.c_cc[VMIN] = 1; - tcsetattr(0, TCSANOW, &termioAttributes); - + push_status(); + echo(false); + icanon(false); + auto character = '\0'; while (read(0, &character, 1) != 1); - - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); - + + pop_status(); return character; } @@ -90,20 +89,16 @@ bool key_available() { if (peek_character != -1) return true; - auto termioAttributes = termios {}; - tcgetattr(0, &termioAttributes); - auto localeBackupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~(ICANON | ECHO); - termioAttributes.c_cc[VTIME] = 0; - termioAttributes.c_cc[VMIN] = 0; - tcsetattr(0, TCSANOW, &termioAttributes); + push_status(); + echo(false); + icanon(false); if (read(0, &peek_character, 1) == -1) { - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); + pop_status(); return false; } - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); + pop_status(); return peek_character != -1; } @@ -115,8 +110,42 @@ static bool is_ansi_supported() { static terminal terminal_; private: + terminal() { + push_status(); + echo(false); + icanon(true); + } + ~terminal() { + pop_status(); + reset_colors_and_attributes(); + } + + void reset_colors_and_attributes() { + if (is_ansi_supported()) std::cout << "\x1b]0;\x7" << std::flush; + } + + termios push_status() { + auto status = termios {}; + tcgetattr(0, &status); + statuses.push_back(status); + return status; + } + + termios pop_status() { + if (statuses.size() == 0) { + auto status = termios {}; + tcgetattr(0, &status); + return status; + } + auto status = statuses.back(); + statuses.pop_back(); + tcsetattr(0, TCSANOW, &status); + return status; + } + + int_least8_t peek_character {-1}; - termios backupedTermioAttributes; + std::vector statuses; }; terminal terminal::terminal_; @@ -728,6 +757,10 @@ static OSStatus au_renderer_proc(void* in_ref_con, AudioUnitRenderActionFlags* i return ::foreground_color; } +bool console::echo(bool on) { + return terminal::terminal_.echo(on); +} + bool console::foreground_color(int_least32_t color) { static auto colors = std::map {{CONSOLE_COLOR_DEFAULT, "\x1b[39m"}, {CONSOLE_COLOR_BLACK, "\x1b[30m"}, {CONSOLE_COLOR_DARK_BLUE, "\x1b[34m"}, {CONSOLE_COLOR_DARK_GREEN, "\x1b[32m"}, {CONSOLE_COLOR_DARK_CYAN, "\x1b[36m"}, {CONSOLE_COLOR_DARK_RED, "\x1b[31m"}, {CONSOLE_COLOR_DARK_MAGENTA, "\x1b[35m"}, {CONSOLE_COLOR_DARK_YELLOW, "\x1b[33m"}, {CONSOLE_COLOR_GRAY, "\x1b[37m"}, {CONSOLE_COLOR_DARK_GRAY, "\x1b[90m"}, {CONSOLE_COLOR_BLUE, "\x1b[94m"}, {CONSOLE_COLOR_GREEN, "\x1b[92m"}, {CONSOLE_COLOR_CYAN, "\x1b[96m"}, {CONSOLE_COLOR_RED, "\x1b[91m"}, {CONSOLE_COLOR_MAGENTA, "\x1b[95m"}, {CONSOLE_COLOR_YELLOW, "\x1b[93m"}, {CONSOLE_COLOR_WHITE, "\x1b[97m"}}; auto it = colors.find(color); diff --git a/src/xtd.core.native.unix/src/xtd/native/unix/console.cpp b/src/xtd.core.native.unix/src/xtd/native/unix/console.cpp index 3ca22cb6e7e1..df71c44a5ffe 100644 --- a/src/xtd.core.native.unix/src/xtd/native/unix/console.cpp +++ b/src/xtd.core.native.unix/src/xtd/native/unix/console.cpp @@ -43,82 +43,111 @@ namespace { } // The SIGINT signal catcher conflicts with with xtd::environment::cancel_interrupt signal... - inline static std::map signal_keys_ {{SIGQUIT, CONSOLE_SPECIAL_KEY_CTRL_BS}, {SIGTSTP, CONSOLE_SPECIAL_KEY_CTRL_Z}/*, {SIGINT, CONSOLE_SPECIAL_KEY_CTRL_C}*/}; + inline static std::map signal_keys_ {{SIGQUIT, CONSOLE_SPECIAL_KEY_CTRL_BS}, {SIGTSTP, CONSOLE_SPECIAL_KEY_CTRL_Z}/*, {SIGINT, CONSOLE_SPECIAL_KEY_CTRL_C}*/}; static console_intercept_signals console_intercept_signals_; }; console_intercept_signals console_intercept_signals::console_intercept_signals_; class terminal final { - private: - terminal() { - termios termioAttributes; - tcgetattr(0, &termioAttributes); - backupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~ECHO; - tcsetattr(0, TCSANOW, &termioAttributes); + public: + bool echo(bool on) { + auto status = termios {}; + tcgetattr(0, &status); + if (on) status.c_lflag |= ECHO; + else status.c_lflag &= ~ECHO; + return tcsetattr(0, TCSANOW, &status) == 0; } - ~terminal() { - tcsetattr(0, TCSANOW, &backupedTermioAttributes); - if (is_ansi_supported()) - std::cout << "\x1b]0;\x7" << std::flush; + + bool icanon(bool on) { + auto status = termios {}; + tcgetattr(0, &status); + if (on) status.c_lflag |= ICANON; + else status.c_lflag &= ~ICANON; + status.c_cc[VMIN] = on ? 1 : 0; + status.c_cc[VTIME] = 0; // Can be discarded. + return tcsetattr(0, TCSANOW, &status) == 0; } - public: int_least32_t getch() { if (peek_character != -1) { - int_least8_t character = peek_character; + auto character = peek_character; peek_character = -1; return character; } - termios termioAttributes; - tcgetattr(0, &termioAttributes); - termios localeBackupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~(ICANON | ECHO); - termioAttributes.c_cc[VTIME] = 0; - termioAttributes.c_cc[VMIN] = 1; - tcsetattr(0, TCSANOW, &termioAttributes); + push_status(); + echo(false); + icanon(false); - int_least8_t character = 0; + auto character = '\0'; while (read(0, &character, 1) != 1); - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); - + pop_status(); return character; } bool key_available() { if (peek_character != -1) return true; - - termios termioAttributes; - tcgetattr(0, &termioAttributes); - termios localeBackupedTermioAttributes = termioAttributes; - termioAttributes.c_lflag &= ~(ICANON | ECHO); - termioAttributes.c_cc[VTIME] = 0; - termioAttributes.c_cc[VMIN] = 0; - tcsetattr(0, TCSANOW, &termioAttributes); + + push_status(); + echo(false); + icanon(false); if (read(0, &peek_character, 1) == -1) { - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); + pop_status(); return false; } - tcsetattr(0, TCSANOW, &localeBackupedTermioAttributes); + pop_status(); return peek_character != -1; } static bool is_ansi_supported() { - static std::string terminal = getenv("TERM") == nullptr ? "" : getenv("TERM"); + static auto terminal = std::string {getenv("TERM") == nullptr ? "" : getenv("TERM")}; return isatty(fileno(stdout)) && (terminal == "xterm" || terminal == "xterm-color" || terminal == "xterm-256color" || terminal == "screen" || terminal == "screen-256color" || terminal == "linux" || terminal == "cygwin"); } static terminal terminal_; private: + terminal() { + push_status(); + echo(false); + icanon(true); + } + ~terminal() { + pop_status(); + reset_colors_and_attributes(); + } + + void reset_colors_and_attributes() { + if (is_ansi_supported()) std::cout << "\x1b]0;\x7" << std::flush; + } + + termios push_status() { + auto status = termios {}; + tcgetattr(0, &status); + statuses.push_back(status); + return status; + } + + termios pop_status() { + if (statuses.size() == 0) { + auto status = termios {}; + tcgetattr(0, &status); + return status; + } + auto status = statuses.back(); + statuses.pop_back(); + tcsetattr(0, TCSANOW, &status); + return status; + } + + int_least8_t peek_character {-1}; - termios backupedTermioAttributes; + std::vector statuses; }; terminal terminal::terminal_; @@ -675,6 +704,10 @@ bool console::cursor_visible(bool visible) { return true; } +bool console::echo(bool on) { + return terminal::terminal_.echo(on); +} + int_least32_t console::foreground_color() { return ::foreground_color; } diff --git a/src/xtd.core.native.win32/src/xtd/native/win32/console.cpp b/src/xtd.core.native.win32/src/xtd/native/win32/console.cpp index 5f822155c9fd..e782c4b75c0e 100644 --- a/src/xtd.core.native.win32/src/xtd/native/win32/console.cpp +++ b/src/xtd.core.native.win32/src/xtd/native/win32/console.cpp @@ -207,6 +207,17 @@ bool console::cursor_visible(bool visible) { return SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci) == TRUE; } +bool console::echo(bool on) { + /* + DWORD mode = 0; + GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode); + if (on) mode |= ENABLE_ECHO_INPUT; + else mode &= ~ENABLE_ECHO_INPUT; + return SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode) == TRUE; + */ + return true; +} + int_least32_t console::foreground_color() { return ::foreground_color; } diff --git a/src/xtd.core.native/include/xtd/native/console.h b/src/xtd.core.native/include/xtd/native/console.h index c6cc62870f0a..e80e6acd2b48 100644 --- a/src/xtd.core.native/include/xtd/native/console.h +++ b/src/xtd.core.native/include/xtd/native/console.h @@ -97,6 +97,9 @@ namespace xtd { /// @param visible true if the cursor is visible; otherwise, false. /// @warning Internal use only static bool cursor_visible(bool visible); + + static bool echo(bool on); + /// @brief Gets the foreground color of the console. /// @return the foreground console color. /// @warning Internal use only diff --git a/src/xtd.core/src/xtd/console.cpp b/src/xtd.core/src/xtd/console.cpp index 04bfdce5217c..0bae35af0f04 100644 --- a/src/xtd.core/src/xtd/console.cpp +++ b/src/xtd.core/src/xtd/console.cpp @@ -302,7 +302,10 @@ int32 console::read() { ustring console::read_line() { register_cancel_key_press(); // Must be first... out.flush(); - return stream_reader {in}.read_line(); + if (!is_input_redirected()) native::console::echo(true); + auto result = stream_reader {in}.read_line(); + if (!is_input_redirected()) native::console::echo(false); + return result; } console_key_info console::read_key() {