Send a CPR request on every unknown sequence (#20009)

This PR is 90% wiring up OOP interfaces.

Closes #19926

## Validation Steps Performed
* Run `github.com/xtermjs/vtc`
* Observed `UnknownSequence` calls under a debugger
This commit is contained in:
Leonard Hecker
2026-03-25 00:00:13 +01:00
committed by GitHub
parent e0400150d0
commit da0446a7d1
17 changed files with 79 additions and 2 deletions

View File

@@ -1522,6 +1522,10 @@ void Terminal::SerializeMainBuffer(HANDLE handle) const
_mainBuffer->SerializeTo(handle);
}
void Terminal::UnknownSequence() noexcept
{
}
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
{
const auto colorSelection = [this](const til::point coordStartInclusive, const til::point coordEndExclusive, const TextAttribute& attr) {

View File

@@ -131,6 +131,7 @@ public:
#pragma region ITerminalApi
// These methods are defined in TerminalApi.cpp
void UnknownSequence() noexcept override;
void ReturnResponse(const std::wstring_view response) override;
bool IsConPTY() const noexcept override;
Microsoft::Console::VirtualTerminal::StateMachine& GetStateMachine() noexcept override;

View File

@@ -24,6 +24,22 @@ ConhostInternalGetSet::ConhostInternalGetSet(_In_ IIoProvider& io) :
{
}
void ConhostInternalGetSet::UnknownSequence() noexcept
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// VT sequences unknown to us may cause the cursor position to change in a way that
// we don't know about. In this case, we need to mark the cursor position as "dirty".
//
// The worst offender is likely PowerShell. It uses VT sequences but also calls
// GetConsoleScreenBufferInfoEx for *every single line of output* (!!!). This prevents
// us from using a more conservative solution (e.g. always fetching the cursor position).
if (gci.IsInVtIoMode())
{
gci.GetActiveOutputBuffer().SetConptyCursorPositionMayBeWrong();
}
}
// - Sends a string response to the input stream of the console.
// - Used by various commands where the program attached would like a reply to one of the commands issued.
// - This will generate two "key presses" (one down, one up) for every character in the string and place them into the head of the console's input stream.

View File

@@ -29,6 +29,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
public:
ConhostInternalGetSet(_In_ Microsoft::Console::IIoProvider& io);
void UnknownSequence() noexcept override;
void ReturnResponse(const std::wstring_view response) override;
bool IsConPTY() const noexcept override;

View File

@@ -35,6 +35,7 @@ public:
#pragma warning(disable : 26432) // suppress rule of 5 violation on interface because tampering with this is fraught with peril
virtual ~ITermDispatch() = 0;
virtual void UnknownSequence() noexcept = 0;
virtual void Print(const wchar_t wchPrintable) = 0;
virtual void PrintString(const std::wstring_view string) = 0;

View File

@@ -37,6 +37,7 @@ namespace Microsoft::Console::VirtualTerminal
ITerminalApi& operator=(const ITerminalApi&) = delete;
ITerminalApi& operator=(ITerminalApi&&) = delete;
virtual void UnknownSequence() noexcept = 0;
virtual void ReturnResponse(const std::wstring_view response) = 0;
struct BufferState

View File

@@ -48,6 +48,11 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettin
{
}
void AdaptDispatch::UnknownSequence() noexcept
{
_api.UnknownSequence();
}
// Routine Description:
// - Translates and displays a single character
// Arguments:
@@ -3607,6 +3612,10 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
_pages.ActivePage().Buffer().StartCommand();
_api.NotifyShellIntegrationMark();
}
else
{
_api.UnknownSequence();
}
}
// Method Description:
@@ -3638,6 +3647,10 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string)
_pages.ActivePage().Buffer().StartPrompt();
_api.NotifyShellIntegrationMark();
}
else
{
_api.UnknownSequence();
}
}
// Method Description:
@@ -3708,9 +3721,14 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
break;
}
default:
_api.UnknownSequence();
break;
}
}
else
{
_api.UnknownSequence();
}
// When we add the rest of the FTCS sequences (GH#11000), we should add a
// simple state machine here to track the most recently emitted mark from
@@ -3785,6 +3803,10 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
// If it's poorly formatted, just eat it
}
else
{
_api.UnknownSequence();
}
}
// Method Description:

View File

@@ -38,6 +38,7 @@ namespace Microsoft::Console::VirtualTerminal
public:
AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettings& renderSettings, TerminalInput& terminalInput) noexcept;
void UnknownSequence() noexcept override;
void Print(const wchar_t wchPrintable) override;
void PrintString(const std::wstring_view string) override;

View File

@@ -22,6 +22,7 @@ namespace Microsoft::Console::VirtualTerminal
class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Console::VirtualTerminal::ITermDispatch
{
public:
void UnknownSequence() noexcept override {}
void Print(const wchar_t wchPrintable) override = 0;
void PrintString(const std::wstring_view string) override = 0;

View File

@@ -60,6 +60,10 @@ using namespace Microsoft::Console::VirtualTerminal;
class TestGetSet final : public ITerminalApi
{
public:
void UnknownSequence() noexcept override
{
}
void ReturnResponse(const std::wstring_view response) override
{
Log::Comment(L"ReturnResponse MOCK called...");

View File

@@ -28,6 +28,7 @@ namespace Microsoft::Console::VirtualTerminal
IStateMachineEngine& operator=(const IStateMachineEngine&) = default;
IStateMachineEngine& operator=(IStateMachineEngine&&) = default;
virtual void UnknownSequence() noexcept = 0;
virtual bool EncounteredWin32InputModeSequence() const noexcept = 0;
virtual bool ActionExecute(const wchar_t wch) = 0;

View File

@@ -129,6 +129,10 @@ til::enumset<DeviceAttribute, uint64_t> InputStateMachineEngine::WaitUntilDA1(DW
return til::enumset<DeviceAttribute, uint64_t>::from_bits(val);
}
void InputStateMachineEngine::UnknownSequence() noexcept
{
}
bool InputStateMachineEngine::EncounteredWin32InputModeSequence() const noexcept
{
return _encounteredWin32InputModeSequence;

View File

@@ -166,6 +166,7 @@ namespace Microsoft::Console::VirtualTerminal
void CaptureNextCursorPositionReport() noexcept;
til::enumset<DeviceAttribute, uint64_t> WaitUntilDA1(DWORD timeout) noexcept;
void UnknownSequence() noexcept override;
bool EncounteredWin32InputModeSequence() const noexcept override;
bool ActionExecute(const wchar_t wch) override;

View File

@@ -24,6 +24,11 @@ OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr<ITermDispatch
THROW_HR_IF_NULL(E_INVALIDARG, _dispatch.get());
}
void OutputStateMachineEngine::UnknownSequence() noexcept
{
_dispatch->UnknownSequence();
}
bool OutputStateMachineEngine::EncounteredWin32InputModeSequence() const noexcept
{
return false;
@@ -683,6 +688,7 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
_dispatch->PopKittyKeyboardProtocol(parameters.at(0));
break;
default:
_dispatch->UnknownSequence();
break;
}
@@ -737,7 +743,7 @@ IStateMachineEngine::StringHandler OutputStateMachineEngine::ActionDcsDispatch(c
handler = _dispatch->RestorePresentationState(parameters.at(0));
break;
default:
handler = nullptr;
_dispatch->UnknownSequence();
break;
}
@@ -858,6 +864,10 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
}
break;
}
case OscActionCodes::CurrentWorkingDirectory:
// TODO: Add support for OSC 7 = CWD sequences?
// In GH#8214 it was decided that it's a bad idea due to WSL compat.
break;
case OscActionCodes::Hyperlink:
{
std::wstring params;
@@ -901,6 +911,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
break;
}
default:
_dispatch->UnknownSequence();
break;
}
@@ -921,6 +932,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
bool OutputStateMachineEngine::ActionSs3Dispatch(const wchar_t /*wch*/, const VTParameters /*parameters*/) noexcept
{
// The output engine doesn't handle any SS3 sequences.
_dispatch->UnknownSequence();
_ClearLastChar();
return true;
}

View File

@@ -24,6 +24,7 @@ namespace Microsoft::Console::VirtualTerminal
OutputStateMachineEngine(std::unique_ptr<ITermDispatch> pDispatch);
void UnknownSequence() noexcept override;
bool EncounteredWin32InputModeSequence() const noexcept override;
bool ActionExecute(const wchar_t wch) override;
@@ -203,13 +204,14 @@ namespace Microsoft::Console::VirtualTerminal
ExitVt52Mode = VTID("<")
};
enum OscActionCodes : unsigned int
enum OscActionCodes : size_t
{
SetIconAndWindowTitle = 0,
SetWindowIcon = 1,
SetWindowTitle = 2,
SetWindowProperty = 3, // Not implemented
SetColor = 4,
CurrentWorkingDirectory = 7,
Hyperlink = 8,
ConEmuAction = 9,
SetForegroundColor = 10,

View File

@@ -1044,6 +1044,7 @@ void StateMachine::_EnterSosPmApcString() noexcept
{
_state = VTStates::SosPmApcString;
_cachedSequence.reset();
_engine->UnknownSequence();
_trace.TraceStateChange(L"SosPmApcString");
}

View File

@@ -40,6 +40,10 @@ public:
dcsDataString.clear();
}
void UnknownSequence() noexcept override
{
}
bool EncounteredWin32InputModeSequence() const noexcept override
{
return false;