diff options
author | Bert Belder <bertbelder@gmail.com> | 2019-03-03 19:07:25 -0800 |
---|---|---|
committer | Bert Belder <bertbelder@gmail.com> | 2019-03-03 19:19:39 -0800 |
commit | 4cb157b85bc1770b79002e36747191d69eb757c0 (patch) | |
tree | 05ddf7d76fc7b711171b0ecf4758e1de6030ebd8 /libdeno/binding.cc | |
parent | 2e9d43391ff977dffe8377be79267119ee1554bc (diff) |
libdeno: fix libdeno.print() unicode output on Windows
Note that emoji are still not supported, due limitations of the
Windows console.
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r-- | libdeno/binding.cc | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc index 064ce2271..7346956fd 100644 --- a/libdeno/binding.cc +++ b/libdeno/binding.cc @@ -2,9 +2,18 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <algorithm> #include <iostream> #include <string> +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <io.h> +#include <windows.h> +#endif // _WIN32 + #include "third_party/v8/include/v8.h" #include "third_party/v8/src/base/logging.h" @@ -93,10 +102,52 @@ void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { DenoIsolate* d = DenoIsolate::FromIsolate(isolate); auto context = d->context_.Get(d->isolate_); v8::HandleScope handle_scope(isolate); - v8::String::Utf8Value str(isolate, args[0]); bool is_err = args.Length() >= 2 ? args[1]->BooleanValue(context).ToChecked() : false; FILE* file = is_err ? stderr : stdout; + +#ifdef _WIN32 + int fd = _fileno(file); + if (fd < 0) return; + + HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); + if (h == INVALID_HANDLE_VALUE) return; + + DWORD mode; + if (GetConsoleMode(h, &mode)) { + // Print to Windows console. Since the Windows API generally doesn't support + // UTF-8 encoded text, we have to use `WriteConsoleW()` which uses UTF-16. + v8::String::Value str(isolate, args[0]); + auto str_len = static_cast<size_t>(str.length()); + auto str_wchars = reinterpret_cast<WCHAR*>(*str); + + // WriteConsoleW has some limit to how many characters can be written at + // once, which is unspecified but low enough to be encountered in practice. + // Therefore we break up the write into chunks of 8kb if necessary. + size_t chunk_start = 0; + while (chunk_start < str_len) { + size_t chunk_end = std::min(chunk_start + 8192, str_len); + + // Do not break in the middle of a surrogate pair. Note that `chunk_end` + // points to the start of the next chunk, so we check whether it contains + // the second half of a surrogate pair (a.k.a. "low surrogate"). + if (chunk_end < str_len && str_wchars[chunk_end] >= 0xdc00 && + str_wchars[chunk_end] <= 0xdfff) { + --chunk_end; + } + + // Write to the console. + DWORD chunk_len = static_cast<DWORD>(chunk_end - chunk_start); + DWORD _; + WriteConsoleW(h, &str_wchars[chunk_start], chunk_len, &_, nullptr); + + chunk_start = chunk_end; + } + return; + } +#endif // _WIN32 + + v8::String::Utf8Value str(isolate, args[0]); fwrite(*str, sizeof(**str), str.length(), file); fflush(file); } |