blob: 03d8fb9f9fb29a17dc5e9e9bb5f02951abe37197 [file] [log] [blame]
// 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)