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 2 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (_usingSoftFont)
if (_usingSoftFont) [[unlikely]]

@lhecker is this the correct usage of that annotation in C++?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that would work. likely/unlikely aren't particularly impactful in MSVC right now, but it does influence the inalienability of functions quite a bit, which can help make our binaries more compact.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just tried this, and it does make a bit of a difference. Both of the _WriteTerminalXXX calls are inline, so there is quite a lot of code in the two branches of that condition. With the unlikely attribute added, part of the first branch gets pulled out of the main body of code, so there is a slightly shorter jump to the else branch.

I don't know if that's really worth the effort, though. I think if you wanted to make a significant difference with these attributes, a good place to put them might be in the RETURN_IF_FAILED macros. That should remove all that diagnostic logging from the main path of execution which is probably quite a chunk of code.

I'm not an optimization expert though, so I'll leave it to you guys to decide.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only saw Leonard's reply after I posted mine. I've applied the suggested change now.

{
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