Skip to content

Conversation

lhecker
Copy link
Member

@lhecker lhecker commented Sep 8, 2025

Note that this PR is not quite finished (~90%?). Missing:

  • Some polish in both conhost & particularly WT integration (e.g. caching the blink interval value)
  • Propagating the user blink setting on the WT side

This PR moves the cursor blinker and VT blink rendition timer into Renderer. To do so, this PR introduces a generic timer system with which you can schedule arbitrary timer jobs. Thanks to this, this PR removes a crapton of code, particularly throughout conhost, and improves throughput by another ~10%. On my PC it can now churn through >400MB/s while rendering at 240FPS. Fun fact: Processing 100kB of text and rendering it in conhost now takes less time than MSCTF (TSF) needs to process 1 keyboard character. When I look at our shell code.

Validation Steps Performed

  • TBD

This comment has been minimized.

@lhecker lhecker force-pushed the dev/lhecker/cursor-blinker branch from 4de989d to 8d17e85 Compare September 8, 2025 19:44
@lhecker lhecker changed the base branch from main to dev/lhecker/BEGONE-FOUL-SPIRIT September 14, 2025 11:16
@lhecker lhecker force-pushed the dev/lhecker/cursor-blinker branch from 8d17e85 to 8390d60 Compare September 14, 2025 11:26
DHowett pushed a commit that referenced this pull request Sep 22, 2025
Goal: Remove `CursorBlinker`.
Problem: Spooky action at a distance via `Cursor::HasMoved`.
Solution: Moved all the a11y event raising into `_stream.cpp` and pray
for the best.

Goal: Prevent node.js from tanking conhost performance via MSAA (WHY).
Problem: `ServiceLocator`.
Solution: Unserviced the locator. Debounced event raising. Performance
increased by >10x.
Problem 2: Lots of files changed.

This PR is a prerequisite for #19330

## Validation Steps Performed
Ran NVDA with and without UIA enabled and with different delays. ✅
@lhecker lhecker force-pushed the dev/lhecker/cursor-blinker branch from 8390d60 to 5742c64 Compare September 22, 2025 22:59
@lhecker lhecker changed the base branch from dev/lhecker/BEGONE-FOUL-SPIRIT to main September 22, 2025 22:59
@lhecker lhecker marked this pull request as ready for review September 22, 2025 22:59
__declspec(dllexport) HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
Copy link
Member

Choose a reason for hiding this comment

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

@DHowett Curious. Since VS is a consumer here, do we just notify them of these changes? Is there anything special we have to be aware of with the partnership?

Copy link
Member

Choose a reason for hiding this comment

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

(as below)

Comment on lines -709 to -717
void _stdcall TerminalClearSelection(void* terminal)
try
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
const auto lock = publicTerminal->_terminal->LockForWriting();
publicTerminal->_ClearSelection();
}
CATCH_LOG()

Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason we're removing this one? Doesn't seem related to the cursor blink changes, and it's removing an API, right?

Copy link
Member

Choose a reason for hiding this comment

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

So, the good news about all of the Terminal* APIs inside HwndTerminal.cpp is that they are all internal. They are a contract between the WPF control and the TermControl. We can change them practically at-will. :)


void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
try
void HwndTerminal::_setFocused(bool focused) noexcept
Copy link
Member

Choose a reason for hiding this comment

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

Big fan of this. It's nice that it's all in one function 😊

Comment on lines -16 to -20
#define FOREACH_ENGINE(var) \
for (auto var : _engines) \
if (!var) \
break; \
else
Copy link
Member

Choose a reason for hiding this comment

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

Huh. We don't need the if (!var) break anymore?

else if (!IsTimerRunning(_cursorBlinker))
{
const auto actual = GetTimerInterval(_cursorBlinker);
auto expected = _pData->GetBlinkInterval();
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
auto expected = _pData->GetBlinkInterval();
const auto expected = _pData->GetBlinkInterval();

const?


bool RenderData::IsUiaDataInitialized() const noexcept
{
return true;
Copy link
Member

Choose a reason for hiding this comment

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

(Double checking) In terminalrenderdata.cpp, we have this:

bool Terminal::IsUiaDataInitialized() const noexcept
{
// GH#11135: Windows Terminal needs to create and return an automation peer
// when a screen reader requests it. However, the terminal might not be fully
// initialized yet. So we use this to check if any crucial components of
// UiaData are not yet initialized.
_assertLocked();
return !!_mainBuffer;
}

We don't need to check the buffer here?

Comment on lines -438 to -452
// initialize cursor GH#4102 - Typically, the cursor is set to on by the
// cursor blinker. Unfortunately, in conpty mode, there is no cursor
// blinker. So, in conpty mode, we need to leave the cursor on always. The
// cursor can still be set to hidden, and whether the cursor should be
// blinking will still be passed through to the terminal, but internally,
// the cursor should always be on.
//
// In particular, some applications make use of a calling
// `SetConsoleScreenBuffer` and `SetCursorPosition` without printing any
// text in between these calls. If we initialize the cursor to Off in conpty
// mode, then the cursor will remain off until they print text. This can
// lead to alignment problems in the terminal, because we won't move the
// terminal's cursor in this _exact_ scenario.
screenInfo.GetTextBuffer().GetCursor().SetIsOn(gci.IsInVtIoMode());

Copy link
Member

Choose a reason for hiding this comment

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

Hmm... Do we need to worry about this scenario still?

Comment on lines 2437 to 2439
// We trigger a scroll rather than a redraw, since that's more efficient,
// but we need to turn the cursor off before doing so; otherwise, a ghost
// cursor can be left behind in the previous position.
Copy link
Member

Choose a reason for hiding this comment

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

update the comment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants