From 530196af81f9981d18888e1326ff37d0bd249d7e Mon Sep 17 00:00:00 2001 From: Martin Ridgers Date: Thu, 18 May 2023 22:37:13 +0200 Subject: [PATCH] Use Windows 10's virtual terminal if available --- clink/terminal/include/terminal/terminal.h | 1 + clink/terminal/src/terminal.cpp | 60 +++++++++++++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/clink/terminal/include/terminal/terminal.h b/clink/terminal/include/terminal/terminal.h index ee9a90ff..324e521c 100644 --- a/clink/terminal/include/terminal/terminal.h +++ b/clink/terminal/include/terminal/terminal.h @@ -12,6 +12,7 @@ struct Terminal { TerminalIn* in; TerminalOut* out; + uintptr_t impl; }; //------------------------------------------------------------------------------ diff --git a/clink/terminal/src/terminal.cpp b/clink/terminal/src/terminal.cpp index a0599ca3..97cf0ff4 100644 --- a/clink/terminal/src/terminal.cpp +++ b/clink/terminal/src/terminal.cpp @@ -6,20 +6,55 @@ #include "ecma48_terminal_out.h" #include "win_screen_buffer.h" #include "win_terminal_in.h" +#include "win_terminal_out.h" #include +//------------------------------------------------------------------------------ +#if defined(PLATFORM_WINDOWS) +struct WinVirtualTerminal +{ + DWORD prev_mode = ~0u; + ScreenBuffer* screen = nullptr; +}; +#endif + //------------------------------------------------------------------------------ Terminal terminal_create(ScreenBuffer* screen) { #if defined(PLATFORM_WINDOWS) - if (screen == nullptr) - screen = new WinScreenBuffer(); // TODO: this leaks. + auto* impl = new WinVirtualTerminal(); + + Terminal terminal; + terminal.in = new WinTerminalIn(); + terminal.out = nullptr; + terminal.impl = uintptr_t(impl); + + // Try and use Win10's VT100 support + DWORD prev_mode; + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (GetConsoleMode(handle, &prev_mode)) + { + if (SetConsoleMode(handle, prev_mode|ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + { + impl->prev_mode = prev_mode; + terminal.out = new WinTerminalOut(); + } + } - return { - new WinTerminalIn(), - new Ecma48TerminalOut(*screen), - }; + // If it wasn't possible to enable VT100 support we'll fallback to Clink's + if (terminal.out == nullptr) + { + if (screen == nullptr) + { + screen = new WinScreenBuffer(); + impl->screen = screen; + } + + terminal.out = new Ecma48TerminalOut(*screen); + } + + return terminal; #else return {}; #endif @@ -30,4 +65,17 @@ void terminal_destroy(const Terminal& terminal) { delete terminal.out; delete terminal.in; + + auto* impl = (WinVirtualTerminal*)(terminal.impl); + + if (impl->prev_mode != ~0u) + { + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleMode(handle, impl->prev_mode); + } + + if (impl->screen != nullptr) + delete impl->screen; + + delete impl; }