mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:40:07 +00:00
[dart:io] Adds Platform.ansiSupported
This is so that flutter_tool can determine whether it can print ANSI codes to the terminal on windows. fixes #28614,#28984 R=asiva@google.com Review-Url: https://codereview.chromium.org/2739683005 .
This commit is contained in:
parent
ae8e6705c0
commit
a8bf498e56
|
@ -92,6 +92,7 @@ namespace bin {
|
|||
V(Platform_Environment, 0) \
|
||||
V(Platform_ExecutableArguments, 0) \
|
||||
V(Platform_GetVersion, 0) \
|
||||
V(Platform_AnsiSupported, 0) \
|
||||
V(Process_Start, 11) \
|
||||
V(Process_Wait, 5) \
|
||||
V(Process_KillPid, 2) \
|
||||
|
|
|
@ -109,6 +109,11 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) {
|
|||
Dart_SetReturnValue(args, Dart_NewStringFromCString(Dart_VersionString()));
|
||||
}
|
||||
|
||||
|
||||
void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) {
|
||||
Dart_SetBooleanReturnValue(args, Platform::AnsiSupported());
|
||||
}
|
||||
|
||||
} // namespace bin
|
||||
} // namespace dart
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ class Platform {
|
|||
static int GetScriptIndex() { return script_index_; }
|
||||
static char** GetArgv() { return argv_; }
|
||||
|
||||
static bool AnsiSupported();
|
||||
|
||||
static DART_NORETURN void Exit(int exit_code);
|
||||
|
||||
private:
|
||||
|
|
|
@ -85,6 +85,11 @@ const char* Platform::ResolveExecutablePath() {
|
|||
}
|
||||
|
||||
|
||||
bool Platform::AnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Platform::Exit(int exit_code) {
|
||||
exit(exit_code);
|
||||
}
|
||||
|
|
|
@ -109,6 +109,11 @@ const char* Platform::ResolveExecutablePath() {
|
|||
}
|
||||
|
||||
|
||||
bool Platform::AnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Platform::Exit(int exit_code) {
|
||||
exit(exit_code);
|
||||
}
|
||||
|
|
|
@ -110,6 +110,11 @@ const char* Platform::ResolveExecutablePath() {
|
|||
}
|
||||
|
||||
|
||||
bool Platform::AnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Platform::Exit(int exit_code) {
|
||||
exit(exit_code);
|
||||
}
|
||||
|
|
|
@ -154,6 +154,11 @@ const char* Platform::ResolveExecutablePath() {
|
|||
}
|
||||
|
||||
|
||||
bool Platform::AnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Platform::Exit(int exit_code) {
|
||||
exit(exit_code);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
native "Platform_ExecutableArguments";
|
||||
@patch static String _version()
|
||||
native "Platform_GetVersion";
|
||||
@patch static bool _ansiSupported()
|
||||
native "Platform_AnsiSupported";
|
||||
|
||||
@patch static String _packageRoot()
|
||||
=> VMLibraryHooks.packageRootString;
|
||||
|
|
|
@ -64,6 +64,12 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) {
|
|||
"Platform is not supported on this platform"));
|
||||
}
|
||||
|
||||
|
||||
void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) {
|
||||
Dart_ThrowException(DartUtils::NewInternalError(
|
||||
"Platform is not supported on this platform"));
|
||||
}
|
||||
|
||||
} // namespace bin
|
||||
} // namespace dart
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ class PlatformWin {
|
|||
static void InitOnce() {
|
||||
platform_win_mutex_ = new Mutex();
|
||||
saved_output_cp_ = -1;
|
||||
saved_input_cp_ = -1;
|
||||
// Set up a no-op handler so that CRT functions return an error instead of
|
||||
// hitting an assertion failure.
|
||||
// See: https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
|
||||
|
@ -61,50 +62,89 @@ class PlatformWin {
|
|||
|
||||
static void SaveAndConfigureConsole() {
|
||||
MutexLocker ml(platform_win_mutex_);
|
||||
// Set both the input and output code pages to UTF8.
|
||||
ASSERT(saved_output_cp_ == -1);
|
||||
ASSERT(saved_input_cp_ == -1);
|
||||
saved_output_cp_ = GetConsoleOutputCP();
|
||||
saved_input_cp_ = GetConsoleCP();
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
|
||||
ansi_supported_ = true;
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if ((out != INVALID_HANDLE_VALUE) &&
|
||||
GetConsoleMode(out, &saved_console_out_mode_)) {
|
||||
const DWORD request =
|
||||
saved_console_out_mode_ | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(out, request);
|
||||
DWORD out_mode;
|
||||
if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) {
|
||||
const DWORD request = out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
ansi_supported_ = ansi_supported_ && SetConsoleMode(out, request);
|
||||
} else {
|
||||
ansi_supported_ = false;
|
||||
}
|
||||
|
||||
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if ((in != INVALID_HANDLE_VALUE) &&
|
||||
GetConsoleMode(in, &saved_console_in_mode_)) {
|
||||
const DWORD request =
|
||||
saved_console_in_mode_ | ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
SetConsoleMode(in, request);
|
||||
DWORD in_mode;
|
||||
if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) {
|
||||
const DWORD request = in_mode | ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
ansi_supported_ = ansi_supported_ && SetConsoleMode(in, request);
|
||||
} else {
|
||||
ansi_supported_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void RestoreConsole() {
|
||||
MutexLocker ml(platform_win_mutex_);
|
||||
|
||||
// STD_OUTPUT_HANDLE and STD_INPUT_HANDLE may have been closed or
|
||||
// redirected. Therefore, we explicitly open the CONOUT$ and CONIN$
|
||||
// devices, so that we can be sure that we are really unsetting
|
||||
// ENABLE_VIRTUAL_TERMINAL_PROCESSING and ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
// respectively.
|
||||
HANDLE out;
|
||||
{
|
||||
Utf8ToWideScope conin("CONOUT$");
|
||||
out = CreateFileW(conin.wide(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
if (out != INVALID_HANDLE_VALUE) {
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, out);
|
||||
}
|
||||
}
|
||||
DWORD out_mode;
|
||||
if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) {
|
||||
DWORD request = out_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
SetConsoleMode(out, request);
|
||||
}
|
||||
|
||||
HANDLE in;
|
||||
{
|
||||
Utf8ToWideScope conin("CONIN$");
|
||||
in = CreateFileW(conin.wide(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
if (in != INVALID_HANDLE_VALUE) {
|
||||
SetStdHandle(STD_INPUT_HANDLE, in);
|
||||
}
|
||||
}
|
||||
DWORD in_mode;
|
||||
if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) {
|
||||
DWORD request = in_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
SetConsoleMode(in, request);
|
||||
}
|
||||
|
||||
if (saved_output_cp_ != -1) {
|
||||
SetConsoleOutputCP(saved_output_cp_);
|
||||
saved_output_cp_ = -1;
|
||||
}
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (out != INVALID_HANDLE_VALUE) {
|
||||
SetConsoleMode(out, saved_console_out_mode_);
|
||||
saved_console_out_mode_ = 0;
|
||||
}
|
||||
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (in != INVALID_HANDLE_VALUE) {
|
||||
SetConsoleMode(in, saved_console_in_mode_);
|
||||
saved_console_in_mode_ = 0;
|
||||
if (saved_input_cp_ != -1) {
|
||||
SetConsoleCP(saved_input_cp_);
|
||||
saved_input_cp_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ansi_supported() { return ansi_supported_; }
|
||||
|
||||
private:
|
||||
static Mutex* platform_win_mutex_;
|
||||
static int saved_output_cp_;
|
||||
static DWORD saved_console_out_mode_;
|
||||
static DWORD saved_console_in_mode_;
|
||||
static int saved_input_cp_;
|
||||
static bool ansi_supported_;
|
||||
|
||||
static void InvalidParameterHandler(const wchar_t* expression,
|
||||
const wchar_t* function,
|
||||
|
@ -120,9 +160,9 @@ class PlatformWin {
|
|||
};
|
||||
|
||||
int PlatformWin::saved_output_cp_ = -1;
|
||||
DWORD PlatformWin::saved_console_out_mode_ = 0;
|
||||
DWORD PlatformWin::saved_console_in_mode_ = 0;
|
||||
int PlatformWin::saved_input_cp_ = -1;
|
||||
Mutex* PlatformWin::platform_win_mutex_ = NULL;
|
||||
bool PlatformWin::ansi_supported_ = false;
|
||||
|
||||
bool Platform::Initialize() {
|
||||
PlatformWin::InitOnce();
|
||||
|
@ -219,6 +259,11 @@ const char* Platform::ResolveExecutablePath() {
|
|||
}
|
||||
|
||||
|
||||
bool Platform::AnsiSupported() {
|
||||
return PlatformWin::ansi_supported();
|
||||
}
|
||||
|
||||
|
||||
void Platform::Exit(int exit_code) {
|
||||
// TODO(zra): Remove once VM shuts down cleanly.
|
||||
::dart::private_flag_windows_run_tls_destructors = false;
|
||||
|
|
|
@ -218,6 +218,10 @@ class _Platform {
|
|||
static String _version() {
|
||||
throw new UnsupportedError("Platform._version");
|
||||
}
|
||||
@patch
|
||||
static _ansiSupported() {
|
||||
throw new UnsupportedError("Platform._ansiSupported");
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
|
|
|
@ -71,6 +71,7 @@ class Platform {
|
|||
static final _operatingSystem = _Platform.operatingSystem;
|
||||
static final _localHostname = _Platform.localHostname;
|
||||
static final _version = _Platform.version;
|
||||
static final _ansiSupported = _Platform.ansiSupported;
|
||||
|
||||
/**
|
||||
* Get the number of processors of the machine.
|
||||
|
@ -124,6 +125,14 @@ class Platform {
|
|||
*/
|
||||
static final bool isFuchsia = (_operatingSystem == "fuchsia");
|
||||
|
||||
/**
|
||||
* When stdio is connected to a terminal, whether ANSI codes are supported.
|
||||
*
|
||||
* This value is hard-coded to `true`, except on Windows where only more
|
||||
* recent versions of Windows 10 support the codes.
|
||||
*/
|
||||
static final bool ansiSupported = _ansiSupported;
|
||||
|
||||
/**
|
||||
* Get the environment for this process.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ class _Platform {
|
|||
external static _localHostname();
|
||||
external static _executable();
|
||||
external static _resolvedExecutable();
|
||||
external static bool _ansiSupported();
|
||||
|
||||
/**
|
||||
* Retrieve the entries of the process environment.
|
||||
|
@ -45,6 +46,7 @@ class _Platform {
|
|||
static int get numberOfProcessors => _numberOfProcessors();
|
||||
static String get pathSeparator => _pathSeparator();
|
||||
static String get operatingSystem => _operatingSystem();
|
||||
static bool get ansiSupported => _ansiSupported();
|
||||
static Uri script;
|
||||
|
||||
static String get localHostname {
|
||||
|
|
23
tests/standalone/io/ansi_supported_test.dart
Normal file
23
tests/standalone/io/ansi_supported_test.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
main() {
|
||||
try {
|
||||
Platform.ansiSupported;
|
||||
} catch (e, s) {
|
||||
Expect.fail("Platform.ansiSupported threw: $e\n$s\n");
|
||||
}
|
||||
Expect.isNotNull(Platform.ansiSupported);
|
||||
Expect.isTrue(Platform.ansiSupported is bool);
|
||||
if (stdout.hasTerminal && Platform.ansiSupported) {
|
||||
stdout.writeln('\x1b[31mThis text has a red foreground using SGR.31.');
|
||||
stdout.writeln('\x1b[39mThis text has restored the foreground color.');
|
||||
} else {
|
||||
stdout.writeln('ANSI codes not supported on this platform');
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue