Skip to content

Commit a2d8068

Browse files
authored
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
1 parent 093f5d1 commit a2d8068

File tree

6 files changed

+45
-41
lines changed

6 files changed

+45
-41
lines changed

src/cascadia/TerminalControl/ControlCore.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
143143
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
144144
{
145145
// Now create the renderer and initialize the render thread.
146-
const auto& renderSettings = _terminal->GetRenderSettings();
146+
auto& renderSettings = _terminal->GetRenderSettings();
147147
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get());
148148

149149
_renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); });

src/renderer/base/RenderSettings.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ void RenderSettings::RestoreDefaultSettings() noexcept
4646
{
4747
_colorTable = _defaultColorTable;
4848
_colorAliasIndices = _defaultColorAliasIndices;
49-
// For now, DECSCNM is the only render mode we need to reset. The others are
50-
// all user preferences that can't be changed programmatically.
51-
_renderMode.reset(Mode::ScreenReversed);
49+
// DECSCNM and Synchronized Output are the only render mode we need to reset.
50+
// The others are all user preferences that can't be changed programmatically.
51+
_renderMode.reset(Mode::ScreenReversed, Mode::SynchronizedOutput);
5252
}
5353

5454
// Routine Description:

src/renderer/base/renderer.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
2525
// - pData - The interface to console data structures required for rendering
2626
// Return Value:
2727
// - An instance of a Renderer.
28-
Renderer::Renderer(const RenderSettings& renderSettings, IRenderData* pData) :
28+
Renderer::Renderer(RenderSettings& renderSettings, IRenderData* pData) :
2929
_renderSettings(renderSettings),
3030
_pData(pData)
3131
{
@@ -187,31 +187,36 @@ void Renderer::NotifyPaintFrame() noexcept
187187
}
188188

189189
// NOTE: You must be holding the console lock when calling this function.
190-
void Renderer::SynchronizedOutputBegin() noexcept
190+
void Renderer::SynchronizedOutputChanged() noexcept
191191
{
192-
// Kick the render thread into calling `_synchronizeWithOutput()`.
193-
_isSynchronizingOutput = true;
194-
}
192+
const auto so = _renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput);
193+
if (_isSynchronizingOutput == so)
194+
{
195+
return;
196+
}
195197

196-
// NOTE: You must be holding the console lock when calling this function.
197-
void Renderer::SynchronizedOutputEnd() noexcept
198-
{
199-
// Unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
200-
_isSynchronizingOutput = false;
201-
WakeByAddressSingle(&_isSynchronizingOutput);
202-
203-
// It's crucial to give the render thread at least a chance to gain the lock.
204-
// Otherwise, a VT application could continuously spam DECSET 2026 (Synchronized Output) and
205-
// essentially drop our renderer to 10 FPS, because `_isSynchronizingOutput` is always true.
206-
//
207-
// Obviously calling LockConsole/UnlockConsole here is an awful, ugly hack,
208-
// since there's no guarantee that this is the same lock as the one the VT parser uses.
209-
// But the alternative is Denial-Of-Service of the render thread.
210-
//
211-
// Note that this causes raw throughput of DECSET 2026 to be comparatively low, but that's fine.
212-
// Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate.
213-
_pData->UnlockConsole();
214-
_pData->LockConsole();
198+
// If `_isSynchronizingOutput` is true, it'll kick the
199+
// render thread into calling `_synchronizeWithOutput()`...
200+
_isSynchronizingOutput = so;
201+
202+
if (!_isSynchronizingOutput)
203+
{
204+
// ...otherwise, unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
205+
WakeByAddressSingle(&_isSynchronizingOutput);
206+
207+
// It's crucial to give the render thread at least a chance to gain the lock.
208+
// Otherwise, a VT application could continuously spam DECSET 2026 (Synchronized Output) and
209+
// essentially drop our renderer to 10 FPS, because `_isSynchronizingOutput` is always true.
210+
//
211+
// Obviously calling LockConsole/UnlockConsole here is an awful, ugly hack,
212+
// since there's no guarantee that this is the same lock as the one the VT parser uses.
213+
// But the alternative is Denial-Of-Service of the render thread.
214+
//
215+
// Note that this causes raw throughput of DECSET 2026 to be comparatively low, but that's fine.
216+
// Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate.
217+
_pData->UnlockConsole();
218+
_pData->LockConsole();
219+
}
215220
}
216221

217222
void Renderer::_synchronizeWithOutput() noexcept
@@ -249,6 +254,7 @@ void Renderer::_synchronizeWithOutput() noexcept
249254
// If a timeout occurred, `_isSynchronizingOutput` may still be true.
250255
// Set it to false now to skip calling `_synchronizeWithOutput()` on the next frame.
251256
_isSynchronizingOutput = false;
257+
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, false);
252258
}
253259

254260
// Routine Description:

src/renderer/base/renderer.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ namespace Microsoft::Console::Render
2828
class Renderer
2929
{
3030
public:
31-
Renderer(const RenderSettings& renderSettings, IRenderData* pData);
31+
Renderer(RenderSettings& renderSettings, IRenderData* pData);
3232

3333
IRenderData* GetRenderData() const noexcept;
3434

3535
[[nodiscard]] HRESULT PaintFrame();
3636

3737
void NotifyPaintFrame() noexcept;
38-
void SynchronizedOutputBegin() noexcept;
39-
void SynchronizedOutputEnd() noexcept;
38+
void SynchronizedOutputChanged() noexcept;
4039
void TriggerSystemRedraw(const til::rect* const prcDirtyClient);
4140
void TriggerRedraw(const Microsoft::Console::Types::Viewport& region);
4241
void TriggerRedraw(const til::point* const pcoord);
@@ -113,7 +112,7 @@ namespace Microsoft::Console::Render
113112
void _prepareNewComposition();
114113
[[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine);
115114

116-
const RenderSettings& _renderSettings;
115+
RenderSettings& _renderSettings;
117116
std::array<IRenderEngine*, 2> _engines{};
118117
IRenderData* _pData = nullptr; // Non-ownership pointer
119118
static constexpr size_t _firstSoftFontChar = 0xEF20;

src/renderer/inc/RenderSettings.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ namespace Microsoft::Console::Render
2525
AlwaysDistinguishableColors,
2626
IntenseIsBold,
2727
IntenseIsBright,
28-
ScreenReversed
28+
ScreenReversed,
29+
SynchronizedOutput,
2930
};
3031

3132
RenderSettings() noexcept;

src/terminal/adapter/adaptDispatch.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,16 +1915,10 @@ void AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con
19151915
_api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable);
19161916
break;
19171917
case DispatchTypes::ModeParams::SO_SynchronizedOutput:
1918+
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, enable);
19181919
if (_renderer)
19191920
{
1920-
if (enable)
1921-
{
1922-
_renderer->SynchronizedOutputBegin();
1923-
}
1924-
else
1925-
{
1926-
_renderer->SynchronizedOutputEnd();
1927-
}
1921+
_renderer->SynchronizedOutputChanged();
19281922
}
19291923
break;
19301924
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
@@ -2065,6 +2059,9 @@ void AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param)
20652059
case DispatchTypes::ModeParams::XTERM_BracketedPasteMode:
20662060
state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste));
20672061
break;
2062+
case DispatchTypes::ModeParams::SO_SynchronizedOutput:
2063+
state = mapTemp(_renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput));
2064+
break;
20682065
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
20692066
state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes);
20702067
break;
@@ -3050,6 +3047,7 @@ void AdaptDispatch::HardReset()
30503047
if (_renderer)
30513048
{
30523049
_renderer->TriggerRedrawAll(true, true);
3050+
_renderer->SynchronizedOutputChanged();
30533051
}
30543052

30553053
// Cursor to 1,1 - the Soft Reset guarantees this is absolute

0 commit comments

Comments
 (0)