From e818dafa6db29cb67cc2418ba67e5e0846a6e9b4 Mon Sep 17 00:00:00 2001 From: Quaylyn Rimer <31830590+killerdevildog@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:20:47 -0700 Subject: [PATCH] Fix scrollbar marks not appearing until scroll or resize (#19185) This PR resolves an issue where scrollbar marks created by shell integration sequences (OSC 133 FTCS, OSC 1337 iTerm2, and OSC 9;12 ConEmu sequences) were not visible on the scrollbar until the user manually scrolled. The problem was that while marks were being created in the buffer correctly, the UI wasn't being notified to refresh the scrollbar display. The fix adds a new NotifyShellIntegrationMark() method to the ITerminalApi interface that calls _NotifyScrollEvent() to trigger scrollbar refresh, and updates all shell integration sequence handlers in AdaptDispatch to call this notification method after creating marks. This ensures scrollbar marks appear immediately when shell integration sequences are processed, bringing feature parity between auto-detected and shell-integration-based marks. Closes #19104 --- src/cascadia/TerminalCore/Terminal.hpp | 1 + src/cascadia/TerminalCore/TerminalApi.cpp | 6 ++++++ src/host/outputStream.cpp | 5 +++++ src/host/outputStream.hpp | 1 + src/terminal/adapter/ITerminalApi.hpp | 1 + src/terminal/adapter/adaptDispatch.cpp | 6 ++++++ src/terminal/adapter/ut_adapter/adapterTest.cpp | 5 +++++ 7 files changed, 25 insertions(+) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index e38841e3bc..837f1c8525 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -156,6 +156,7 @@ public: bool IsVtInputEnabled() const noexcept override; void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; void NotifyBufferRotation(const int delta) override; + void NotifyShellIntegrationMark() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9822d68208..825712028e 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -404,3 +404,9 @@ void Terminal::NotifyBufferRotation(const int delta) _NotifyScrollEvent(); } } + +void Terminal::NotifyShellIntegrationMark() +{ + // Notify the scrollbar that marks have been added so it can refresh the mark indicators + _NotifyScrollEvent(); +} diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 5fc568d456..5ef852065f 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -446,6 +446,11 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta) } } +void ConhostInternalGetSet::NotifyShellIntegrationMark() +{ + // Not implemented for conhost - shell integration marks are a Terminal app feature. +} + void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/) { // Not implemented for conhost. diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 7b76e5f3d0..cfb1c85f16 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -67,6 +67,7 @@ public: void NotifyAccessibilityChange(const til::rect& changedRect) override; void NotifyBufferRotation(const int delta) override; + void NotifyShellIntegrationMark() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 532133f9a8..3ce0e5c83d 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -86,6 +86,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0; virtual void NotifyBufferRotation(const int delta) = 0; + virtual void NotifyShellIntegrationMark() = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 23e12f979f..5de6cdd602 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3600,6 +3600,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string) else if (subParam == 12) { _pages.ActivePage().Buffer().StartCommand(); + _api.NotifyShellIntegrationMark(); } } @@ -3630,6 +3631,7 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string) if (action == L"SetMark") { _pages.ActivePage().Buffer().StartPrompt(); + _api.NotifyShellIntegrationMark(); } } @@ -3663,16 +3665,19 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) case L'A': // FTCS_PROMPT { _pages.ActivePage().Buffer().StartPrompt(); + _api.NotifyShellIntegrationMark(); break; } case L'B': // FTCS_COMMAND_START { _pages.ActivePage().Buffer().StartCommand(); + _api.NotifyShellIntegrationMark(); break; } case L'C': // FTCS_COMMAND_EXECUTED { _pages.ActivePage().Buffer().StartOutput(); + _api.NotifyShellIntegrationMark(); break; } case L'D': // FTCS_COMMAND_FINISHED @@ -3693,6 +3698,7 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) } _pages.ActivePage().Buffer().EndCurrentCommand(error); + _api.NotifyShellIntegrationMark(); break; } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index a6eecb6b37..5821bb28ae 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -212,6 +212,11 @@ public: Log::Comment(L"NotifyBufferRotation MOCK called..."); } + void NotifyShellIntegrationMark() override + { + Log::Comment(L"NotifyShellIntegrationMark MOCK called..."); + } + void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override { Log::Comment(L"InvokeCompletions MOCK called...");