Merge remote-tracking branch 'origin/main' into feature/llm

This commit is contained in:
Console Service Bot 2025-11-25 02:31:56 +00:00
commit c30736e198
15 changed files with 90 additions and 58 deletions

View File

@ -1091,6 +1091,8 @@ NEXTLINE
nfe nfe
NLSMODE NLSMODE
NOACTIVATE NOACTIVATE
NOACTIVATEKEYBOARDLAYOUT
NOACTIVATETIP
NOAPPLYNOW NOAPPLYNOW
NOCLIP NOCLIP
NOCOMM NOCOMM
@ -1504,7 +1506,6 @@ scrolllock
scrolloffset scrolloffset
SCROLLSCALE SCROLLSCALE
SCROLLSCREENBUFFER SCROLLSCREENBUFFER
scursor
sddl sddl
SDKDDK SDKDDK
segfault segfault
@ -1703,9 +1704,11 @@ TEXTMETRIC
TEXTMETRICW TEXTMETRICW
textmode textmode
texttests texttests
TFCAT
THUMBPOSITION THUMBPOSITION
THUMBTRACK THUMBTRACK
tilunittests tilunittests
TIPCAP
titlebars titlebars
TITLEISLINKNAME TITLEISLINKNAME
TLDP TLDP
@ -1757,6 +1760,8 @@ UIACCESS
uiacore uiacore
uiautomationcore uiautomationcore
uielem uielem
UIELEMENTENABLED
UIELEMENTENABLEDONLY
UINTs UINTs
uld uld
uldash uldash

View File

@ -462,6 +462,11 @@ void HwndTerminal::SendOutput(std::wstring_view data)
_terminal->Write(data); _terminal->Write(data);
} }
void _stdcall AvoidBuggyTSFConsoleFlags()
{
Microsoft::Console::TSF::Handle::AvoidBuggyTSFConsoleFlags();
}
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{ {
auto publicTerminal = std::make_unique<HwndTerminal>(parentHwnd); auto publicTerminal = std::make_unique<HwndTerminal>(parentHwnd);

View File

@ -41,6 +41,7 @@ typedef struct _TerminalTheme
} TerminalTheme, *LPTerminalTheme; } TerminalTheme, *LPTerminalTheme;
extern "C" { extern "C" {
__declspec(dllexport) void _stdcall AvoidBuggyTSFConsoleFlags();
__declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal); __declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
__declspec(dllexport) void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data); __declspec(dllexport) void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data);
__declspec(dllexport) void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int)); __declspec(dllexport) void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int));

View File

@ -4,6 +4,7 @@ EXPORTS
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
; Flat C ABI ; Flat C ABI
AvoidBuggyTSFConsoleFlags
CreateTerminal CreateTerminal
DestroyTerminal DestroyTerminal
TerminalCalculateResize TerminalCalculateResize

View File

@ -1297,9 +1297,15 @@ void Terminal::ScrollToSearchHighlight(til::CoordType searchScrollOffset)
if (_searchHighlightFocused < _searchHighlights.size()) if (_searchHighlightFocused < _searchHighlights.size())
{ {
const auto focused = til::at(_searchHighlights, _searchHighlightFocused); const auto focused = til::at(_searchHighlights, _searchHighlightFocused);
const auto adjustedStart = til::point{ focused.start.x, std::max(0, focused.start.y - searchScrollOffset) };
const auto adjustedEnd = til::point{ focused.end.x, std::max(0, focused.end.y - searchScrollOffset) }; // Only adjust the y coordinates if "start" is in a row that would be covered by the search box
_ScrollToPoints(adjustedStart, adjustedEnd); auto adjustedStart = focused.start;
const auto firstVisibleRow = _VisibleStartIndex();
if (focused.start.y > firstVisibleRow && focused.start.y < firstVisibleRow + searchScrollOffset)
{
adjustedStart.y = std::max(0, focused.start.y - searchScrollOffset);
}
_ScrollToPoints(adjustedStart, focused.end);
} }
} }

View File

@ -152,29 +152,14 @@ const til::point_span* Terminal::GetSearchHighlightFocused() const noexcept
// - The updated scroll offset // - The updated scroll offset
til::CoordType Terminal::_ScrollToPoints(const til::point coordStart, const til::point coordEnd) til::CoordType Terminal::_ScrollToPoints(const til::point coordStart, const til::point coordEnd)
{ {
auto notifyScrollChange = false;
if (coordStart.y < _VisibleStartIndex()) if (coordStart.y < _VisibleStartIndex())
{ {
// recalculate the scrollOffset _ScrollToPoint(coordStart);
_scrollOffset = ViewStartIndex() - coordStart.y;
notifyScrollChange = true;
} }
else if (coordEnd.y > _VisibleEndIndex()) else if (coordEnd.y > _VisibleEndIndex())
{ {
// recalculate the scrollOffset, note that if the found text is _ScrollToPoint(coordEnd);
// beneath the current visible viewport, it may be within the
// current mutableViewport and the scrollOffset will be smaller
// than 0
_scrollOffset = std::max(0, ViewStartIndex() - coordStart.y);
notifyScrollChange = true;
} }
if (notifyScrollChange)
{
_activeBuffer().TriggerScroll();
_NotifyScrollEvent();
}
return _VisibleStartIndex(); return _VisibleStartIndex();
} }

View File

@ -159,12 +159,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
CurrentColor(nullptr); CurrentColor(nullptr);
} }
safe_void_coroutine NullableColorPicker::MoreColors_Clicked(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/) void NullableColorPicker::Flyout_Opening(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
co_await ColorPickerDialog().ShowAsync();
}
void NullableColorPicker::ColorPickerDialog_Opened(const IInspectable& /*sender*/, const ContentDialogOpenedEventArgs& /*args*/)
{ {
// Initialize color picker with current color // Initialize color picker with current color
if (CurrentColor()) if (CurrentColor())
@ -185,7 +180,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
} }
} }
void NullableColorPicker::ColorPickerDialog_PrimaryButtonClick(const IInspectable& /*sender*/, const ContentDialogButtonClickEventArgs& /*args*/) void NullableColorPicker::Flyout_Closing(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::Controls::Primitives::FlyoutBaseClosingEventArgs& /*args*/)
{ {
const auto& selectedColor = ColorPickerControl().Color(); const auto& selectedColor = ColorPickerControl().Color();
const Microsoft::Terminal::Core::Color terminalColor{ selectedColor.R, selectedColor.G, selectedColor.B, selectedColor.A }; const Microsoft::Terminal::Core::Color terminalColor{ selectedColor.R, selectedColor.G, selectedColor.B, selectedColor.A };

View File

@ -22,10 +22,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ColorChip_DataContextChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DataContextChangedEventArgs& args); void ColorChip_DataContextChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DataContextChangedEventArgs& args);
void NullColorButton_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args); void NullColorButton_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
safe_void_coroutine MoreColors_Clicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args); void Flyout_Opening(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
void Flyout_Closing(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::Primitives::FlyoutBaseClosingEventArgs& args);
void ColorPickerDialog_Opened(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::ContentDialogOpenedEventArgs& args);
void ColorPickerDialog_PrimaryButtonClick(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& args);
DEPENDENCY_PROPERTY(Editor::ColorSchemeViewModel, ColorSchemeVM); DEPENDENCY_PROPERTY(Editor::ColorSchemeViewModel, ColorSchemeVM);
DEPENDENCY_PROPERTY(Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>, CurrentColor); DEPENDENCY_PROPERTY(Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>, CurrentColor);

View File

@ -97,25 +97,6 @@
<StackPanel x:Name="ContentStackPanel" <StackPanel x:Name="ContentStackPanel"
Orientation="Horizontal" Orientation="Horizontal"
Spacing="5"> Spacing="5">
<ContentDialog x:Name="ColorPickerDialog"
x:Uid="NullableColorPicker_ColorPickerContentDialog"
DefaultButton="Primary"
Opened="ColorPickerDialog_Opened"
PrimaryButtonClick="ColorPickerDialog_PrimaryButtonClick"
TabFocusNavigation="Cycle">
<muxc:ColorPicker x:Name="ColorPickerControl"
Margin="0,0,0,-40"
ColorSpectrumShape="Box"
IsAlphaEnabled="False"
IsAlphaSliderVisible="True"
IsAlphaTextInputVisible="True"
IsColorChannelTextInputVisible="False"
IsColorSliderVisible="False"
IsHexInputVisible="False"
IsMoreButtonVisible="False"
Orientation="Horizontal" />
</ContentDialog>
<ContentPresenter Content="{x:Bind ColorSchemeVM, Mode=OneWay}" <ContentPresenter Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
ContentTemplate="{StaticResource ColorSchemeTemplate}" /> ContentTemplate="{StaticResource ColorSchemeTemplate}" />
@ -146,8 +127,14 @@
</ToggleButton> </ToggleButton>
<Button x:Uid="NullableColorPicker_MoreColorsButton" <Button x:Uid="NullableColorPicker_MoreColorsButton"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch">
Click="MoreColors_Clicked" /> <Button.Flyout>
<Flyout Closing="Flyout_Closing"
Opening="Flyout_Opening">
<muxc:ColorPicker x:Name="ColorPickerControl" />
</Flyout>
</Button.Flyout>
</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@ -171,6 +171,9 @@ namespace Microsoft.Terminal.Wpf
SWP_SHOWWINDOW = 0x0040, SWP_SHOWWINDOW = 0x0040,
} }
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
public static extern void AvoidBuggyTSFConsoleFlags();
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)] [DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void CreateTerminal(IntPtr parent, out IntPtr hwnd, out IntPtr terminal); public static extern void CreateTerminal(IntPtr parent, out IntPtr hwnd, out IntPtr terminal);

View File

@ -11,7 +11,6 @@ namespace Microsoft.Terminal.Wpf
using System.Windows.Automation.Peers; using System.Windows.Automation.Peers;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
/// <summary> /// <summary>
/// The container class that hosts the native hwnd terminal. /// The container class that hosts the native hwnd terminal.
@ -32,6 +31,11 @@ namespace Microsoft.Terminal.Wpf
/// </summary> /// </summary>
public TerminalContainer() public TerminalContainer()
{ {
// WPF & TSF can't deal with us setting TF_TMAE_CONSOLE on the UI thread.
// It simply crashes on Windows 10 if you use the Emoji picker.
// (On later versions of Windows it just doesn't work.)
NativeMethods.AvoidBuggyTSFConsoleFlags();
this.MessageHook += this.TerminalContainer_MessageHook; this.MessageHook += this.TerminalContainer_MessageHook;
this.GotFocus += this.TerminalContainer_GotFocus; this.GotFocus += this.TerminalContainer_GotFocus;
this.Focusable = true; this.Focusable = true;

View File

@ -16,6 +16,11 @@ Handle Handle::Create()
return handle; return handle;
} }
void Handle::AvoidBuggyTSFConsoleFlags()
{
Implementation::AvoidBuggyTSFConsoleFlags();
}
void Handle::SetDefaultScopeAlphanumericHalfWidth(bool enable) void Handle::SetDefaultScopeAlphanumericHalfWidth(bool enable)
{ {
Implementation::SetDefaultScopeAlphanumericHalfWidth(enable); Implementation::SetDefaultScopeAlphanumericHalfWidth(enable);

View File

@ -33,6 +33,7 @@ namespace Microsoft::Console::TSF
struct Handle struct Handle
{ {
static Handle Create(); static Handle Create();
static void AvoidBuggyTSFConsoleFlags();
static void SetDefaultScopeAlphanumericHalfWidth(bool enable); static void SetDefaultScopeAlphanumericHalfWidth(bool enable);
Handle() = default; Handle() = default;

View File

@ -27,9 +27,45 @@ static void TfPropertyvalClose(TF_PROPERTYVAL* val)
} }
using unique_tf_propertyval = wil::unique_struct<TF_PROPERTYVAL, decltype(&TfPropertyvalClose), &TfPropertyvalClose>; using unique_tf_propertyval = wil::unique_struct<TF_PROPERTYVAL, decltype(&TfPropertyvalClose), &TfPropertyvalClose>;
// The flags passed to ActivateEx don't replace the flags during previous calls.
// Instead, they're additive. So, if we pass flags that some concurrently running
// TSF clients don't expect we may blow them up accidentally.
//
// Such is the case with WPF (and TSF, which is actually at fault there).
// If you pass TF_TMAE_CONSOLE it'll instantly crash on Windows 10 on first text input.
// On Windows 11 it'll at least not crash but still make emoji input completely non-functional.
//
// --------
//
// In any case, we pass the same flags as conhost v1:
// - TF_TMAE_UIELEMENTENABLEDONLY: TSF activates only text services that are
// categorized in GUID_TFCAT_TIPCAP_UIELEMENTENABLED.
// - TF_TMAE_NOACTIVATEKEYBOARDLAYOUT: TSF does not sync the current keyboard layout
// while this method is called. The keyboard layout will be adjusted when the
// calling thread gets focus. This flag must be used with TF_TMAE_NOACTIVATETIP.
// - TF_TMAE_CONSOLE: A text service is activated for console usage.
// Some IMEs are known to use this as a hint. Particularly a Korean IME can benefit
// from this, because Korean relies on "recomposing" previously finished compositions.
// That can't work in a terminal, since we submit composed text to the shell immediately.
//
// I'm not sure what TF_TMAE_UIELEMENTENABLEDONLY does. I tried to figure it out but failed.
//
// For TF_TMAE_NOACTIVATEKEYBOARDLAYOUT, I'm 99% sure it doesn't do anything, including in
// conhost v1. This is because IMM will be initialized on WM_ACTIVATE, which calls ActivateEx(0).
// Any subsequent ActivateEx() calls will update the flags, _except_ for this one and
// TF_TMAE_NOACTIVATETIP which are explicitly filtered out.
//
// TF_TMAE_NOACTIVATETIP however is important. Without it, TIPs are immediately initialized.
static std::atomic<DWORD> s_activationFlags{ TF_TMAE_NOACTIVATETIP | TF_TMAE_UIELEMENTENABLEDONLY | TF_TMAE_NOACTIVATEKEYBOARDLAYOUT | TF_TMAE_CONSOLE };
void Implementation::AvoidBuggyTSFConsoleFlags() noexcept
{
s_activationFlags.fetch_and(~static_cast<DWORD>(TF_TMAE_CONSOLE), std::memory_order_relaxed);
}
static std::atomic<bool> s_wantsAnsiInputScope{ false };
void Implementation::SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept void Implementation::SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept
{ {
_wantsAnsiInputScope.store(enable, std::memory_order_relaxed); s_wantsAnsiInputScope.store(enable, std::memory_order_relaxed);
} }
void Implementation::Initialize() void Implementation::Initialize()
@ -40,7 +76,7 @@ void Implementation::Initialize()
// There's no point in calling TF_GetThreadMgr. ITfThreadMgr is a per-thread singleton. // There's no point in calling TF_GetThreadMgr. ITfThreadMgr is a per-thread singleton.
_threadMgrEx = wil::CoCreateInstance<ITfThreadMgrEx>(CLSID_TF_ThreadMgr, CLSCTX_INPROC_SERVER); _threadMgrEx = wil::CoCreateInstance<ITfThreadMgrEx>(CLSID_TF_ThreadMgr, CLSCTX_INPROC_SERVER);
THROW_IF_FAILED(_threadMgrEx->ActivateEx(&_clientId, TF_TMAE_CONSOLE)); THROW_IF_FAILED(_threadMgrEx->ActivateEx(&_clientId, s_activationFlags.load(std::memory_order_relaxed)));
THROW_IF_FAILED(_threadMgrEx->CreateDocumentMgr(_documentMgr.addressof())); THROW_IF_FAILED(_threadMgrEx->CreateDocumentMgr(_documentMgr.addressof()));
TfEditCookie ecTextStore; TfEditCookie ecTextStore;
@ -319,7 +355,7 @@ STDMETHODIMP Implementation::GetWnd(HWND* phwnd) noexcept
STDMETHODIMP Implementation::GetAttribute(REFGUID rguidAttribute, VARIANT* pvarValue) noexcept STDMETHODIMP Implementation::GetAttribute(REFGUID rguidAttribute, VARIANT* pvarValue) noexcept
{ {
if (_wantsAnsiInputScope.load(std::memory_order_relaxed) && IsEqualGUID(rguidAttribute, GUID_PROP_INPUTSCOPE)) if (s_wantsAnsiInputScope.load(std::memory_order_relaxed) && IsEqualGUID(rguidAttribute, GUID_PROP_INPUTSCOPE))
{ {
_ansiInputScope.AddRef(); _ansiInputScope.AddRef();
pvarValue->vt = VT_UNKNOWN; pvarValue->vt = VT_UNKNOWN;

View File

@ -16,6 +16,7 @@ namespace Microsoft::Console::TSF
struct Implementation : ITfContextOwner, ITfContextOwnerCompositionSink, ITfTextEditSink struct Implementation : ITfContextOwner, ITfContextOwnerCompositionSink, ITfTextEditSink
{ {
static void AvoidBuggyTSFConsoleFlags() noexcept;
static void SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept; static void SetDefaultScopeAlphanumericHalfWidth(bool enable) noexcept;
virtual ~Implementation() = default; virtual ~Implementation() = default;
@ -129,6 +130,5 @@ namespace Microsoft::Console::TSF
int _compositions = 0; int _compositions = 0;
AnsiInputScope _ansiInputScope{ this }; AnsiInputScope _ansiInputScope{ this };
inline static std::atomic<bool> _wantsAnsiInputScope{ false };
}; };
} }