Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass through soft fonts over conpty #13965

Merged
3 commits merged into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/renderer/vt/Xterm256Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IRenderData*> pData,
const bool /*usingSoftFont*/,
const bool usingSoftFont,
const bool isSettingDefaultBrushes) noexcept
{
RETURN_HR_IF(S_FALSE, _passthrough && isSettingDefaultBrushes);
Expand All @@ -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);
}
Expand Down
13 changes: 11 additions & 2 deletions src/renderer/vt/paint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions src/renderer/vt/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/vt/vtrenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ namespace Microsoft::Console::Render

bool _usingLineRenditions;
bool _stopUsingLineRenditions;
bool _usingSoftFont;
TextAttribute _lastTextAttributes;

std::function<void(bool)> _pfnSetLookingForDSR;
Expand Down Expand Up @@ -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;

Expand Down
60 changes: 52 additions & 8 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
Expand Down Expand Up @@ -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:
// - <none>
// 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.
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ namespace Microsoft::Console::VirtualTerminal
void _ReportSGRSetting() const;
void _ReportDECSTBMSetting();

StringHandler _CreateDrcsPassthroughHandler(const DispatchTypes::DrcsCharsetSize charsetSize);
StringHandler _CreatePassthroughHandler();

std::vector<bool> _tabStopColumns;
Expand Down