Skip to content

Commit

Permalink
Make the DOCS sequence set both input and output code pages.
Browse files Browse the repository at this point in the history
  • Loading branch information
j4james committed Sep 19, 2024
1 parent a4067fc commit 1118f54
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 70 deletions.
6 changes: 4 additions & 2 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,10 @@ class Microsoft::Terminal::Core::Terminal final :
void SetWindowTitle(const std::wstring_view title) override;
CursorType GetUserDefaultCursorStyle() const noexcept override;
bool ResizeWindow(const til::CoordType width, const til::CoordType height) override;
void SetConsoleOutputCP(const unsigned int codepage) noexcept override;
unsigned int GetConsoleOutputCP() const noexcept override;
void SetCodePage(const unsigned int codepage) noexcept override;
void ResetCodePage() noexcept override;
unsigned int GetOutputCodePage() const noexcept override;
unsigned int GetInputCodePage() const noexcept override;
void CopyToClipboard(wil::zwstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
void SetWorkingDirectory(std::wstring_view uri) override;
Expand Down
19 changes: 15 additions & 4 deletions src/cascadia/TerminalCore/TerminalApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,25 @@ bool Terminal::ResizeWindow(const til::CoordType width, const til::CoordType hei
return false;
}

void Terminal::SetConsoleOutputCP(const unsigned int /*codepage*/) noexcept
void Terminal::SetCodePage(const unsigned int /*codepage*/) noexcept
{
// TODO: This will be needed to support 8-bit charsets and DOCS sequences.
// Code pages are dealt with in ConHost, so this isn't needed.
}

unsigned int Terminal::GetConsoleOutputCP() const noexcept
void Terminal::ResetCodePage() noexcept
{
// TODO: See SetConsoleOutputCP above.
// There is nothing to reset, since the code page never changes.
}

unsigned int Terminal::GetOutputCodePage() const noexcept
{
// See above. The code page is always UTF-8.
return CP_UTF8;
}

unsigned int Terminal::GetInputCodePage() const noexcept
{
// See above. The code page is always UTF-8.
return CP_UTF8;
}

Expand Down
45 changes: 25 additions & 20 deletions src/host/getset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,23 +1128,16 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
CATCH_RETURN();
}

[[nodiscard]] HRESULT DoSrvSetConsoleOutputCodePage(const unsigned int codepage)
void DoSrvSetConsoleOutputCodePage(const unsigned int codepage)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

// Return if it's not known as a valid codepage ID.
RETURN_HR_IF(E_INVALIDARG, !(IsValidCodePage(codepage)));

// Do nothing if no change.
if (gci.OutputCP != codepage)
{
// Set new code page
gci.OutputCP = codepage;

SetConsoleCPInfo(TRUE);
}

return S_OK;
}

// Routine Description:
Expand All @@ -1157,13 +1150,32 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
{
try
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
return DoSrvSetConsoleOutputCodePage(codepage);
// Return if it's not known as a valid codepage ID.
RETURN_HR_IF(E_INVALIDARG, !(IsValidCodePage(codepage)));
DoSrvSetConsoleOutputCodePage(codepage);
// Setting the code page via the API also updates the default value.
// This is how the initial code page is set to UTF-8 in a WSL shell.
gci.DefaultOutputCP = codepage;
return S_OK;
}
CATCH_RETURN();
}

void DoSrvSetConsoleInputCodePage(const unsigned int codepage)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// Do nothing if no change.
if (gci.CP != codepage)
{
// Set new code page
gci.CP = codepage;
SetConsoleCPInfo(FALSE);
}
}

// Routine Description:
// - Sets the codepage used for translating text when calling A versions of functions affecting the input buffer.
// Arguments:
Expand All @@ -1177,19 +1189,12 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });

// Return if it's not known as a valid codepage ID.
RETURN_HR_IF(E_INVALIDARG, !(IsValidCodePage(codepage)));

// Do nothing if no change.
if (gci.CP != codepage)
{
// Set new code page
gci.CP = codepage;

SetConsoleCPInfo(FALSE);
}

DoSrvSetConsoleInputCodePage(codepage);
// Setting the code page via the API also updates the default value.
// This is how the initial code page is set to UTF-8 in a WSL shell.
gci.DefaultCP = codepage;
return S_OK;
}
CATCH_RETURN();
Expand Down
3 changes: 2 additions & 1 deletion src/host/getset.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Revision History:
#include "../inc/conattrs.hpp"
class SCREEN_INFORMATION;

[[nodiscard]] HRESULT DoSrvSetConsoleOutputCodePage(const unsigned int codepage);
void DoSrvSetConsoleOutputCodePage(const unsigned int codepage);
void DoSrvSetConsoleInputCodePage(const unsigned int codepage);
2 changes: 2 additions & 0 deletions src/host/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ using namespace Microsoft::Console::Interactivity;
// codepage by console.cpl or shell32. The default codepage is OEMCP.
gci.CP = gci.GetCodePage();
gci.OutputCP = gci.GetCodePage();
gci.DefaultCP = gci.GetCodePage();
gci.DefaultOutputCP = gci.GetCodePage();

gci.Flags |= CONSOLE_USE_PRIVATE_FLAGS;

Expand Down
42 changes: 35 additions & 7 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,27 +221,55 @@ void ConhostInternalGetSet::ShowWindow(bool showOrHide)
}

// Routine Description:
// - Connects the SetConsoleOutputCP API call directly into our Driver Message servicing call inside Conhost.exe
// - Set the code page used for translating text when calling A versions of I/O functions.
// Arguments:
// - codepage - the new output codepage of the console.
// - codepage - the new code page of the console.
// Return Value:
// - <none>
void ConhostInternalGetSet::SetConsoleOutputCP(const unsigned int codepage)
void ConhostInternalGetSet::SetCodePage(const unsigned int codepage)
{
THROW_IF_FAILED(DoSrvSetConsoleOutputCodePage(codepage));
if (IsValidCodePage(codepage))
{
DoSrvSetConsoleOutputCodePage(codepage);
DoSrvSetConsoleInputCodePage(codepage);
}
}

// Routine Description:
// - Reset the code pages to their default values.
// Arguments:
// - <none>
// Return Value:
// - <none>
void ConhostInternalGetSet::ResetCodePage()
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
DoSrvSetConsoleOutputCodePage(gci.DefaultOutputCP);
DoSrvSetConsoleInputCodePage(gci.DefaultCP);
}

// Routine Description:
// - Gets the codepage used for translating text when calling A versions of functions affecting the output buffer.
// - Gets the code page used for translating text when calling A versions of output functions.
// Arguments:
// - <none>
// Return Value:
// - the outputCP of the console.
unsigned int ConhostInternalGetSet::GetConsoleOutputCP() const
// - the output code page of the console.
unsigned int ConhostInternalGetSet::GetOutputCodePage() const
{
return ServiceLocator::LocateGlobals().getConsoleInformation().OutputCP;
}

// Routine Description:
// - Gets the code page used for translating text when calling A versions of input functions.
// Arguments:
// - <none>
// Return Value:
// - the input code page of the console.
unsigned int ConhostInternalGetSet::GetInputCodePage() const
{
return ServiceLocator::LocateGlobals().getConsoleInformation().CP;
}

// Routine Description:
// - Copies the given content to the clipboard.
// Arguments:
Expand Down
6 changes: 4 additions & 2 deletions src/host/outputStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::

bool ResizeWindow(const til::CoordType width, const til::CoordType height) override;

void SetConsoleOutputCP(const unsigned int codepage) override;
unsigned int GetConsoleOutputCP() const override;
void SetCodePage(const unsigned int codepage) override;
void ResetCodePage() override;
unsigned int GetOutputCodePage() const override;
unsigned int GetInputCodePage() const override;

void CopyToClipboard(const wil::zwstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
Expand Down
3 changes: 3 additions & 0 deletions src/host/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ class CONSOLE_INFORMATION :
// the following fields are used for ansi-unicode translation
UINT CP = 0;
UINT OutputCP = 0;
// the VT RIS sequence uses these default values to reset the code pages
UINT DefaultCP = 0;
UINT DefaultOutputCP = 0;

ULONG CtrlFlags = 0; // indicates outstanding ctrl requests
ULONG LimitingProcessId = 0;
Expand Down
6 changes: 4 additions & 2 deletions src/terminal/adapter/ITerminalApi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ namespace Microsoft::Console::VirtualTerminal

virtual void ShowWindow(bool showOrHide) = 0;

virtual void SetConsoleOutputCP(const unsigned int codepage) = 0;
virtual unsigned int GetConsoleOutputCP() const = 0;
virtual void SetCodePage(const unsigned int codepage) = 0;
virtual void ResetCodePage() = 0;
virtual unsigned int GetOutputCodePage() const = 0;
virtual unsigned int GetInputCodePage() const = 0;

virtual void CopyToClipboard(const wil::zwstring_view content) = 0;
virtual void SetTaskbarProgress(const DispatchTypes::TaskbarState state, const size_t progress) = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/terminal/adapter/InteractDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void InteractDispatch::WriteString(const std::wstring_view string)
{
if (!string.empty())
{
const auto codepage = _api.GetConsoleOutputCP();
const auto codepage = _api.GetOutputCodePage();
InputEventQueue keyEvents;

for (const auto& wch : string)
Expand Down
29 changes: 13 additions & 16 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,7 +1243,7 @@ void AdaptDispatch::FillRectangularArea(const VTParameter ch, const VTInt top, c
const auto charValue = ch.value_or(0) == 0 ? 32 : ch.value();
const auto glChar = (charValue >= 32 && charValue <= 126);
const auto grChar = (charValue >= 160 && charValue <= 255);
const auto unicodeChar = (charValue >= 256 && charValue <= 65535 && _api.GetConsoleOutputCP() == CP_UTF8);
const auto unicodeChar = (charValue >= 256 && charValue <= 65535 && _api.GetOutputCodePage() == CP_UTF8);
if (glChar || grChar || unicodeChar)
{
const auto fillChar = _termOutput.TranslateKey(gsl::narrow_cast<wchar_t>(charValue));
Expand Down Expand Up @@ -2780,22 +2780,15 @@ void AdaptDispatch::_InitTabStopsForWidth(const VTInt width)
// - codingSystem - The coding system that will be selected.
void AdaptDispatch::DesignateCodingSystem(const VTID codingSystem)
{
// If we haven't previously saved the initial code page, do so now.
// This will be used to restore the code page in response to a reset.
if (!_initialCodePage.has_value())
{
_initialCodePage = _api.GetConsoleOutputCP();
}

switch (codingSystem)
{
case DispatchTypes::CodingSystem::ISO2022:
_api.SetConsoleOutputCP(28591);
_api.SetCodePage(28591);
AcceptC1Controls(true);
_termOutput.EnableGrTranslation(true);
break;
case DispatchTypes::CodingSystem::UTF8:
_api.SetConsoleOutputCP(CP_UTF8);
_api.SetCodePage(CP_UTF8);
AcceptC1Controls(false);
_termOutput.EnableGrTranslation(false);
break;
Expand Down Expand Up @@ -2874,7 +2867,14 @@ void AdaptDispatch::AcceptC1Controls(const bool enabled)
// - enabled - true to send C1 controls, false to send escape sequences.
void AdaptDispatch::SendC1Controls(const bool enabled)
{
_terminalInput.SetInputMode(TerminalInput::Mode::SendC1, enabled);
// If this is an attempt to enable C1 controls, the input code page must be
// one of the DOCS choices (UTF-8 or ISO-8859-1), otherwise there's a risk
// that those controls won't have a valid encoding.
const auto codepage = _api.GetInputCodePage();
if (enabled == false || codepage == CP_UTF8 || codepage == 28591)
{
_terminalInput.SetInputMode(TerminalInput::Mode::SendC1, enabled);
}
}

//Routine Description:
Expand Down Expand Up @@ -3009,11 +3009,8 @@ void AdaptDispatch::HardReset()

// Completely reset the TerminalOutput state.
_termOutput = {};
if (_initialCodePage.has_value())
{
// Restore initial code page if previously changed by a DOCS sequence.
_api.SetConsoleOutputCP(_initialCodePage.value());
}
// Reset the code page to the default value.
_api.ResetCodePage();
// Disable parsing and sending of C1 control codes.
AcceptC1Controls(false);
SendC1Controls(false);
Expand Down
1 change: 0 additions & 1 deletion src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ namespace Microsoft::Console::VirtualTerminal
std::shared_ptr<SixelParser> _sixelParser;
std::unique_ptr<FontBuffer> _fontBuffer;
std::shared_ptr<MacroBuffer> _macroBuffer;
std::optional<unsigned int> _initialCodePage;

// We have two instances of the saved cursor state, because we need
// one for the main buffer (at index 0), and another for the alt buffer
Expand Down
38 changes: 24 additions & 14 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,28 @@ class TestGetSet final : public ITerminalApi
return true;
}

void SetConsoleOutputCP(const unsigned int codepage) override
void SetCodePage(const unsigned int codepage) override
{
Log::Comment(L"SetConsoleOutputCP MOCK called...");
THROW_HR_IF(E_FAIL, !_setConsoleOutputCPResult);
VERIFY_ARE_EQUAL(_expectedOutputCP, codepage);
Log::Comment(L"SetCodePage MOCK called...");
THROW_HR_IF(E_FAIL, !_setCodePageResult);
VERIFY_ARE_EQUAL(_expectedCodePage, codepage);
}

unsigned int GetConsoleOutputCP() const override
void ResetCodePage() override
{
Log::Comment(L"GetConsoleOutputCP MOCK called...");
return _expectedOutputCP;
Log::Comment(L"ResetCodePage MOCK called...");
}

unsigned int GetOutputCodePage() const override
{
Log::Comment(L"GetOutputCodePage MOCK called...");
return _expectedCodePage;
}

unsigned int GetInputCodePage() const override
{
Log::Comment(L"GetInputCodePage MOCK called...");
return _expectedCodePage;
}

void CopyToClipboard(const wil::zwstring_view /*content*/)
Expand Down Expand Up @@ -367,7 +378,7 @@ class TestGetSet final : public ITerminalApi
til::point _expectedCursorPos;

TextAttribute _expectedAttribute = {};
unsigned int _expectedOutputCP = 0;
unsigned int _expectedCodePage = 0;
bool _isPty = false;

bool _returnResponseResult = false;
Expand All @@ -376,8 +387,7 @@ class TestGetSet final : public ITerminalApi

bool _setWindowTitleResult = false;
std::wstring_view _expectedWindowTitle{};
bool _setConsoleOutputCPResult = false;
bool _getConsoleOutputCPResult = false;
bool _setCodePageResult = false;
bool _expectedShowWindow = false;

std::wstring _expectedMenuJson{};
Expand Down Expand Up @@ -3529,15 +3539,15 @@ class AdapterTest

Log::Comment(L"3. Designate ISO-2022 coding system");
// Code page should be set to ISO-8859-1 and C1 parsing enabled
_testGetSet->_setConsoleOutputCPResult = true;
_testGetSet->_expectedOutputCP = 28591;
_testGetSet->_setCodePageResult = true;
_testGetSet->_expectedCodePage = 28591;
_pDispatch->DesignateCodingSystem(DispatchTypes::CodingSystem::ISO2022);
VERIFY_IS_TRUE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1));

Log::Comment(L"4. Designate UTF-8 coding system");
// Code page should be set to UTF-8 and C1 parsing disabled
_testGetSet->_setConsoleOutputCPResult = true;
_testGetSet->_expectedOutputCP = CP_UTF8;
_testGetSet->_setCodePageResult = true;
_testGetSet->_expectedCodePage = CP_UTF8;
_pDispatch->DesignateCodingSystem(DispatchTypes::CodingSystem::UTF8);
VERIFY_IS_FALSE(_stateMachine->GetParserMode(StateMachine::Mode::AcceptC1));
}
Expand Down

0 comments on commit 1118f54

Please sign in to comment.