| 1 | // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | 
|---|
| 2 | // for details. All rights reserved. Use of this source code is governed by a | 
|---|
| 3 | // BSD-style license that can be found in the LICENSE file. | 
|---|
| 4 |  | 
|---|
| 5 | #include "platform/globals.h" | 
|---|
| 6 | #if defined(HOST_OS_WINDOWS) | 
|---|
| 7 | #include "bin/console.h" | 
|---|
| 8 |  | 
|---|
| 9 | #include "bin/file.h" | 
|---|
| 10 | #include "bin/lockers.h" | 
|---|
| 11 | #include "bin/platform.h" | 
|---|
| 12 | #include "bin/utils.h" | 
|---|
| 13 | #include "bin/utils_win.h" | 
|---|
| 14 |  | 
|---|
| 15 | // These are not always defined in the header files. See: | 
|---|
| 16 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx | 
|---|
| 17 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | 
|---|
| 18 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 | 
|---|
| 19 | #endif | 
|---|
| 20 |  | 
|---|
| 21 | namespace dart { | 
|---|
| 22 | namespace bin { | 
|---|
| 23 |  | 
|---|
| 24 | class ConsoleWin { | 
|---|
| 25 | public: | 
|---|
| 26 | static const int kInvalidFlag = -1; | 
|---|
| 27 |  | 
|---|
| 28 | static void Initialize() { | 
|---|
| 29 | saved_output_cp_ = kInvalidFlag; | 
|---|
| 30 | saved_input_cp_ = kInvalidFlag; | 
|---|
| 31 | // Set up a signal handler that restores the console state on a | 
|---|
| 32 | // CTRL_C_EVENT signal. This will only run when there is no signal handler | 
|---|
| 33 | // registered for the CTRL_C_EVENT from Dart code. | 
|---|
| 34 | SetConsoleCtrlHandler(SignalHandler, TRUE); | 
|---|
| 35 |  | 
|---|
| 36 | // Set both the input and output code pages to UTF8. | 
|---|
| 37 | const int output_cp = GetConsoleOutputCP(); | 
|---|
| 38 | const int input_cp = GetConsoleCP(); | 
|---|
| 39 | if (output_cp != CP_UTF8) { | 
|---|
| 40 | SetConsoleOutputCP(CP_UTF8); | 
|---|
| 41 | saved_output_cp_ = output_cp; | 
|---|
| 42 | } | 
|---|
| 43 | if (input_cp != CP_UTF8) { | 
|---|
| 44 | SetConsoleCP(CP_UTF8); | 
|---|
| 45 | saved_input_cp_ = input_cp; | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 | // Try to set the bits for ANSI support, but swallow any failures. | 
|---|
| 49 | saved_stdout_mode_ = | 
|---|
| 50 | ModifyMode(STD_OUTPUT_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING); | 
|---|
| 51 | saved_stderr_mode_ = | 
|---|
| 52 | ModifyMode(STD_ERROR_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING); | 
|---|
| 53 | saved_stdin_mode_ = ModifyMode(STD_INPUT_HANDLE, 0); | 
|---|
| 54 |  | 
|---|
| 55 | // TODO(28984): Due to issue #29104, we cannot set | 
|---|
| 56 | // ENABLE_VIRTUAL_TERMINAL_INPUT here, as it causes ENABLE_PROCESSED_INPUT | 
|---|
| 57 | // to be ignored. | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | static void Cleanup() { | 
|---|
| 61 | // STD_OUTPUT_HANDLE, may have been closed or redirected. Therefore, we | 
|---|
| 62 | // explicitly open the CONOUT$, CONERR$ and CONIN$ devices, so that we can | 
|---|
| 63 | // be sure that we are really restoring the console to its original state. | 
|---|
| 64 | if (saved_stdout_mode_ != kInvalidFlag) { | 
|---|
| 65 | CleanupDevices( "CONOUT$", STD_OUTPUT_HANDLE, saved_stdout_mode_); | 
|---|
| 66 | saved_stdout_mode_ = kInvalidFlag; | 
|---|
| 67 | } | 
|---|
| 68 | if (saved_stderr_mode_ != kInvalidFlag) { | 
|---|
| 69 | CleanupDevices( "CONERR$", STD_ERROR_HANDLE, saved_stderr_mode_); | 
|---|
| 70 | } | 
|---|
| 71 | if (saved_stdin_mode_ != kInvalidFlag) { | 
|---|
| 72 | CleanupDevices( "CONIN$", STD_INPUT_HANDLE, saved_stdin_mode_); | 
|---|
| 73 | } | 
|---|
| 74 | if (saved_output_cp_ != kInvalidFlag) { | 
|---|
| 75 | SetConsoleOutputCP(saved_output_cp_); | 
|---|
| 76 | saved_output_cp_ = kInvalidFlag; | 
|---|
| 77 | } | 
|---|
| 78 | if (saved_input_cp_ != kInvalidFlag) { | 
|---|
| 79 | SetConsoleCP(saved_input_cp_); | 
|---|
| 80 | saved_input_cp_ = kInvalidFlag; | 
|---|
| 81 | } | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 | private: | 
|---|
| 85 | static int saved_output_cp_; | 
|---|
| 86 | static int saved_input_cp_; | 
|---|
| 87 | static DWORD saved_stdout_mode_; | 
|---|
| 88 | static DWORD saved_stderr_mode_; | 
|---|
| 89 | static DWORD saved_stdin_mode_; | 
|---|
| 90 |  | 
|---|
| 91 | static BOOL WINAPI SignalHandler(DWORD signal) { | 
|---|
| 92 | if (signal == CTRL_C_EVENT) { | 
|---|
| 93 | Cleanup(); | 
|---|
| 94 | } | 
|---|
| 95 | return FALSE; | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | static DWORD ModifyMode(DWORD handle, DWORD flags) { | 
|---|
| 99 | HANDLE h = GetStdHandle(handle); | 
|---|
| 100 | DWORD mode; | 
|---|
| 101 | DWORD old_mode = kInvalidFlag; | 
|---|
| 102 |  | 
|---|
| 103 | /// GetConsoleMode fails if this instance of the VM isn't attached to a | 
|---|
| 104 | /// console. In that case, we'll just return kInvalidFlag and won't try | 
|---|
| 105 | /// to reset the state when we cleanup. | 
|---|
| 106 | if ((h != INVALID_HANDLE_VALUE) && GetConsoleMode(h, &mode)) { | 
|---|
| 107 | old_mode = mode; | 
|---|
| 108 | if (flags != 0) { | 
|---|
| 109 | const DWORD request = mode | flags; | 
|---|
| 110 | SetConsoleMode(h, request); | 
|---|
| 111 | } | 
|---|
| 112 | } | 
|---|
| 113 | return old_mode; | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | static void CleanupDevices(const char* device, | 
|---|
| 117 | DWORD handle, | 
|---|
| 118 | DWORD orig_flags) { | 
|---|
| 119 | const intptr_t kWideBufLen = 64; | 
|---|
| 120 | wchar_t widebuf[kWideBufLen]; | 
|---|
| 121 | int result = | 
|---|
| 122 | MultiByteToWideChar(CP_UTF8, 0, device, -1, widebuf, kWideBufLen); | 
|---|
| 123 | ASSERT(result != 0); | 
|---|
| 124 | HANDLE h = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, | 
|---|
| 125 | FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); | 
|---|
| 126 | if (h != INVALID_HANDLE_VALUE) { | 
|---|
| 127 | SetStdHandle(STD_OUTPUT_HANDLE, h); | 
|---|
| 128 | if (orig_flags != kInvalidFlag) { | 
|---|
| 129 | SetConsoleMode(h, orig_flags); | 
|---|
| 130 | } | 
|---|
| 131 | } | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | DISALLOW_ALLOCATION(); | 
|---|
| 135 | DISALLOW_IMPLICIT_CONSTRUCTORS(ConsoleWin); | 
|---|
| 136 | }; | 
|---|
| 137 |  | 
|---|
| 138 | int ConsoleWin::saved_output_cp_ = ConsoleWin::kInvalidFlag; | 
|---|
| 139 | int ConsoleWin::saved_input_cp_ = ConsoleWin::kInvalidFlag; | 
|---|
| 140 | DWORD ConsoleWin::saved_stdout_mode_ = ConsoleWin::kInvalidFlag; | 
|---|
| 141 | DWORD ConsoleWin::saved_stderr_mode_ = ConsoleWin::kInvalidFlag; | 
|---|
| 142 | DWORD ConsoleWin::saved_stdin_mode_ = ConsoleWin::kInvalidFlag; | 
|---|
| 143 |  | 
|---|
| 144 | void Console::SaveConfig() { | 
|---|
| 145 | ConsoleWin::Initialize(); | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | void Console::RestoreConfig() { | 
|---|
| 149 | ConsoleWin::Cleanup(); | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | }  // namespace bin | 
|---|
| 153 | }  // namespace dart | 
|---|
| 154 |  | 
|---|
| 155 | #endif  // defined(HOST_OS_WINDOWS) | 
|---|
| 156 |  | 
|---|