Use RenderSettings for DECSET 2026 - Synchronized Output (#18833)

`RenderSettings` already stores `DECSCNM` (reversed screen),
so it only makes sense to also store DECSET 2026 there.

## Validation Steps Performed
* Same as in #18826 
This commit is contained in:
Leonard Hecker 2025-04-24 19:22:30 +02:00 committed by GitHub
parent 093f5d168c
commit a2d80682c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 44 additions and 40 deletions

View File

@ -143,7 +143,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer. // the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
{ {
// Now create the renderer and initialize the render thread. // Now create the renderer and initialize the render thread.
const auto& renderSettings = _terminal->GetRenderSettings(); auto& renderSettings = _terminal->GetRenderSettings();
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get()); _renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get());
_renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); }); _renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); });

View File

@ -46,9 +46,9 @@ void RenderSettings::RestoreDefaultSettings() noexcept
{ {
_colorTable = _defaultColorTable; _colorTable = _defaultColorTable;
_colorAliasIndices = _defaultColorAliasIndices; _colorAliasIndices = _defaultColorAliasIndices;
// For now, DECSCNM is the only render mode we need to reset. The others are // DECSCNM and Synchronized Output are the only render mode we need to reset.
// all user preferences that can't be changed programmatically. // The others are all user preferences that can't be changed programmatically.
_renderMode.reset(Mode::ScreenReversed); _renderMode.reset(Mode::ScreenReversed, Mode::SynchronizedOutput);
} }
// Routine Description: // Routine Description:

View File

@ -25,7 +25,7 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
// - pData - The interface to console data structures required for rendering // - pData - The interface to console data structures required for rendering
// Return Value: // Return Value:
// - An instance of a Renderer. // - An instance of a Renderer.
Renderer::Renderer(const RenderSettings& renderSettings, IRenderData* pData) : Renderer::Renderer(RenderSettings& renderSettings, IRenderData* pData) :
_renderSettings(renderSettings), _renderSettings(renderSettings),
_pData(pData) _pData(pData)
{ {
@ -187,17 +187,21 @@ void Renderer::NotifyPaintFrame() noexcept
} }
// NOTE: You must be holding the console lock when calling this function. // NOTE: You must be holding the console lock when calling this function.
void Renderer::SynchronizedOutputBegin() noexcept void Renderer::SynchronizedOutputChanged() noexcept
{ {
// Kick the render thread into calling `_synchronizeWithOutput()`. const auto so = _renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput);
_isSynchronizingOutput = true; if (_isSynchronizingOutput == so)
} {
return;
}
// NOTE: You must be holding the console lock when calling this function. // If `_isSynchronizingOutput` is true, it'll kick the
void Renderer::SynchronizedOutputEnd() noexcept // render thread into calling `_synchronizeWithOutput()`...
{ _isSynchronizingOutput = so;
// Unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
_isSynchronizingOutput = false; if (!_isSynchronizingOutput)
{
// ...otherwise, unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
WakeByAddressSingle(&_isSynchronizingOutput); WakeByAddressSingle(&_isSynchronizingOutput);
// It's crucial to give the render thread at least a chance to gain the lock. // It's crucial to give the render thread at least a chance to gain the lock.
@ -212,6 +216,7 @@ void Renderer::SynchronizedOutputEnd() noexcept
// Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate. // Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate.
_pData->UnlockConsole(); _pData->UnlockConsole();
_pData->LockConsole(); _pData->LockConsole();
}
} }
void Renderer::_synchronizeWithOutput() noexcept void Renderer::_synchronizeWithOutput() noexcept
@ -249,6 +254,7 @@ void Renderer::_synchronizeWithOutput() noexcept
// If a timeout occurred, `_isSynchronizingOutput` may still be true. // If a timeout occurred, `_isSynchronizingOutput` may still be true.
// Set it to false now to skip calling `_synchronizeWithOutput()` on the next frame. // Set it to false now to skip calling `_synchronizeWithOutput()` on the next frame.
_isSynchronizingOutput = false; _isSynchronizingOutput = false;
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, false);
} }
// Routine Description: // Routine Description:

View File

@ -28,15 +28,14 @@ namespace Microsoft::Console::Render
class Renderer class Renderer
{ {
public: public:
Renderer(const RenderSettings& renderSettings, IRenderData* pData); Renderer(RenderSettings& renderSettings, IRenderData* pData);
IRenderData* GetRenderData() const noexcept; IRenderData* GetRenderData() const noexcept;
[[nodiscard]] HRESULT PaintFrame(); [[nodiscard]] HRESULT PaintFrame();
void NotifyPaintFrame() noexcept; void NotifyPaintFrame() noexcept;
void SynchronizedOutputBegin() noexcept; void SynchronizedOutputChanged() noexcept;
void SynchronizedOutputEnd() noexcept;
void TriggerSystemRedraw(const til::rect* const prcDirtyClient); void TriggerSystemRedraw(const til::rect* const prcDirtyClient);
void TriggerRedraw(const Microsoft::Console::Types::Viewport& region); void TriggerRedraw(const Microsoft::Console::Types::Viewport& region);
void TriggerRedraw(const til::point* const pcoord); void TriggerRedraw(const til::point* const pcoord);
@ -113,7 +112,7 @@ namespace Microsoft::Console::Render
void _prepareNewComposition(); void _prepareNewComposition();
[[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine); [[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine);
const RenderSettings& _renderSettings; RenderSettings& _renderSettings;
std::array<IRenderEngine*, 2> _engines{}; std::array<IRenderEngine*, 2> _engines{};
IRenderData* _pData = nullptr; // Non-ownership pointer IRenderData* _pData = nullptr; // Non-ownership pointer
static constexpr size_t _firstSoftFontChar = 0xEF20; static constexpr size_t _firstSoftFontChar = 0xEF20;

View File

@ -25,7 +25,8 @@ namespace Microsoft::Console::Render
AlwaysDistinguishableColors, AlwaysDistinguishableColors,
IntenseIsBold, IntenseIsBold,
IntenseIsBright, IntenseIsBright,
ScreenReversed ScreenReversed,
SynchronizedOutput,
}; };
RenderSettings() noexcept; RenderSettings() noexcept;

View File

@ -1915,16 +1915,10 @@ void AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con
_api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable); _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable);
break; break;
case DispatchTypes::ModeParams::SO_SynchronizedOutput: case DispatchTypes::ModeParams::SO_SynchronizedOutput:
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, enable);
if (_renderer) if (_renderer)
{ {
if (enable) _renderer->SynchronizedOutputChanged();
{
_renderer->SynchronizedOutputBegin();
}
else
{
_renderer->SynchronizedOutputEnd();
}
} }
break; break;
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
@ -2065,6 +2059,9 @@ void AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param)
case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: case DispatchTypes::ModeParams::XTERM_BracketedPasteMode:
state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste)); state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste));
break; break;
case DispatchTypes::ModeParams::SO_SynchronizedOutput:
state = mapTemp(_renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput));
break;
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes); state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes);
break; break;
@ -3050,6 +3047,7 @@ void AdaptDispatch::HardReset()
if (_renderer) if (_renderer)
{ {
_renderer->TriggerRedrawAll(true, true); _renderer->TriggerRedrawAll(true, true);
_renderer->SynchronizedOutputChanged();
} }
// Cursor to 1,1 - the Soft Reset guarantees this is absolute // Cursor to 1,1 - the Soft Reset guarantees this is absolute