| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "platform/globals.h" | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| #include "bin/console.h" | |
| #include "bin/file.h" | |
| #include "bin/lockers.h" | |
| #include "bin/platform.h" | |
| #include "bin/utils.h" | |
| #include "bin/utils_win.h" | |
| // These are not always defined in the header files. See: | |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx | |
| #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | |
| #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 | |
| #endif | |
| namespace dart { | |
| namespace bin { | |
| class ConsoleWin { | |
| public: | |
| static constexpr int kInvalidFlag = -1; | |
| static void Initialize() { | |
| saved_output_cp_ = kInvalidFlag; | |
| saved_input_cp_ = kInvalidFlag; | |
| // Set up a signal handler that restores the console state on a | |
| // CTRL_C_EVENT signal. This will only run when there is no signal handler | |
| // registered for the CTRL_C_EVENT from Dart code. | |
| SetConsoleCtrlHandler(SignalHandler, TRUE); | |
| // Set both the input and output code pages to UTF8. | |
| const int output_cp = GetConsoleOutputCP(); | |
| const int input_cp = GetConsoleCP(); | |
| if (output_cp != CP_UTF8) { | |
| SetConsoleOutputCP(CP_UTF8); | |
| saved_output_cp_ = output_cp; | |
| } | |
| if (input_cp != CP_UTF8) { | |
| SetConsoleCP(CP_UTF8); | |
| saved_input_cp_ = input_cp; | |
| } | |
| // Try to set the bits for ANSI support, but swallow any failures. | |
| saved_stdout_mode_ = | |
| ModifyMode(STD_OUTPUT_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING); | |
| saved_stderr_mode_ = | |
| ModifyMode(STD_ERROR_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING); | |
| saved_stdin_mode_ = ModifyMode(STD_INPUT_HANDLE, 0); | |
| // TODO(28984): Due to issue #29104, we cannot set | |
| // ENABLE_VIRTUAL_TERMINAL_INPUT here, as it causes ENABLE_PROCESSED_INPUT | |
| // to be ignored. | |
| } | |
| static void Cleanup() { | |
| // STD_OUTPUT_HANDLE, may have been closed or redirected. Therefore, we | |
| // explicitly open the CONOUT$, CONERR$ and CONIN$ devices, so that we can | |
| // be sure that we are really restoring the console to its original state. | |
| if (saved_stdout_mode_ != kInvalidFlag) { | |
| CleanupDevices("CONOUT$", STD_OUTPUT_HANDLE, saved_stdout_mode_); | |
| saved_stdout_mode_ = kInvalidFlag; | |
| } | |
| if (saved_stderr_mode_ != kInvalidFlag) { | |
| CleanupDevices("CONERR$", STD_ERROR_HANDLE, saved_stderr_mode_); | |
| } | |
| if (saved_stdin_mode_ != kInvalidFlag) { | |
| CleanupDevices("CONIN$", STD_INPUT_HANDLE, saved_stdin_mode_); | |
| } | |
| if (saved_output_cp_ != kInvalidFlag) { | |
| SetConsoleOutputCP(saved_output_cp_); | |
| saved_output_cp_ = kInvalidFlag; | |
| } | |
| if (saved_input_cp_ != kInvalidFlag) { | |
| SetConsoleCP(saved_input_cp_); | |
| saved_input_cp_ = kInvalidFlag; | |
| } | |
| } | |
| private: | |
| static int saved_output_cp_; | |
| static int saved_input_cp_; | |
| static DWORD saved_stdout_mode_; | |
| static DWORD saved_stderr_mode_; | |
| static DWORD saved_stdin_mode_; | |
| static BOOL WINAPI SignalHandler(DWORD signal) { | |
| if (signal == CTRL_C_EVENT) { | |
| Cleanup(); | |
| } | |
| return FALSE; | |
| } | |
| static DWORD ModifyMode(DWORD handle, DWORD flags) { | |
| HANDLE h = GetStdHandle(handle); | |
| DWORD mode; | |
| DWORD old_mode = kInvalidFlag; | |
| /// GetConsoleMode fails if this instance of the VM isn't attached to a | |
| /// console. In that case, we'll just return kInvalidFlag and won't try | |
| /// to reset the state when we cleanup. | |
| if ((h != INVALID_HANDLE_VALUE) && GetConsoleMode(h, &mode)) { | |
| old_mode = mode; | |
| // No reason to restore the mode on exit if it was already desirable. | |
| if ((mode & flags) == flags) { | |
| return kInvalidFlag; | |
| } | |
| if (flags != 0) { | |
| const DWORD request = mode | flags; | |
| SetConsoleMode(h, request); | |
| } | |
| } | |
| return old_mode; | |
| } | |
| static void CleanupDevices(const char* device, | |
| DWORD handle, | |
| DWORD orig_flags) { | |
| const intptr_t kWideBufLen = 64; | |
| wchar_t widebuf[kWideBufLen]; | |
| int result = | |
| MultiByteToWideChar(CP_UTF8, 0, device, -1, widebuf, kWideBufLen); | |
| ASSERT(result != 0); | |
| HANDLE h = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, | |
| FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); | |
| if (h != INVALID_HANDLE_VALUE) { | |
| SetStdHandle(STD_OUTPUT_HANDLE, h); | |
| if (orig_flags != kInvalidFlag) { | |
| SetConsoleMode(h, orig_flags); | |
| } | |
| } | |
| } | |
| DISALLOW_ALLOCATION(); | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(ConsoleWin); | |
| }; | |
| int ConsoleWin::saved_output_cp_ = ConsoleWin::kInvalidFlag; | |
| int ConsoleWin::saved_input_cp_ = ConsoleWin::kInvalidFlag; | |
| DWORD ConsoleWin::saved_stdout_mode_ = ConsoleWin::kInvalidFlag; | |
| DWORD ConsoleWin::saved_stderr_mode_ = ConsoleWin::kInvalidFlag; | |
| DWORD ConsoleWin::saved_stdin_mode_ = ConsoleWin::kInvalidFlag; | |
| void Console::SaveConfig() { | |
| ConsoleWin::Initialize(); | |
| } | |
| void Console::RestoreConfig() { | |
| ConsoleWin::Cleanup(); | |
| } | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // defined(DART_HOST_OS_WINDOWS) |