- 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)
- 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`.
`_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
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.
* 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#20226Closes#20342Closes#20716
## Validation Steps Performed
* Continuously render, scroll up -> no GPU use ✅
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).
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 ✅
* 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 `¨¨ä` ✅
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
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.
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 ✅)
This PR is 90% wiring up OOP interfaces.
Closes#19926
## Validation Steps Performed
* Run `github.com/xtermjs/vtc`
* Observed `UnknownSequence` calls under a debugger
Avoid translating W32IM sequences to KKP.
Closes#19977
## Validation Steps Performed
* Use French Bepo keyboard layout
* Disable KKP
* Use fish shell
* `AltGr+Y` produces `{` ✅
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()`.
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
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 []{}... ✅
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 ✅
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 ✅
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.
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 ✅
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 ✅
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.
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. ✅
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 ✅
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.
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 ✅
`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 ✅
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
* 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>
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.
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 ✅
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. ✅
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 ✅
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 ✅
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...
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 ✅
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 ✅
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#18732Closes#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 ✅
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.
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 ✅
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 ✅
`RenderSettings` already stores `DECSCNM` (reversed screen),
so it only makes sense to also store DECSET 2026 there.
## Validation Steps Performed
* Same as in #18826✅
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#18117Closes#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 ✅
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
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 ✅
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 ✅
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 ✅
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 ✅
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 ❌
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 ✅
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 ✅
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 ✅
* `_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.
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.
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 ✅
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.
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>
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 ✅
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
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. ✅