| // 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) |