diff --git a/src/renderer/vt/Xterm256Engine.cpp b/src/renderer/vt/Xterm256Engine.cpp index 33a0e92ec59..dc506322ccf 100644 --- a/src/renderer/vt/Xterm256Engine.cpp +++ b/src/renderer/vt/Xterm256Engine.cpp @@ -29,7 +29,7 @@ Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe, [[nodiscard]] HRESULT Xterm256Engine::UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& /*renderSettings*/, const gsl::not_null pData, - const bool /*usingSoftFont*/, + const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept { RETURN_HR_IF(S_FALSE, _passthrough && isSettingDefaultBrushes); @@ -38,6 +38,17 @@ Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe, RETURN_IF_FAILED(_UpdateHyperlinkAttr(textAttributes, pData)); + // If we're using a soft font, it should have already been mapped into the + // G1 table, so we just need to switch between G0 and G1 when turning the + // soft font on and off. We don't want to do this when setting the default + // brushes, though, because that could result in an unnecessary G0 switch + // at the start of every frame. + if (usingSoftFont != _usingSoftFont && !isSettingDefaultBrushes) + { + RETURN_IF_FAILED(_Write(usingSoftFont ? "\x0E" : "\x0F")); + _usingSoftFont = usingSoftFont; + } + // Only do extended attributes in xterm-256color, as to not break telnet.exe. return _UpdateExtendedAttrs(textAttributes); } diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp index db824f2cb24..228ef858016 100644 --- a/src/renderer/vt/paint.cpp +++ b/src/renderer/vt/paint.cpp @@ -535,8 +535,17 @@ using namespace Microsoft::Console::Types; // Move the cursor to the start of this run. RETURN_IF_FAILED(_MoveCursor(coord)); - // Write the actual text string - RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8({ _bufferLine.data(), cchActual })); + // Write the actual text string. If we're using a soft font, the character + // set should have already been selected, so we just need to map our internal + // representation back to ASCII (handled by the _WriteTerminalDrcs method). + if (_usingSoftFont) [[unlikely]] + { + RETURN_IF_FAILED(VtEngine::_WriteTerminalDrcs({ _bufferLine.data(), cchActual })); + } + else + { + RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8({ _bufferLine.data(), cchActual })); + } // GH#4415, GH#5181 // If the renderer told us that this was a wrapped line, then mark diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index 872f419a004..e292e242104 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -31,6 +31,7 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, _hFile(std::move(pipe)), _usingLineRenditions(false), _stopUsingLineRenditions(false), + _usingSoftFont(false), _lastTextAttributes(INVALID_COLOR, INVALID_COLOR), _lastViewport(initialViewport), _pool(til::pmr::get_default_resource()), @@ -209,6 +210,30 @@ CATCH_RETURN(); return _Write(needed); } +// Method Description: +// - Writes a wstring to the tty when the characters are from the DRCS soft font. +// It is assumed that the character set has already been designated in the +// client terminal, so we just need to re-map our internal representation +// of the characters into ASCII. +// Arguments: +// - wstr - wstring of text to be written +// Return Value: +// - S_OK or suitable HRESULT error from writing pipe. +[[nodiscard]] HRESULT VtEngine::_WriteTerminalDrcs(const std::wstring_view wstr) noexcept +{ + std::string needed; + needed.reserve(wstr.size()); + + for (const auto& wch : wstr) + { + // Our DRCS characters use the range U+EF20 to U+EF7F from the Unicode + // Private Use Area. To map them back to ASCII we just mask with 7F. + needed.push_back(wch & 0x7F); + } + + return _Write(needed); +} + // Method Description: // - This method will update the active font on the current device context // Does nothing for vt, the font is handed by the terminal. diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index aee2428bb1d..18ac4e4cc8f 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -100,6 +100,7 @@ namespace Microsoft::Console::Render bool _usingLineRenditions; bool _stopUsingLineRenditions; + bool _usingSoftFont; TextAttribute _lastTextAttributes; std::function _pfnSetLookingForDSR; @@ -225,6 +226,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _WriteTerminalUtf8(const std::wstring_view str) noexcept; [[nodiscard]] HRESULT _WriteTerminalAscii(const std::wstring_view str) noexcept; + [[nodiscard]] HRESULT _WriteTerminalDrcs(const std::wstring_view str) noexcept; [[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 400096db064..eb6546921a4 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2585,14 +2585,6 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const VTInt fontNumber, const VTParameter cellHeight, const DispatchTypes::DrcsCharsetSize charsetSize) { - // If we're a conpty, we're just going to ignore the operation for now. - // There's no point in trying to pass it through without also being able - // to pass through the character set designations. - if (_api.IsConsolePty()) - { - return nullptr; - } - // The font buffer is created on demand. if (!_fontBuffer) { @@ -2612,7 +2604,19 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const VTInt fontNumber, return nullptr; } + // If we're a conpty, we create a special passthrough handler that will + // forward the DECDLD sequence to the conpty terminal with a hardcoded ID. + // That ID is also pre-mapped into the G1 table, so the VT engine can just + // switch to G1 when it needs to output any DRCS characters. But note that + // we still need to process the DECDLD sequence locally, so the character + // set translation is correctly handled on the host side. + const auto conptyPassthrough = _api.IsConsolePty() ? _CreateDrcsPassthroughHandler(charsetSize) : nullptr; + return [=](const auto ch) { + if (conptyPassthrough) + { + conptyPassthrough(ch); + } // We pass the data string straight through to the font buffer class // until we receive an ESC, indicating the end of the string. At that // point we can finalize the buffer, and if valid, update the renderer @@ -2643,6 +2647,46 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const VTInt fontNumber, }; } +// Routine Description: +// - Helper method to create a string handler that can be used to pass through +// DECDLD sequences when in conpty mode. This patches the original sequence +// with a hardcoded character set ID, and pre-maps that ID into the G1 table. +// Arguments: +// - +// Return value: +// - a function to receive the data or nullptr if the initial flush fails +ITermDispatch::StringHandler AdaptDispatch::_CreateDrcsPassthroughHandler(const DispatchTypes::DrcsCharsetSize charsetSize) +{ + const auto defaultPassthrough = _CreatePassthroughHandler(); + if (defaultPassthrough) + { + auto& engine = _api.GetStateMachine().Engine(); + return [=, &engine, gotId = false](const auto ch) mutable { + // The character set ID is contained in the first characters of the + // sequence, so we just ignore that initial content until we receive + // a "final" character (i.e. in range 30 to 7E). At that point we + // pass through a hardcoded ID of "@". + if (!gotId) + { + if (ch >= 0x30 && ch <= 0x7E) + { + gotId = true; + defaultPassthrough('@'); + } + } + else if (!defaultPassthrough(ch)) + { + // Once the DECDLD sequence is finished, we also output an SCS + // sequence to map the character set into the G1 table. + const auto charset96 = charsetSize == DispatchTypes::DrcsCharsetSize::Size96; + engine.ActionPassThroughString(charset96 ? L"\033-@" : L"\033)@"); + } + return true; + }; + } + return nullptr; +} + // Method Description: // - DECRSTS - Restores the terminal state from a stream of data previously // saved with a DECRQTSR query. diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 081185a4732..d2c48ae9b9e 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -209,6 +209,7 @@ namespace Microsoft::Console::VirtualTerminal void _ReportSGRSetting() const; void _ReportDECSTBMSetting(); + StringHandler _CreateDrcsPassthroughHandler(const DispatchTypes::DrcsCharsetSize charsetSize); StringHandler _CreatePassthroughHandler(); std::vector _tabStopColumns;