100 Commits
Author SHA1 Message Date
Leonard Hecker c91793f7dc Fix a WriteCharsLegacy deadlock (#20862)
The line wrapping check was busted. This new code
is a 1:1 copy from `AdaptDispatch::_WriteToBuffer`.

Closes #20693
2026-10-27 22:58:39 +00:00
Leonard Hecker 21d1f1e617 Fix several scrollbar issues in conhost (#20824)
- Closes #19585, by always tracking the main buffer for scrollbar state,
  instead of letting `SIF_DISABLENOSCROLL` track it implicitly
- Optimized scrollbar state on startup by setting its visibility eagerly
- Fixed a transient full-height thumb on return from the alt buffer

And a real zinger (decade old bug, basically):
- Fixed a bug where the alt buffer doesn't resize if its size matches
  the main buffer (technically unrelated to scrollbars)
2026-10-22 22:27:01 -05:00
Leonard Hecker 5951f69654 Fix several ScrollConsoleScreenBuffer ConPTY issues (#20774)
- Fixed #20772 by fully committing to lazy DA1 capabilities path via
  `VtIo::SetDeviceAttributes`. It accidentally only did so with enabled
  cursor inheritance before.
- Fixed #20773 by fixing the source/destination rectangle alignment
  in the non-DECCRA path of `ScrollConsoleScreenBuffer`.

Also closes MSFT-64450466.

As a drive-by:
- Fixed the DECCRA path not restoring cursor attributes in conhost.
- Reduced memory use in the non-DECCRA path.
- Added a `IL/DL` sequence fast-pass for `ScrollConsoleScreenBuffer`.
- Fixed a broken bounds checks in `Read/WriteConsoleOutputWImplHelper`.
- Fixed the TraceLogging definition for `SMALL_RECT`.
2026-10-08 01:16:58 +02:00
Leonard Hecker 2d417e4eda Minor improvements to til::parse_(un)signed (#20778)
* Allow an explicit `+` prefix like other parsers
* Reject invalid bases
* Fix UB during `uint64_t` -> `T` cast in `parse_signed`
2026-10-07 17:27:02 -05:00
Leonard Hecker b7752edc52 Restore KKP flags when disabling ASB (#20768)
`_kittyFlags` represents the current buffer flags. When you enable the
ASB, we reset `_kittyFlags` to 0. When you disable the ASB, we restore
the flags from the `_kittyMainStack`. Boom, broken. We never pushed
anything into the `_kittyMainStack` in the first place.

The gets rid of `_kittyFlags`. To make it neat, I also replaced
the heap vector with a static array + length.

Closes #20763
2026-10-06 12:56:34 -05:00
Leonard Hecker 72e7b0630c Avoid loading keyboard layouts for KKP US mappings (#20769)
Replaces `LoadKeyboardLayoutW` with a static LUT.
No more random US keyboards in your language picker.

Closes #20761
2026-10-06 16:31:19 +02:00
Leonard Hecker 0626dc1457 Fix 5s lags when using OSC 52 clipboard copy (#20728)
Hilarious repro: `printf "\e]0;A\a\e]52;c;Qg==\a"`

This issue is exactly what happens when you Ctrl+X in a clean file in
msedit and it freezes Windows Terminal. It's because it puts a "•" in
the title and copies the text.

It triggers a title and clipboard change in the same frame which causes
the UI and VT thread to deadlock trying to get the console lock.
2026-09-28 15:11:15 +00:00
Leonard Hecker fda72a0709 Fix several Present issues in AtlasEngine (#20723)
* D3D uses deferred execution where render commands are collected as
  descriptors in a ring buffer before they're sent off to the GPU.
  When you don't call `Flush()` (or `Present()` which implies `Flush()`)
  it'll fill up 100s of MB of memory rapidly.
  This PR solves it in two ways:
  * Call `Flush()` whenever we can't call `Present()`
  * Make calling `Flush()` unnecessary by checking if we need to render
    anything in the first place.
* This immediately results in an issue: Not rendering means the frame
  latency waitable object yields immediately (because we can't wait on
  it twice). The solution is to wait 16ms before trying again.
* If the display is off, DWM will not deque frames. `Present1()` will
  indicate this (`DXGI_STATUS_OCCLUDED`) but it's not a failure so we
  don't handle it. Because of this we'll fill the queue to its maximum
  (64 frames). Once the display is on again, DWM will deque at V-Sync
  rate, but since we render as fast as the frame latency waitable
  object, also signaled at V-Sync rate, we'll push frames at the same
  rate they're dequeued, causing a 64-frame latency (~1.1s at 60Hz).
  `SetMaximumFrameLatency(1)` doesn't change this.
  The solution is that we don't limit waiting on the frame latency
  waitable object to just 100ms, but `INFINITE`.
* Since that fix causes the renderer to wait potentially forever,
  a mechanism to cancel waits on shutdown is necessary.
  This also uncovered a flaw in the current design, wherein it blocks
  the UI thread for up to 100ms every time you close or move a tab
  on a low frame rate (or no active) display.

Closes #20226
Closes #20342
Closes #20716

## Validation Steps Performed
* Continuously render, scroll up -> no GPU use ✅
2026-09-26 00:59:39 +00:00
Leonard Hecker d3b8ed33e6 Introduce an AI Usage Policy (#20654)
After quite a lot of deliberation today, I think this is how I would
personally define it. I tried to keep it short and open to AI-use,
but particularly restrictive to autonomous AI-use (= typically slop).
2026-09-11 02:24:53 +02:00
Leonard Hecker 385c0c75f7 Avoid window events during ASB (#20617)
Important context: To make a window smaller programmatically,
the win32 console APIs have since forever required you to
* narrow the viewport
* then narrow the buffer

To make the window larger you had to
* enlarge the buffer
* then enlarge the viewport

This is because a viewport had to fit into the buffer and vice versa.
ConEmu was very popular in the past and used this to implement proper
terminals around conhost. When WSL came along this resulted in a
problem: It wants SIGWINCH-style notifications. And there are none
on Windows. A long time ago, a hack was introduced that simply
raised buffer size events on any viewport size changes.

The problem: It was raised for _any_ viewport size changes.
And it was also raised for ASB switching.

Problema numero due: OpenSSH for WIndows is like WSL, but it has a
severe bug: A SIGWINCH during a partial input read will corrupt the
read and the other side will read either garbage or duplicate input.

Well, we can fix that by just not raising buffer size events.
It's very dangerous, because it may easily regress ConEmu (etc.).

See: https://github.com/PowerShell/Win32-OpenSSH/issues/2275

Also: Fun fact, we've been ignoring `ENABLE_WINDOW_INPUT`
since Windows 1607! No way fixing this breaks anything! /s

So, this PR introduces two **regressions**:
* `ENABLE_WINDOW_INPUT` is being respected again
* `WINDOW_BUFFER_SIZE_EVENT` is not sent anymore
  when the viewport changes if VT mode disabled

Changes:
* Reintroduce support for `ENABLE_WINDOW_INPUT`
* Coalesce buffer size events
* Avoid such events when switching ASB on/off
* Ensure we reliably raise the events for viewport size changes

This is part of #281.

## Validation Steps Performed
* Spawn OpenConsole
* Run `ConEmuC64.exe -AUTOATTACH -GHWND=NEW`
* Run `wsl -- bash`
* Run `while stty size; do read -rt .05 ||:; done`
* Narrowing the window reduces the WSL width ✅
* Widening the window increases the WSL width ✅
2026-09-10 19:21:40 -05:00
Leonard Hecker 04f525cb37 Add a note about HWND_MESSAGE (#20656)
Forgot about this tidbit from #17829.
2026-09-08 19:55:17 +00:00
Leonard Hecker 20588130d8 Fix two KKP ReportEventTypes issues (#20551)
* Dead key releases should not be reported
* Modifier key releases should only be reported
  if `ReportAllKeysAsEscapeCodes` is also set
* (Also fixes encoding for numpad Enter)

Closes #20522

## Validation Steps Performed
* Switch to Swiss French (`0000100c`) layout
* Enable modes 0b11 (`DisambiguateEscapeCodes`, `ReportEventTypes`)
* Pressing `Shift+]` produces `!` ✅
* Pressing `] ] ] A` produces `¨¨ä` ✅
2026-08-19 00:23:48 +02:00
Leonard Hecker e907f9fb75 Fix visual artifacts when using QQ Wubi IME (#20542) 2026-08-13 22:13:27 +02:00
Leonard Hecker bfc054685c ConPTY: Do not wait for DA1 on startup (#20536)
A counterpart to #20510. After this commit you can spawn ConPTY
again without handling any queries and it'll (mostly) just work.
2026-08-12 17:49:11 -05:00
Leonard Hecker a47fb5d070 Do not retry a DSR-CPR if the ConPTY host did not respond (#20510)
People sometimes use ConPTY just for console API translation and so the
host lacks any VT capabilities. This means DSR-CPR queries never get a
response. Previously we would never clear the dirty flag which would
result in a 500ms lag on each `GetConsoleScreenBufferInfo` call and
PowerShell calls that API _**on every single stdout print**_
(sometimes per character due to lack of any stdout buffering).

## Validation Steps Performed
* Lobotomize conhost's ability to respond to DSR-CPR
* Run OpenConsole --> VtPipeTerm --> pwsh
* Resize the window
* After this PR, stdio will not lag
2026-08-05 12:40:58 -05:00
Leonard Hecker e74649d5f1 Fix KKP auto-repeat events (#20500)
Turns out, ReportEventTypes does not control autorepeat.

Closes #20499
2026-08-03 17:14:39 +02:00
Leonard Hecker fbda436dc6 Fix various KKP encoding issues (#20052)
NOTE:
This commit changes our input model to treat all
Ctrl-Alt combinations as an AltGr modifier by default.
Ctrl+Alt+/ will not produce `"\x1b\x1f"` anymore.

This fixes the following issues:
* Base-key report of non-ASCII keys was broken
* AltGr on international keyboards would spam
  Ctrl/Alt up/down events repeatedly forever
* KKP doesn't report modifier key repeats
* AltGr+Key combinations were reported as Ctrl+Alt+key
  but should've been just plain Key-But-Modified

Closes #19977

## Validation Steps Performed
This cannot be easily tested in unit tests, due to
the dependence on international keyboard layouts.
It appears to work correctly from what I can tell.
2026-07-31 18:14:39 -05:00
Leonard Hecker 4ce79d7289 Implement OSC 7 for setting the CWD (#20019)
This adds support for OSC 7 which is the same as OSC 9;9 but using
file URIs. As per the previous discussion in #8214, the problem is
that WSL shells emit URIs that can't work because the hostname in
the URI translates to an UNC path with an non-existing hostname.
In my opinion this doesn't deter the fact though that OSC 7 works
just fine for a native Windows application.

In the future we should consider trying to detect WSL file URIs
and translating it to the `--cd` argument for `wsl.exe`.

All of the heavy lifting for parsing the URI is done by 
`PathCreateFromUrlW`. It just had to be plugged into the OSC 9;9 code.

Closes #3158

## Validation Steps Performed
* Launch fish-shell in WSL
* Duplicate tab works ✅
* (And because it doesn't work without using
  `IsValidDirectory` I know that OSC 7 works ✅)
2026-03-30 18:10:26 +00:00
Leonard Hecker da0446a7d1 Send a CPR request on every unknown sequence (#20009)
This PR is 90% wiring up OOP interfaces.

Closes #19926

## Validation Steps Performed
* Run `github.com/xtermjs/vtc`
* Observed `UnknownSequence` calls under a debugger
2026-03-24 23:00:13 +00:00
Leonard Hecker 0f5d883c59 Make disabling the Kitty Keyboard Protocol possible (#19995)
Avoid translating W32IM sequences to KKP.

Closes #19977

## Validation Steps Performed
* Use French Bepo keyboard layout
* Disable KKP
* Use fish shell
* `AltGr+Y` produces `{` ✅
2026-03-19 21:42:15 +01:00
Leonard Hecker ddf514e99e Fix a deadlock during cooked reads (#19994)
Closes #19922

## Validation Steps Performed
* The repro now works as expected ✅
  (`CreatePseudoConsole({1,20})` + `WriteFile("キ")`)
2026-03-19 17:31:04 +00:00
Leonard Hecker 36fb444d3e Improve TSF failure handling (#19965)
An internal AI correctly flagged that we aren't calling
`UnadviseSink()` in case a later `Initialize()` call fails.
We can easily fix this by calling `Uninitialize()`.
2026-03-11 21:31:04 +01:00
Leonard Hecker 30b1456ffe Make TSF initialization fully fallible (#19958)
Apparently, on some (internal) variants of Windows `TF_CategoryMgr`
can exist while `TF_DisplayAttributeMgr` is absent. This is likely
a variant configuration error, but we shouldn't crash anyway.

Closes MSFT-61309810
2026-03-10 21:31:54 +01:00
Leonard Hecker 5bd9e9fd89 Always use the cls shim for cmd/pwsh (#19957)
This improves performance and avoids a memory spike on `cls`.

## Validation Steps Performed
* `cls` clears ✅
* RSS doesn't spike ✅
2026-03-10 21:31:44 +01:00
Leonard Hecker c81c66b406 Implement the Kitty Keyboard Protocol (#19817)
This essentially rewrites `TerminalInput` from scratch.
There's significant overlap between what kind of information
the Kitty protocol needs from the OS and our existing code.
The rewrite allows us to share large parts of the implementation.

Closes #11509

## Validation Steps Performed
* `kitten show-key -m kitty` ✅
* US Intern. " + ' produces `\'` ✅
* Hebrew base keys produce Unicode ✅
* Hebrew AltGr combinations produce Unicode ✅
* French AltGr+Space produces U+00A0 ✅
* German AltGr+Decimals produce []{}... ✅
2026-02-16 19:53:49 -06:00
Leonard Hecker 71409f84f7 Avoid scrolling when the search is open (#19775)
When text scrolls in and out of view, depending on the length
of the scrollback and latency of input, `GetSearchHighlightFocused`
would change which would trigger a `ScrollToSearchHighlight` call.

This PR changes the behavior such that only actively changing the
search mask triggers a search (typing text or pressing enter).

Closes #19754

## Validation Steps Performed
* Brining text in and out of view doesn't scroll ✅
* Toggling the aA button scrolls results into view ✅
2026-01-23 18:33:18 -06:00
Leonard Hecker e8971c80ae Prevent RPC_E_CANTCALLOUT_ININPUTSYNCCALL during WM_SETTINGCHANGE (#19755)
Closes #19505
2026-01-19 11:56:53 -06:00
Leonard Hecker a528141fe6 Use smart pointers in coroutines (#19752)
After the coroutines resume, `this` may have already been destroyed.
So, this PR adds proper use of `get_weak/strong`.

Closes #19745
2026-01-16 15:43:30 -06:00
Leonard Hecker c4557594ce Use til::hstring_format wherever possible (#19733)
A follow-up of #19721 which added `til::hstring_format`.
2026-01-16 15:04:49 -06:00
Leonard Hecker 726c8f0cef Fix preview terminal cursor visibility (#19746)
Closes #19735
2026-01-15 10:58:25 -06:00
Leonard Hecker 58f1f3b312 Remove TF_TMAE_UIELEMENTENABLEDONLY (#19738)
Closes #19670
Closes #19722
2026-01-13 13:59:59 -06:00
Leonard Hecker c0debf8e26 Improve sixel performance (#19639)
Together with #19640 this bumps the performance on my HW by around 60%.
You can now stream 4K images at >60 FPS on a high-end CPU.

## Validation Steps Performed
* Used imagemagick to dump large images ✅
2026-01-12 14:05:33 +01:00
Leonard Hecker 3c2eb594a5 Set the vcpkg toolset to v143 (#19720)
This fixes building with the latest VS2026,
but it's probably a good idea regardless.
2026-01-07 20:27:18 -06:00
Leonard Hecker d05dd85275 Fix a freeze when pasting lots of text (#19721)
WinUI doesn't react well to lots of text,
so this PR caps the max. amount shown.

Related to #9232
2026-01-07 18:03:49 -06:00
Leonard Hecker 8f418d10a6 Clean up SCREEN_INFORMATION even further (#19648)
A continuation of #19645. Maintainer modifications were disabled,
unfortunately.
2025-12-19 16:08:03 -06:00
Leonard Hecker a79078924a Close the TermControl when closing a TerminalPaneContent (#19657)
This fixes an issue where calling close on a tab/pane won't
raise a StateChange notification on the connection.
2025-12-17 18:19:15 +00:00
Leonard Hecker f321c30cc1 Use Char[] for Connection stdin (#19655)
This makes it possible to pass through non-null-terminated strings.
This is needed for the tmux control mode which passes string slices.
2025-12-17 17:25:26 +00:00
Leonard Hecker aefacd6a12 Clean up til::event (#19654)
Dedupe the two structs by using a type alias.
Also cleans up the implicit bool operator, template args, etc.
2025-12-17 17:57:04 +01:00
Leonard Hecker 2f8c51e23d Standardize on Tab::Close for closing tabs (#19656)
This makes it possible to subscribe to `Close` events.
2025-12-17 17:53:41 +01:00
Leonard Hecker a331c42d3c Prevent redundant SetWindowAssociatedProcesses calls (#19658)
This is just an idea. Tmux tabs don't have any associated processes,
so switching between them would raise debug logs all the time.
Not really an issue per se, so I'd be happy to just close this PR.
2025-12-17 17:50:32 +01:00
Leonard Hecker 5f6b814057 Improve performance of til::equals (#19653)
As explained in the added comment. Perf 📈.
2025-12-17 17:49:01 +01:00
Leonard Hecker e707c100e7 Fix cursor blinking before being focused (#19652)
This affects the tmux control mode as it creates
many panes at once, without any involvement of WinUI.
2025-12-17 17:48:47 +01:00
Leonard Hecker 224ac9de47 Stop leaking CPR sequences (#19620)
Due to the `IsVtInputEnabled` early return, we'd skip
the `_captureNextCursorPositionReport` by accident.

Closes #19575
Closes #19609
2025-12-05 21:24:06 +00:00
Leonard Hecker 1ca0c76bc7 Fix a WPF<>TSF crash by avoiding TF_TMAE_CONSOLE (#19584)
As explained in detail in the diff.

Closes #19562
2025-11-24 12:01:21 -06:00
Leonard Hecker 81cdb07646 Separate pruning of elevated/unelevated session buffers (#19546)
Previously, launching an unelevated session after an elevated one would
delete the latter's persisted buffers, and vice versa of course. Also,
elevated buffers didn't have an ACL forbidding access to unelevated
users. That's also fixed now.

Closes #19526

## Validation Steps Performed
* Unelevated/elevated WT doesn't erase each other's buffers ✅
* Old buffers named `buffer_` are renamed to `elevated_` if needed ✅
2025-11-20 11:49:14 -08:00
Leonard Hecker 898b9e3cab Fetch the terminal cursor position after a resize (#19535)
Closes #18725

## Validation Steps Performed
Functionality was observed under a
debugger while using PowerShell 5.
2025-11-18 11:42:09 -08:00
Leonard Hecker 2e78665ee0 Move all blink handling into Renderer (#19330)
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.

## Validation Steps Performed
* Focus/unfocus starts/stops blinking ✅
* OS-wide blink settings apply on focus ✅
2025-11-11 15:29:48 +01:00
Leonard Hecker 5ae95d7df0 Allow creating new windows on another virtual desktop (#19458)
Whoops. Closes #18652

<DHowett> I chatted with Leonard to figure out why I kept
misunderstanding this PR. The key is that **this function should not
always return an existing window.** It's supposed to find an existing
window on the current virtual desktop, not literally any window
anywhere.
2025-10-21 14:08:57 -05:00
Leonard Hecker 4600c4791b Rewrite the MSAA/UIA integration into conhost (#19344)
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. ✅
2025-09-22 22:53:52 +00:00
Leonard Hecker 0aee174e68 Fix behavior of split-pane for existing windows (#19347)
Closes #18815

## Validation Steps Performed
* `wt -w 0 sp` splits the current tab ✅
2025-09-16 13:09:56 -05:00
Leonard Hecker 384932183f Fix a crash in _makeCursorVisible (#19329)
Fixes the crash and also makes `SnapOnOutput` a bit nicer.

Closes #19325

## Validation Steps Performed
* Launch vim in WSL
* Exit
* No crash ✅
2025-09-10 21:21:46 +02:00
Leonard Hecker 4a34a76504 Fix right click on tabs closing them (#19273)
I do not like this.

## Validation Steps Performed
* Enable close buttons on tabs
* Open a tab
* Close the tab with middle click
* Open a tab
* Right click the tab
* Tab doesn't close, Menu opens ✅
2025-09-08 23:16:20 +00:00
Leonard Hecker 8d41ace320 Avoid reentrancy issues when dropping AppHost (#19296)
tl;dr: ~Apphost() may pump the message loop.
That's no bueno. See comments in the diff.

Additionally, this PR enables `_assertIsMainThread` in
release to trace down mysterious crashes in those builds.
2025-09-01 15:33:11 +02:00
Leonard Hecker 7849b00cbd Fix CoreWindow being destroyed after handoff (#19298)
As per: https://github.com/microsoft/terminal/discussions/19280#discussioncomment-14237148

## Validation Steps Performed
* Launch wtd via handoff (spawn cmd, etc.)
* Shift+Click the tab bar + button to create a new window
* Close the initial window
* UI doesn't lock up ✅
2025-09-01 15:32:58 +02:00
Leonard Hecker 5899343237 Fix a race condition around Open/CloseClipboard (#19297)
tl;dr: Open/CloseClipboard are surprisingly not thread-safe.

## Validation Steps Performed
* Copy a large amount of text (>1MB)
* Run `edit.exe`
* Press and hold Ctrl+Shift+V
* Doesn't crash ✅
2025-08-29 20:20:53 +02:00
Leonard Hecker e97388cb27 Fix SnapOnOutput not always snapping (#19247)
`IsOn` is the blinker on/off state, which `IsVisible`
is the actual cursor visibility on/off state.

## Validation Steps Performed
* Run bash/zsh in WSL
* (Repeatedly) Quickly scroll right and press A-Z
* Scrolls to the left ✅
2025-08-15 20:34:29 +00:00
Leonard Hecker 6b19d21845 Fix a output marks performance regression (#19242)
An alternative approach for #18291. Improves perf by ~7%.
2025-08-13 18:10:18 +02:00
Leonard Hecker 8a05910e3c Implement snap-on-input/output for conhost (#17453)
This extends our current behavior in conhost to scroll to the
cursor position when typing. This is especially relevant in WSL,
where this won't happen at all, otherwise.

Closes #18073
Closes MSFT:49027268
2025-08-12 12:22:33 -07:00
Leonard Hecker 0c064905b3 Revert "ConPTY: Emit DSR CPR on resize (#19089)" (#19237)
This reverts commit c55aca508b
because it is unaware of the VT state and may inject the DSR CPR
while e.g. a DCS is going on.

Reopens #18725
2025-08-11 17:48:35 +00:00
Leonard HeckerandDustin L. Howett e2f3e53064 Don't trim bracketed pastes (#19067)
* Moves clipboard writing from `ControlCore` to `TerminalPage`.
  This requires adding a bunch of event types and logic.
  This is technically not needed anymore after changing the
  direction of this PR, but I kept it because it's better.
* Add a `WarnAboutMultiLinePaste` enum to differentiate between
  "paste without warning always/never/if-bracketed-paste-disabled".

Closes #13014
Closes https://github.com/microsoft/edit/issues/279

## Validation Steps Performed
* Launch Microsoft Edit and copy text with a trailing newline
* Paste it with Ctrl+Shift+V
* It's pasted as it was copied ✅
* Changing the setting to "always" always warns ✅

Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
2025-08-08 20:53:42 +00:00
Leonard Hecker 5b41f14660 Disable WIN32IM on shutdown (#19229)
Closes #19153

## Validation Steps Performed
I tried reproducing the issue on Windows 10 and couldn't.
I'm not sure what I did wrong. But I tested it under a
debugger with VtPipeTerm and it wrote the sequences to stdout.
2025-08-08 19:48:52 +02:00
Leonard Hecker 6fb70eb510 Throttle incoming handoffs so WinUI can layout them (#19220)
As explained in the diff itself.

Closes #13136

## Validation Steps Performed
* In conhost, with `wtd` not running, execute:
  ```pwsh
1..10 | % { wt -w 1 nt --title=$_ pwsh -NoExit -Command "Get-Date
-Format HH:mm:ss.ffff" }
  ```
* Every tab is up and running immediately ✅
2025-08-06 18:55:29 +00:00
Leonard Hecker c55aca508b ConPTY: Emit DSR CPR on resize (#19089)
This will help terminals with a reflow behavior unlike
the one implemented in ConPTY, such as VS Code.

Closes #18725

## Validation Steps Performed
* Tested under a debugger ✅
* Use PowerShell 5 and reflow lines in the ConPTY buffer until they're
  pushed outside the top viewport. Then type something in the prompt.
  Cursor is in a consistent position, even if slightly off. ✅
2025-08-06 13:06:44 +02:00
Leonard Hecker 0a6394270e Fix WSL PATH corruption & potential use-after-free (#19211)
Closes #19152

## Validation Steps Performed
* Set `PATH` on a linux profile
* `PATH` isn't messed up inside WSL ✅
2025-08-04 22:42:26 +00:00
Leonard Hecker dbf740cf2c Extend lead/trail edge support for throttled_func (#19210)
You can now create throttled functions which trigger both on the leading
and trailing edge. This was then also ported to `ThrottledFunc` for
`DispatcherQueue`s and used for title/taskbar updates.

Closes #19188

## Validation Steps Performed
* In CMD run:
  ```batch
  FOR /L %N IN () DO @echo %time%
  ```
* Doesn't hang the UI ✅
2025-08-04 20:25:37 +02:00
Leonard Hecker 9ab7cf312f Remove dependency on .NET 6 and Framework 4.8 (#19199)
* .NET 6 is EOL
* Framework 4.7.2 comes by default with VS, but 4.8 doesn't
2025-08-04 13:09:22 -05:00
Leonard Hecker dfcc8f3c62 Fix use-after-free when disabling the ASB (#19186)
Closes #17515

## Validation Steps Performed
* Disable the ASB while there's a pending cooked read
* Type some text
* No crash ✅
2025-07-29 13:48:33 -05:00
Leonard Hecker 0c3002c1b9 Remove ColorHelper (#19187)
Most of `ColorHelper` was unused and one of them had UB.

Related to #19183

## Validation Steps Performed
* Set a custom tabRow theme color
  Split button looks similar to before ✅
* White/black FG color decision for colored
  tabs is similar to before ✅
2025-07-29 11:35:48 -05:00
Leonard Hecker f2b30b4e1e Improve TSF color filtering logic (re: QQPinyin) (#19117)
If an IME provider sets both `crText` and `crBk` we should respect this,
but the previous logic would incorrectly assert for `crLine !=
TF_CT_NONE`.

## Validation Steps Performed
❌ I'm not aware which TSF even sets these colors in a
way that's compatible with us in the first place...
2025-07-14 17:19:10 -05:00
Leonard Hecker ac07afebcb Fix a crash during commandline handoff (#19096)
The crash occurs because WinRT `abort()`s when it encounters
a `std::wstring_view` without null-terminator.

Closes #19093

## Validation Steps Performed
* Set `wtd` as the default terminal
* Launch `cmd`
* Launch `wtd`
* 2 windows ✅
2025-07-03 22:19:12 +00:00
Leonard Hecker 97f0a06fbe Fix another VT input double-encoding issue (#19083)
Closes #17264
Closes https://github.com/microsoft/edit/issues/182

Long shot, but probably also...
Closes #18579
Closes #19082
2025-07-02 16:49:33 -07:00
Leonard Hecker a25d968fe0 Move ConPTY handoff logic into WindowEmperor (#19088)
This changes the ConPTY handoff COM server from `REGCLS_SINGLEUSE`
to `REGCLS_MULTIPLEUSE`. The former causes a race condition, because
handoff runs concurrently with the creation of WinUI windows.
This can then result in the a window getting the wrong handoff.

It then moves the "root" of ConPTY handoff from `TerminalPage`
(WindowEmperor -> AppHost -> TerminalWindow -> TerminalPage)
into `WindowEmperor` (WindowEmperor).

Closes #19049

## Validation Steps Performed
* Launching cmd from the Start Menu shows a "Command Prompt" tab ✅
* Win+R -> `cmd` creates windows in the foreground ✅
* Win+R -> `cmd /c start /max cmd` creates a fullscreen tab ✅
  * This even works for multiple windows, unlike with Canary ✅
* Win+R -> `cmd /c start /min cmd` does not work ❌
  * It also doesn't work in Canary, so it's not a bug in this PR ✅
2025-07-01 19:00:00 +00:00
Leonard Hecker fc0a06c3b6 Preserve the cursor row during Clear Buffer (#18976)
Introduces an ABI change to the ConptyClearPseudoConsole signal.
Otherwise, we have to make it so that the API call always retains
the row the cursor is on, but I feel like that makes it worse.

Closes #18732
Closes #18878

## Validation Steps Performed
* Launch `ConsoleMonitor.exe`
* Create some text above & below the cursor in PowerShell
* Clear Buffer
* Buffer is cleared except for the cursor row ✅
* ...same in ConPTY ✅
2025-06-25 16:31:28 +00:00
Leonard Hecker b47fdfc7e6 Fix support for the Tencent QQPinyin IME (#19046) 2025-06-16 23:52:26 +00:00
Leonard Hecker 26cd15a14b Eagerly persist on WM_ENDSESSION (#18912)
Once an application has returned from handling `WM_ENDSESSION`
the OS may kill the app at any point. This means we must
persist our state while inside the message handler.

Closes #17179

## Validation Steps Performed

Honestly, none. It's a race condition in the first place.
2025-05-22 18:02:58 -05:00
Leonard Hecker 076746a7a6 Implement custom text context menus to fix crashes (#18854)
This works around a bug in WinUI where it creates a single context
menu/flyout for text elements per thread, not per `XamlRoot`, similar to
many other areas. Since the `XamlRoot` cannot change after creation,
this means that once you've opened the flyout, you're locked into that
window (= XAML root) forever. You can't open the flyout in another
window and once you've closed that window, you can't open it anywhere at
all.

Closes #18599

## Validation Steps Performed
* Flies out right click in the
  * About dialog ✅
  * Search dialog ✅
  * Word delimiters setting ✅
  * Launch size setting ✅
* Across two windows ✅
2025-05-14 21:47:23 +02:00
Leonard Hecker 4d094df508 Fix one source of mouse vanishing (#18911)
When the cursor is over the non-client area it wouldn't
show the cursor when moving it. Now it works as expected.
2025-05-14 17:25:20 +00:00
Leonard Hecker 69fc2d94ee Clean up persisted windows after the feature is disabled (#18910)
I finally realized the missing piece.
We need to clear the state array unconditionally,
as otherwise it won't get cleared. Duh.

Closes #18584

## Validation Steps Performed
* Craft a state.json with a layout
* Launch Terminal while the feature is disabled
* state.json is cleaned up on exit ✅
2025-05-14 19:04:13 +02:00
Leonard Hecker a2d80682c9 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 ✅
2025-04-24 12:22:30 -05:00
Leonard Hecker 2992421761 Fix a major stdin wakeup race condition (#18816)
The conhost v2 rewrite from a decade ago introduced a race condition:
Previously, we would acquire and hold the global console lock while
servicing
a console API call. If the call cannot be completed a wait task is
enqueued,
while the lock is held. The v2 rewrite then split the project up into a
"server" and "host" component (which remain to this day). The "host"
would
hold the console lock, while the "server" was responsible for enqueueing
wait
tasks _outside of the console lock_. Without any form of
synchronization,
any operations on the waiter list would then of course introduce a race
condition. In conhost this primarily meant keyboard/mouse input, because
that
runs on the separate Win32 window thread. For Windows Terminal it
primarily
meant the VT input thread.

I do not know why this issue is so extremely noticeable specifically
when we
respond to DSC CPR requests, but I'm also not surprised: I suspect that
the
overall performance issues that conhost had for a long time, meant that
most
things it did were slower than allocating the wait task.
Now that both conhost and Windows Terminal became orders of magnitudes
faster
over the last few years, it probably just so happens that the DSC CPR
request
takes almost exactly as many cycles to complete as allocating the wait
task
does, hence perfectly reproducing the race condition.

There's also a slight chance that this is actually a regression from my
ConPTY
rewrite #17510, but I fail to see what that would be. Regardless of
that,
I'm 100% certain though, that this is a bug that has existed in v0.1.

Closes #18117
Closes #18800

## Validation Steps Performed
* See repro in #18800. In other words:
  * Continuously emit DSC CPR sequences
  * ...read the response from stdin
  * ...and print the response to stdout
  * Doesn't deadlock randomly anymore ✅
* Feature & Unit tests ✅
2025-04-23 15:27:21 -05:00
Leonard Hecker 773a4b9198 Implement DECSET 2026 - Synchronized Output (#18826) 2025-04-23 12:15:51 -05:00
Leonard Hecker 3accdcfc6b Fix cwd not applying on launch (#18801) 2025-04-15 20:22:09 +00:00
Leonard Hecker 354e05d713 Fix CRLF translation when DISABLE_NEWLINE_AUTO_RETURN is reset (#18781)
We can't do the `pos.x != 0` check. Instead, I replaced it with
a CR check to avoid redundant CRs during CRLF translation.

Closes #18735

## Validation Steps Performed
* Run the repro in the linked issue
2025-04-15 02:57:30 +02:00
Leonard Hecker 8b01f546cb Backup and restore attributes during cooked reads (#18797)
Use DECSC/DECRC and XTPUSHSGR/XTPOPSGR while redrawing
popups, since they're drawn using the current popup colors.

I wish we could just use the reverse video rendition...

Closes #18742

## Validation Steps Performed
* Run `color 3f` and then press F7
* Works fine in conhost (VtPipeTerm) ✅
* Works as expected (black background) in VS Code ✅
2025-04-14 23:59:46 +00:00
Leonard Hecker 90c312f7da Fix wide char support for WriteConsoleOutputAttribute (#18796)
When we overwrite the attributes during the fill,
we must retain the lead/trail byte attributes.

Closes #18746

## Validation Steps Performed
* Added a unit test ✅
2025-04-14 14:12:01 -07:00
Leonard Hecker 70f85a4a35 Fix a shutdown race condition in ControlCore (#18632)
I found multiple issues while investigating this:
* Render thread shutdown is racy, because it doesn't actually stop the
render thread.
* Lifetime management in `ControlCore` failed to account for the
circular dependency of render thread --> renderer --> render data -->
terminal --> renderer --> render thread. Fixed by reordering the
`ControlCore` members to ensure their correct destruction.
* Ensured that the connection setter calls close on the previous
connection.

(Hopefully) Closes #18598

## Validation Steps Performed
* Can't repro the original failure ❌
* Opening and closing tabs as fast as possible doesn't crash anymore ✅
* Detaching and reattaching a tab producing continuous output ✅
2025-03-14 15:06:01 -07:00
Leonard Hecker 32ae00f71a Fix a ConPTY startup hang with 0-param DA1 responses (#18681)
Since `WaitForDA1` would wait until `_deviceAttributes` is non-zero,
we must ensure it's actually non-zero at the end of this handler,
even if there are no parameters.

## Validation Steps Performed
* Mod the Terminal DA1 to be `\x1b[?6c`. No hang ✅
* Mod the Terminal DA1 to be `\x1b[?61c`. No hang ✅
2025-03-12 16:29:36 -07:00
Leonard Hecker 2693210ead Fix a handoff deadlock if layout completes synchronously (#18676)
I've received a dump from an affected user, and it showed that the
layout event in TerminalPage was raised synchronously. This meant that
during page initialization, the handoff listener was started while still
being stuck inside the handoff listener. This resulted in a deadlock.

This PR fixes the issue by not holding the lock across handoff callback
calls.

Closes #18634

## Validation Steps Performed
* Can't repro ❌
2025-03-12 16:27:12 -07:00
Leonard Hecker 3760caed97 Fix dialogs not working across multiple windows (#18636)
This can be considered "part 1" of fixing #18599: It prevents crashes
(due to unhandled exceptions) by ensuring we only create 1 content
dialog across all windows at a time. Sounds bad, but I tried it and it's
not actually _that_ bad in practice (it's still really gross though).

The bad news is that I don't have a "part 2", because I can't figure out
what's going on:
* Create 2 windows
* Open the About dialog in window 1
  and right click the text
* Close the About dialog
* Open the About dialog in window 2
  and right click the text
* WinUI will simply toss the focus to window 1

It appears as if context menus are permanently associated with the first
window that uses them. It has nothing to do with whether a ContentDialog
instance is reused (I tested that).

## Validation Steps Performed
* Open 2 windows with 2 tabs each
* Attempt to close window 1, dialog appears ✅
* Attempt to close window 2, dialog moves to window 2 ✅
2025-02-28 15:14:16 -08:00
Leonard Hecker 96d1407c59 Fix bugs introduced in #18623 (#18635)
The logic didn't work when persistence was enabled and you had 2 windows
and closed the 2nd one, or when dragging the last tab out of the only
window.

## Validation Steps Performed
* 2 windows, close the 2nd one, app doesn't exit ✅
* 1 window, 1 tab, drag the tab out of the window, app doesn't exit ✅
2025-02-28 23:19:35 +01:00
Leonard Hecker e1be2f4c73 Fix persistence of the last closed window (#18623)
Does what it says on the tin.

Closes #18525

## Validation Steps Performed
* Enable persistence
* Close the last window
* Persisted ✅
2025-02-26 11:05:59 -08:00
Leonard Hecker e1b28e72b3 Fix panes being dropped when tearing off tabs (#18627)
I don't actually know why this is happening, because it doesn't
happen with startup actions specified in the settings file.
In any case, it's fixed with more delays.

Closes #18572

## Validation Steps Performed
* Create a tab with 2 panes
* Tear it off into a new window
* New window has 1 tab with 2 panes ✅
2025-02-26 10:58:34 -08:00
Leonard Hecker ff9664d2d4 Fix two sources of runtime exceptions (#18628)
* `_ApplyLanguageSettingChange` calls `PrimaryLanguageOverride`
  (the WinRT API function) and we would call it every time a new
  window is created. Now it's only called on settings load.
* `_RegisterTabEvents` would listen for "Content" changes which can
  be null. `IVector::Append` throws if a null object is given.
  In our case, it's null if the content got erased with nothing.

Additionally, this fixes a bug where we wouldn't call
`_ProcessLazySettingsChanges` on startup. This is important if the
settings file was changed while Windows Terminal wasn't running.

Lastly, there's a lifetime fix in this PR, which is a one-line change
and I didn't want to make a separate PR for that.
2025-02-25 20:50:25 +01:00
Leonard Hecker 0df82681fe Reduce log spam on conhost exit (#18629)
When the server handle gets closed on conhost (= terminal is gone),
and e.g. PowerShell is used, we would previously log 6 error messages.
This PR reduces it to zero, by removing the 3 biggest offenders.
2025-02-25 20:50:06 +01:00
Leonard Hecker 62e7f4bfad Fix a crash when closing tabs (#18620)
WinUI asynchronously updates its tab view items, so it may happen that
we're given a `TabViewItem` that still contains a `TabBase` which has
actually already been removed. Regressed in #15924.

Closes #18581

## Validation Steps Performed
* Close tabs rapidly with middle click
* No crash ✅
2025-02-24 18:31:32 +00:00
Leonard Hecker 265d841509 Fix persistence of empty windows (#18622)
This is a theoretical fix for #18584 as I cannot reproduce the issue
anymore. It did happen briefly on one of my devices though, and at the
time I observed that it would persist a window with no startup actions.
2025-02-24 18:27:45 +00:00
Leonard HeckerandDustin L. Howett c7f0d0addb Fix leaking the contents of the first tab in the first window (#18621)
Found this one completely randomly.

## Validation Steps Performed
* Open 2 windows with 1 tab each
* Click the X button on the tab in the 1st window
* OpenConsole/etc. is cleaned up ✅

---------

Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
2025-02-24 18:27:36 +00:00
Leonard Hecker 733a5e7bec ConPTY: Fix shutdown if killed during startup (#18588)
During startup we relinquish ownership of the console lock to wait for
the DA1 response of the hosting terminal. The problem occurs if the
hosting terminal disconnects during that time. The broken pipe will
cause `VtIo` to send out `CTRL_CLOSE_EVENT` messages, but those won't
achieve anything, because the first and only client hasn't even finished
connecting yet. What we need to do instead is to return an error code.

In order to not use a bunch of booleans to control this behavior, I gave
`VtIo` a state enum. This however required restructuring the calling
code in order to not have a dozen states.

## Validation Steps Performed
* Launch cmd.exe with ConPTY
* ...but leave the stdin pipe unbound (which will hang the DA1 request)
* Immediately kill the ConPTY session
* cmd.exe exits after clicking away the error message ✅
2025-02-21 16:57:37 -06:00
Leonard Hecker e60acbc12a Add Oklab based color scheme "Ottosson" (#18502)
Campbell has been the default color scheme for a long time now,
but it has quite some issues with hue and chroma.

This PR introduces a new scheme which was created using the Oklab
color space to find colors with maximal distance to each other
and well distributed and consistent hue and chroma.
Because of this, I've named the scheme after the creator of Oklab.
 
Closes #17818
2025-02-03 15:11:27 -08:00
Leonard Hecker 1040035b55 Reduce likelihood of races between stdout and cooked stdin reads (#18326)
As explained in the comment on `_getViewportCursorPosition`, printing
to stdout after initiating a cooked stdin reads is a race condition
between the application and the terminal. But we can significantly
reduce the likelihood of this being obvious with this change.

Related to #18265
Possibly related to #18081

## Validation Steps Performed

Execute the following Go code and start typing:
```go
package main

import (
	"fmt"
	"time"
)

func main() {
	go func() {
		time.Sleep(50 * time.Millisecond)
		fmt.Printf("Here is a prompt! >")
	}()

	var text string
	fmt.Scanln(&text)
}
```

Without this change the prompt will disappear,
and with this change in place, it'll work as expected. ✅
2025-01-27 14:37:04 -08:00