Adds two new commands, `selectOutput` and `selectCommand`. These don't
do much without shell integration enabled, unfortunately. If you do
enable it, however, you can use these commands to quickly navigate the
history to select whole commands (or their output).
Some sample JSON:
```json
{ "keys": "ctrl+shift+<", "command": { "action": "selectCommand", "direction": "prev" } },
{ "keys": "ctrl+shift+>", "command": { "action": "selectCommand", "direction": "next" } },
{ "keys": "ctrl+shift+[", "command": { "action": "selectOutput", "direction": "prev" } },
{ "keys": "ctrl+shift+]", "command": { "action": "selectOutput", "direction": "next" } },
```
**Demo gifs** in
https://github.com/microsoft/terminal/issues/4588#issuecomment-1352042789closes#4588
Tested manually.
<details>
<summary>CMD.exe user? It's dangerous to go alone! Take this.</summary>
Surely, there's a simpler way to do it, this is adapted from my own
script.
```cmd
prompt $e]133;D$e\$e]133;A$e\$e\$e]9;9;$P$e\$e[30;107m[$T]$e[97;46m$g$P$e[36;49m$g$e[0m$e[K$_$e[0m$e[94m%username%$e[0m@$e[32m%computername%$e[0m$G$e]133;B$e\
```
</details>
I noticed this bug while resizing my window on my 150% scale display.
Every 3 "snaps" of the window size, it would fail to resize the text
buffer. I found that this occurs, because we convert the swap chain
size from a float into a double, which converts my 597.333313 height
into 597.33331298828125, which then multiplied by 1.5 results in
895.999969482421875. If you just cast this to an integer, it'll
result in a height of 895px instead of the expected 896px.
This PR addresses the issue in two ways:
* Replace casts to integers with `lrint` or `floor`, etc.
* Remove many of the redundant double <> float conversions.
## PR Checklist
* Resizing my window always resizes the text buffer ✅
Rendering hyperlinks is unneccessarily complex at the moment, because
it requires you to implement `UpdateDrawingBrushes`, manually extract
the hyperlink flag from the given `TextAttribute` and save it until the
next call to `PaintBufferGridLines` which does not get that flag.
This isn't particularly clean as it assumes that `PaintBufferGridLines`
will be called after `UpdateDrawingBrushes` in the first place.
Instead, we can simply pass the hyperlink flag to `UpdateDrawingBrushes`
so that the renderers don't need to deal with this anymore.
## PR Checklist
* Hyperlinks show up with a dotted line ✅
* Hovering hyperlinks underline them ✅
_Lo! Harken to me, for I shall divulge the heart of the tab tear-out
saga. Verily, this PR shall bestow upon thee the power to move tabs and
panes between windows by means of pre-defined actions. Though be warned,
it does not yet grant thee the power to drag and drop them as thou
mayest desire. Yet, the same plumbing that underpins this work shall
remain steadfast. Behold, the majority of this undertaking concerns the
elevation of the RequestMoveContent event from the TerminalPage to the
very summit of the Monarch. From thence, a great AttachContent method
shall descend back to the lowest depths. Furthermore, there are minor
revisions to TermControl that shall enable thee to better detach the
content and attach it to a new one._
This is the most important part of the tab tear-out saga. This PR
enables the user to move tabs and panes between windows using
pre-defined actions. It does _not_ enable the user to drag/drop them
yet, but the same fundamental plumbing will still apply. Most of the PR
is plumbing the `RequestMoveContent` event up from the `TerminalPage` up
to the `Monarch`, and then plumbing an `AttachContent` method back down.
There are also small changes to `TermControl` to better support
detaching the content and attaching to a new one.
For testing, I recommend:
```json
{ "keys": "f1", "command": { "action": "moveTab", "window": "1" } },
{ "keys": "f2", "command": { "action": "moveTab", "window": "2" } },
{ "keys": "f3", "command": { "action": "movePane", "window": "1" } },
{ "keys": "f4", "command": { "action": "movePane", "window": "2" } },
{ "keys": "shift+f3", "command": { "action": "movePane", "window": "1", "index": 3 } },
{ "keys": "shift+f4", "command": { "action": "movePane", "window": "2", "index": 3 } },
```
* Related to #1256
* Related to #5000
---------
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
This regression is caused by 0eff8c0. It previously said `.Y` here.
I went through the diff again and found no other width/height mistake.
Closes#14762Closes#15043
## Summary of the Pull Request
PR adds functionality to enable or disable readOnly mode within panes.
This functionality is different to toggling as if you call the same
functionality twice, it will not toggle between states.
## References and Relevant Issues
- Closes https://github.com/microsoft/terminal/issues/14415
- Documentation https://github.com/MicrosoftDocs/terminal/pull/645
## Validation Steps Performed
- Checked readOnly is enabled when command triggered
- Checked readOnly is enabled when command triggered while read only
already enabled
- Checked readOnly is disabled when command triggered while read only is
enabled
- Checked readOnly stays disabled when command triggered while read only
is disabled
- Checked above with multiple tabs and split panes
## PR Checklist
- [ ] Closes#14415
- [X] Tests added/passed
- [x] Documentation updated
- If checked, please file a pull request on [our docs
repo](https://github.com/MicrosoftDocs/terminal) and link it here:
https://github.com/MicrosoftDocs/terminal/pull/645
- [X] Schema updated (if necessary)
---------
Co-authored-by: Mike Griese <migrie@microsoft.com>
Interesting things we could do after this:
- remove all `InitializeComponent` calls - they do it automatically
- have some Clang support (!)
- use `std::optional`<->`IReference` automatic binding
- use `std::format` support (!) for json/uri/hostname/http stuff/all
`IStringable`s
- potentially move to `/await:strict` for C++20 coroutines
I've also fixed up a couple ambiguities introduced by this change.
Does what it says in the title. After this commit you can customize the height
and width of the terminal's cells. This commit supports parts of CSS'
`<length-percentage>` data type: Font-size relative sizes as multiples (`1.2`),
percentage (`120%`), or advance-width relative (`1.2ch`), as well as absolute
sizes in CSS pixels (`px`) or points (`pt`).
This PR is neither bug free in DxEngine, nor in AtlasEngine.
The former fails to implement glyph advance corrections (for instance #9381),
as well as disallowing glyphs to overlap rows. The latter has the same
overlap issue, but more severely as it tries to shrink glyphs to fit in.
Closes#3498Closes#14068
## Validation Steps Performed
* Setting `height` to `1` creates 12pt tall rows ✅
* Setting `height` to `1ch` creates square cells ✅
* Setting `width` to `1` creates square cells ✅
* Setting `width` or `height` to `Npx` or `Npt` works ✅
* Trailing zeroes are trimmed properly during serialization ✅
* Patching the PR to allow >100 line heights and entering "100.123456"
displays 6 fractional digits ✅
`s/it's/its/`
Note that I didn't touch the several errors in the doc and doc/spec directories, since those seem to be dated and signed email excerpts, and I don't want to violate authorial integrity. Let me know if you would like me to fix those as well.
## References
p 57. Murray, L. (1824). English grammar. Philadelphia : E. T. Scott.
I skimmed several hundred usages of the word "it's" in the code. This actually wasn't as tiresome as it sounds, since many of the code comments in this repo are entertaining and educational — the adjectives do not _necessarily_ apply in that order, but do _possibly_ apply in that order.
When a terminal process exits (successful or not) and the profile isn't
set to automatically close the pane, a new message is displayed:
You can now close this terminal with ^D or Enter to restart.
Ctrl+D then is able to close the pane and Enter restarts it.
I originally tried to do this at the ConptyConnection layer by changing
the connection state from Failed to Closed, but then that didn't work
for the case where the process exited successfully but the profile isn't
set to exit automatically. So, I added an event to
ControlCore/TermControl that Pane watches. ControlCore watches to see if
the input is Ctrl+D (0x4) and if the connection is closed or failed, and
then raises the event so that Pane can close itself. As it turned out, I
think this is the better place to have the logic to watch for the Ctrl+D
key. Doing it at the ConptyConnection layer meant I had to parse out the
key from the escaped text passed to ConptyConnection::WriteInput.
## Validation Steps Performed
Tried adding lots of panes and then killing the processes outside of
Terminal. Each showed the new message and I could close them with Ctrl+D
or restart them with Enter. Also set a profile to never close
automatically to make sure Ctrl+D would work when a process exits
successfully.
Closes#12849Closes#11570Closes#4379
My goal is to make `IRenderData` "snapshottable", so that we
can render a frame of text without holding the console lock.
To facilitate this, this commit merges our 3 data interfaces
into one. It includes no actual changes apart from renames.
My long-term plan is to replace the `CodepointWidth` enum with a simple integer
return value that indicates the amount of columns a codepoint is wide.
This is necessary so that we can return 0 for ZWJs (zero width joiners).
This initial commit represents a cleanup effort around `CodepointWidthDetector`.
Since less code runs faster, this change has the nice side-effect of running
roughly 5-10% faster across the board. It also drops the binary size by ~1.2kB.
## Validation Steps Performed
* `CodepointWidthDetectorTests` passes ✅
* U+26bf (``"`u{26bf}"`` inside pwsh) is a wide glyph
in OpenConsole and narrow one in Windows Terminal ✅
As noted in #11000.
This adds support for `FTCS_COMMAND_START`, `FTCS_COMMAND_EXECUTED` and `FTCS_COMMAND_FINISHED`, which allow a shell to more clearly markup parts of the buffer.
As a trick, I'm also making the `experimental.autoMarkPrompts` setting act like a `FTCS_COMMAND_EXECUTED` if it comes after a `FTCS_COMMAND_START`. This lets the whole sequence work for cmd.exe (which wouldn't otherwise be possible).
* My cmd prompt
```bat
PROMPT $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\[$T]$e[97;46m%_seperator%$P$e[36;49m%_seperator%$e[0m$_$e[0m%_GITPROMPT%$e[94m%username%$e[0m@$e[32m%computername%$e[0m$G$e]133;B$e\
```
* pwsh profile, heavily cribbed from vscode
```pwsh
$Global:__LastHistoryId = -1
function Global:__Terminal-Get-LastExitCode {
if ($? -eq $True) {
return 0
}
# TODO: Should we just return a string instead?
# return -1
if ("$LastExitCode" -ne "") { return $LastExitCode }
return -1
}
function prompt {
# $gle = $LastExitCode
$gle = $(__Terminal-Get-LastExitCode);
$LastHistoryEntry = $(Get-History -Count 1)
# Skip finishing the command if the first command has not yet started
if ($Global:__LastHistoryId -ne -1) {
if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
$out += "`e]133;D`a"
} else {
# Command finished exit code
# OSC 633 ; D [; <ExitCode>] ST
$out += "`e]133;D;$gle`a"
}
}
$loc = $($executionContext.SessionState.Path.CurrentLocation);
# IMPORTANT: Make sure there's a printable charater _last_ in the prompt.
# Otherwise, PSReadline is gonna use the terminating `\` here and colorize
# that if it detects a syntax error
$out += "`e]133;A$([char]07)";
$out += "`e]9;9;`"$loc`"$([char]07)";
$out += "PWSH $loc$('>' * ($nestedPromptLevel + 1)) ";
$out += "`e]133;B$([char]07)";
$Global:__LastHistoryId = $LastHistoryEntry.Id
return $out
}
```
* Doesn't close any issues, because this was always just an element in #11000
* I work here
* From FHL code
This is a follow-up of #13025 to make the members of `til::point/size/rect`
uniform and consistent without the use of `unions`. The only file that has
any changes is `src/host/getset.cpp` where an if condition was simplified.
## Validation Steps Performed
* Host unit tests ✅
* Host feature tests ✅
* ControlCore feature tests ✅
This commit replaces `Utf16Parser` with `<til/unicode.h>` which includes:
* `til::utf16_iterator` as a replacement for `Utf16Parser::Parse`
* `til::utf16_next` as a replacement for `Utf16Parser::ParseNext`
This fixes 2 bugs with `Utf16Parser`:
* Swallowing invalid surrogate pairs instead of turning them into U+FFFD.
* `std::vector<std::vector<wchar_t>>`. It's now >12000% faster.
## Validation Steps Performed
* New unit tests pass ✅
* Searching for narrow/wide characters in conhost works ✅
## Summary of the Pull Request
Ensures that reading the buffer content actually returns the content.
## References
Regressed in #13626.
## PR Checklist
* [x] Closes#14378
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
## Validation Steps Performed
Added a test.
This commit is a from-scratch rewrite of `ROW` with the primary goal to get
rid of the rather bodgy `UnicodeStorage` class and improve Unicode support.
Previously a 120x9001 terminal buffer would store a vector of 9001 `ROW`s
where each `ROW` stored exactly 120 `wchar_t`. Glyphs exceeding their
allocated space would be stored in the `UnicodeStorage` which was basically
a `hashmap<Coordinate, String>`. Iterating over the text in a `ROW` would
require us to check each glyph and fetch it from the map conditionally.
On newlines we'd have to invalidate all map entries that are now gone,
so for every invalidated `ROW` we'd iterate through all glyphs again and if
a single one was stored in `UnicodeStorage`, we'd then iterate through the
entire hashmap to remove all coordinates that were residing on that `ROW`.
All in all, this wasn't the most robust nor performant code.
The new implementation is simple (from a design perspective):
Store all text in a `ROW` in a regular string. Grow the string if needed.
The association between columns and text works by storing character offsets
in a column-wide array. This algorithm is <100 LOC and removes ~1000.
As an aside this PR does a few more things that go hand in hand:
* Remove most of `ROW` helper classes, which aren't needed anymore.
* Allocate backing memory in a single `VirtualAlloc` call.
* Rewrite `IsCursorDoubleWidth` to use `DbcsAttrAt` directly.
Improves overall performance by 10-20% and makes this implementation
faster than the previous NxM storage, despite the added complexity.
Part of #8000
## Validation Steps Performed
* Existing and new unit and feature tests complete ✅
* Printing Unicode completes without crashing ✅
* Resizing works without crashing ✅
My hope with this commit is to make our code more robust against accidental
recursive locking, as well as making it easier to write code with confidence,
with only a slight performance trade-off.
## Validation Steps Performed
* Playing Pac Man music through MIDI ✅
* Windows Terminal runs happily ever after ✅
Silent MIDI notes can be used to seemingly deny a user's input for long
durations (multiple minutes). This commit improves the situation by ignoring
all DECPS sequences for a second when Ctrl+C/Ctrl+Break is pressed.
Additionally it fixes a regression introduced in 666c446:
When we close a tab we need to unblock/shutdown `MidiAudio` early,
so that `ConptyConnection::Close()` can run down as fast as possible.
## Validation Steps Performed
* In pwsh in Windows Terminal 1.16 run ``while ($True) { echo "`e[3;8;3,~" }``
* Ctrl+C doesn't do anything ❎
* Closing the tab doesn't do anything ❎
* With these modifications in Windows Terminal:
* Ctrl+C stops the output ✅
* Closing the tab completes instantly ✅
* With these modifications in OpenConsole:
* Ctrl+C stops the output ✅
* Closing the window completes instantly ✅
If the opacity is set to 100%, the background becomes solid instead of 'fully opaque acrylic'. If the opacity is below 100% the acrylic material is re-enabled (depending on the user's settings).
## Validation Steps Performed
I updated two unit tests to reflect the change in behavior and manually tested the transition from <100% opacity to 100% opacity (and vice versa) on win11.
Steps:
1. Start with 100% opacity and acrylic material enabled.
2. Decrease opacity and observe acrylic effect.
3. Increase opacity back to 100% and disable the acrylic effect.
4. Decrease opacity and notice that acrylic effect is no longer there.
Closes#12880
c0b2f488c1
Unlike iTerm2, we're not planning on making it configurable.
This commit also adds a max length of 1024 characters on the "display URI" that we pass up to XAML, so as to not inundate the UI.
Fixes#14200
After this commit a user may specify fractional font sizes.
Support was only implemented for AtlasEngine however.
DxEngine continues to use rounded (integer) font sizes.
Closes#6678
## Validation Steps Performed
* Install a bitmap font that requires fractional font sizes
(e.g. Terminus TTF, https://files.ax86.net/terminus-ttf/)
* Set font size to something integer (e.g. 14pt)
Glyphs are blurry ✅
* Set font size to something fractional (e.g. 13.5pt)
Glyphs are crisp ✅
- fix: if a selection exists, mark mode should promote the existing selection instead of creating one at the cursor
- fix: mark mode --> move to middle of a word --> ExpandSelectionToWord --> Shift+Right/Left had some weird behavior. Fix that
- fix: Ctrl+enter on random selection clears selection. It should try to treat selection as a URL.
## References
#13854
NOTE: 3b53f3c can be serviced
As noted by the `winrt::event` documentation:
> [...] But for asynchronous events, even after revoking [...], an in-flight
> event might reach your object after it has started destructing.
This is because while adding/removing/calling event handlers might be
thread-safe, there's no guarantee that they run mutually exclusive.
This commit fixes the issue by reverting 6f0f245. Since we never checked
the result of closing a terminal connection anyways, this commit simply drops
the wait on the connection being teared down to ensure #1996 doesn't regress.
Closes#13880
## Validation Steps Performed
* Open tab, close tab, open tab, close tab, open tab, close tab
* ConPTY ✅
* Azure ✅
* Closing a tab with a huge amount of panes ✅
* Opening a bunch of tabs and then closing the window ✅
* Closing a tab while it's busy with VT ✅
* `wtd -w 0 nt cmd /c exit` ✅
* `wtd -w -1 cmd /c exit`
* No WerFault spawns ✅
In testing the following issues were found in AtlasEngine and fixed:
1. "Toggle terminal visual effects" action not working
2. `d2dMode` failed to work with transparent backgrounds
3. `GetSwapChainHandle()` is thread-unsafe due to it being called outside
of the console lock and with single-threaded Direct2D enabled
4. 2 swap chain buffers are less performant than 3
5. Flip-Discard and `Present()` is less energy efficient than
Flip-Sequential and `Present1()`
6. `d2dMode` used to copy the front to back buffer for partial rendering,
but always redraw the entire dirty region anyways
7. Added support for DirectX 9 hardware
8. If custom shaders are used not all pixels would be presented
Closes#13906
## Validation Steps Performed
1. Toggling visual effects runs retro shader ✅
With a custom shader set, it toggles the shader ✅
Toggling `experimental.rendering.software` toggles the shader ✅
2. `"backgroundImage": "desktopWallpaper"` works with D2D ✅ and D3D ✅
3. Adding a `Sleep(3000)` in `_AttachDxgiSwapChainToXaml` doesn't break
Windows 10 ✅ nor Windows 11 ✅
4. Screen animations run at 144 FPS ✅ even while moving the window ✅
5. No weird artefacts during cursor movement or scrolling ✅
6. No weird artefacts during cursor movement or scrolling ✅
7. Forcing DirectX 9.3 in `dxcpl` runs fine ✅
## Summary of the Pull Request
As described in #9583, this change implements the legacy conhost "EnableColorSelection" feature.
## Detailed Description of the Pull Request / Additional comments
@zadjii-msft was super nice and provided the outline/plumbing (WinRT classes and such) as a hackathon-type project (thank you!)--a "SelectionColor" runtimeclass, a ColorSelection method on the ControlCore runtimeclass, associated plumbing through the layers; plus the action-and-args plumbing to allow hooking up a basic "ColorSelection" action, which allows you to put actions in your settings JSON like so:
```json
{
"command":
{
"action": "experimental.colorSelection",
"foreground": "#0f3"
},
"keys": "alt+4"
},
```
On top of that foundation, I added a couple of things:
* The ability to specify indexes for colors, in addition to RGB and RRGGBB colors.
- It's a bit hacky, because there are some conversions that fight against sneaking an "I'm an index" flag in the alpha channel.
* A new "matchMode" parameter on the action, allowing you to say if you want to only color the current selection ("0") or all matches ("1").
- I made it an int, because I'd like to enable at least one other "match mode" later, but it requires me/someone to fix up search.cpp to handle regex first.
- Search used an old UIA "ColorSelection" method which was previously `E_NOTIMPL`, but is now wired up. Don't know what/if anything else uses this.
* An uber-toggle setting, "EnableColorSelection", which allows you to set a single `bool` in your settings JSON, to light up all the keybindings you would expect from the legacy "EnableColorSelection" feature:
- alt+[0..9]: color foreground
- alt+shift+[0..9]: color foreground, all matches
- ctrl+[0..9]: color background
- ctrl+shift+[0..9]: color background, all matches
* A few of the actions cannot be properly invoked via their keybindings, due to #13124. `*!*` But they work if you do them from the command palette.
* If you have "`EnableColorSelection : true`" in your settings JSON, but then specify a different action in your JSON that uses the same key binding as a color selection keybinding, your custom one wins, which I think is the right thing.
* I fixed what I think was a bug in search.cpp, which also affected the legacy EnableColorSelection feature: due to a non-inclusive coordinate comparison, you were not allowed to color a single character; but I see no reason why that should be disallowed. Now you can make all your `X`s red if you like.
"Soft" spots:
* I was a bit surprised at some of the helpers I had to provide in textBuffer.cpp. Perhaps there are existing methods that I didn't find?
* Localization? Because there are so many (40!) actions, I went to some trouble to try to provide nice command/arg descriptions. But I don't know how localization works…
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes#9583
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed *(what would be the right place to add tests for this?)*
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated. *(is this needed?)*
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
## Validation Steps Performed
Just manual testing.
## Summary of the Pull Request
Introduces a new action `expandSelectionToWord()` which expands the beginning and end of the selection to encompass the word(s) it's on. This was implemented as a conditional keybinding where the key chord is passed through to the terminal if no selection is active (similar to `copy()`).
It is not bound to anything by default.
## PR Checklist
* [x] Closes#8274
* [x] Schema updated.
## Validation Steps Performed
- Scenario in #8274:
- search for some text in the find dialog
- ESC to close the dialog
- execute `expandSelectionToWord()`
- the new selection encompasses the whole word
- mark mode
- move onto a word
- execute `expandSelectionToWord()`
- mouse selection (same as above)
- select a portion of two words --> new selection fully encompasses both words
Since locking the console can take a non-trivial amount of time,
the main thread might have already released the ControlCore instance, while the
`UpdatePatternLocations` background thread is holding the last active reference.
If the function call goes out of scope, we destroy the instance, which might
not be safe, considering its members are usually only used by the main thread.
This commit fixes the issue by only holding a reference of the `Terminal`.
## Validation Steps Performed
* Patterns are recognized ✅
See https://github.com/microsoft/terminal/issues/12176#issuecomment-1199488906, and MSFT:39723014.
I have literally no idea how to repro this one, or debug it. The dump I looked at looked like there was a `SwapChainScaleChanged` that was being dispatched as the app was tearing down. The `ControlCore` had started closing, but the `TermControl` hadn't yet. Apparently, just none of the `_refreshSizeUnderLock` callers checked if we were already closing.
All the callers appear to be on the main thread.
Closes#12176
Since there's no real way for me to repro this manually, I'm thinking we fire this fix off to the OS terminal build, where we'll pretty quickly be able to see if this fixed it or not.
## Summary of the Pull Request
Polishes #13405 with some additional feedback found in testing and post-mortem reviews. Such feedback includes:
- [x] ControlInteractivity vs ControlCore split ([link](https://github.com/microsoft/terminal/pull/13405#discussion_r919365435))
- [x] clearing the selection should be under lock when copying text via "Enter"
- [x] move mark mode keybindings into a helper function
- [x] decide if "Enter" should be configurable or non-configurable ([link](https://github.com/microsoft/terminal/pull/13405#discussion_r919379305))
- [x] rename `_isTargetingUrl`
- [x] bugfix: ctrl+enter when the link is outside of the viewport
## References
Original PR: #13405
Relevant issue: #6649
Epic: #4993
## Summary of the Pull Request
This PR moves the key handling for mark mode into a helper method that is then called before an action/key binding is attempted.
## References
Epic: #4993Closes#13533
## Validation Steps Performed
- Add custom keybinding to "down" arrow key
- in mark mode --> selection updates appropriately
- out of mark mode --> keybinding executed
In #13024, we removed `Terminal::GetCursorPosition` from TerminalCore.
This has been widely regarded as a good move.
Now, you might rightly be wondering: why didn't compilation immediately
fail? Well. It turns out that there were _two_ copies of
`GetCursorPosition`. One for `const Terminal`, and one for `Terminal`.
This is important.
`Terminal::GetCursorPosition()` returned the cursor position relative to
the viewport. `Terminal::GetCursorPosition() const`, however, returns
the cursor position in absolute.
We removed the non-`const` one. Fortunately, thanks to the lookup rules
for `const`-qualified members, this didn't matter. Code that called
`GetCursorPosition()` still called `GetCursorPosition()`, and everything
was fine.
Except that part about the relative coordinates. That was not fine.
The TSF control is the _only_ consumer of `ControlCore.CursorPosition`,
and that was the _only_ consumer of relative-`GetCursorPosition()`.
This commit restores equilibrium by introducing a new
`GetViewportRelativeCursorPosition()` member to `Terminal` and switching
over the only consumer of relative cursor position to use it.
Closes#13769.
Repurpose `Feature_AtlasEngine` to enable the atlas engine by default in Dev and Preview builds.
Introduce `Feature_ConhostAtlasEngine` to solely control atlas engine inclusion in conhost.
Closes#13745
We have a number of theories why #12607 is happening, one of which is that
some GPU drivers somehow rely on Win32 messages or similar which we process
on the main thread. If we then try to acquire the console lock on the main
thread, while the GPU-driver thread itself is holding that lock, we've got
ourselves a deadlock. This PR makes this less likely by running the repeat
offender `UpdatePatternLocations` on a background thread instead.
We have a number of other locations which acquire the console lock on the
main thread and a thorough bug fix must be done in a different way.
## Validation Steps Performed
* After pasting an URL it gets underlined on hover ✅
## Summary of the Pull Request
The original `DECPS` implementation made use of the Windows MIDI APIs to
generate the sound, but that required a 3MB package dependency for the
GS wavetable DLS. This PR reimplements the `MidiAudio` class using
`DirectSound`, so we can avoid that dependency.
## References
The original `DECPS` implementation was added in PR #13208, but was
hidden behind a velocity flag in #13258.
## PR Checklist
* [x] Closes#13252
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. Issue number
where discussion took place: #13252
## Detailed Description of the Pull Request / Additional comments
The way it works is by creating a sound buffer with a single triangle
wave that is played in a loop. We generate different notes simply by
adjusting the frequency at which that buffer is played.
When we need a note to end, we just set the volume to its minimum value
rather than stopping the buffer. If we don't do that, the repeated
starting and stopping tends to produce a lot of static in the output. We
also use two buffers, which we alternate between notes, as another way
to reduce that static.
One other thing worth mentioning is the handling of the buffer position.
At the end of each note we save the current position, and then use an
offset from that position when starting the following note. This helps
produce a clearer separation between tones when repeating sequences of
the same note.
In an ideal world, we should really have something like an attack-decay-
sustain-release envelope for each note, but the above hack seems to work
reasonably well, and keeps the implementation simple.
## Validation Steps Performed
I've manually tested both conhost and Terminal with the sample tunes
listed in issue #8687, as well as a couple of games that I have which
make use of `DECPS` sound effects.
## Summary of the Pull Request
Adds support to navigate to clickable hyperlinks using only the keyboard. When in mark mode, the user can press [shift+]tab to go the previous/next hyperlink in the text buffer. Once a hyperlink is selected, the user can press <kbd>Ctrl+Enter</kbd> to open the hyperlink.
## References
#4993
## PR Checklist
* [x] Closes#6649
* [x] Documentation updated at https://github.com/MicrosoftDocs/terminal/pull/558
## Detailed Description of the Pull Request / Additional comments
- Main change
- The `OpenHyperlink` event needs to be piped down to `ControlCore` now so that we can open a hyperlink at that layer.
- `SelectHyperlink(dir)` searches the buffer in a given direction and finds the first hyperlink, then selects it.
- "finding the hyperlink" is the tough part because the pattern tree takes in buffer coordinates, searches through the buffer in that given space, then stores everything relative to the defined space. Normally, the given buffer coordinates would align with the viewport's start and end. However, we're now trying to search outside of the viewport (sometimes), so we need to manage two coordinate systems at the same time.
- `convertToSearchArea()` lambda was used to convert a given coordinate into the search area coordinate system. So if the search area is the visible viewport, we spit out a viewport position. If the search area is the _next_ viewport, we spit out a position relative to that.
- `extractResultFromList()` lambda takes the list of patterns from the pattern tree and spits out the hyperlink we want. If we're searching forwards, we get the next one. Otherwise, we get the previous one. We explicitly ignore the one we're already on. If we don't find any, we return `nullopt`.
- Now that we have all these cool tools, we use them to progressively search through the buffer to find the next/previous hyperlink. Start by searching the visible viewport _after_ (or _before_) the current selection. If we can't find anything, go to the next "page" (viewport scrolled up/down). Repeat this process until something comes up.
- If we can't find anything, nothing happens. We don't wrap around.
- Other relevant changes
- the `copy` action is no longer bound to `Enter`. Instead, we manually handle it in `ControlCore.cpp`. This also lets us handle <kbd>Shift+Enter</kbd> appropriately without having to take another key binding.
- `_ScrollToPoint` was added. It's a simple function that just scrolls the viewport such that the provided buffer position is in view. This was used to de-duplicate code.
- `_ScrollToPoint` was added into the `ToggleMarkMode()` function. Turns out, we don't scroll to the new selection when we enter mark mode (whoops!). We _should_ do that and we should backport this part of the change too. I'll handle that.
- add some clarity when some functions are using the viewport position vs the buffer position. This is important because the pattern interval tree works in the viewport space.
## Validation Steps Performed
- case: all hyperlinks are in the view
- ✅ get next link
- ✅ get prev link
- ✅ case: need to scroll down for next link
- ✅ case: need to scroll up for next link
Does what it says on the tin. When we get focused, temporarily turn off readonly mode, as to not pop the dialog when the focus sequence is eventually sent to the connection.
* closes#13461
## Summary of the Pull Request
In #13370, we should be notifying the renderer that the selection changed. Minor oversight and simple fix.
## References
#4993#13370Closes#13413
## Summary of the Pull Request
Introduces the `switchSelectionEndpoint` action which switches whichever selection endpoint is targeted when a selection is present. For example, if you are targeting "start", `switchSelectionEndpoint` makes it so that now you are targeting "end". This also updates the selection markers appropriately.
## References
Spec - #5804#13358Closes#3663
## Detailed Description of the Pull Request / Additional comments
Most of the code is just standard code of adding a new action. Other than that, we have...
- if there is no selection, the action fails and the keybinding is passed through (similar to `copy()`)
- when we update the selection endpoint, we need to also update the "pivot". This ensures that future calls of `UpdateSelection()` respect this swap.
- [Corner Case] if the cursor is being moved, we make it so that you basically "anchored" an endpoint and you don't have to hold shift anymore.
## Summary of the Pull Request
1. [copy on select] when manually copying text (i.e. kbd or right-click) while in mark/quick-edit mode, we now dismiss the selection.
2. `Enter` is now bound to copy by default.
- This works very well with mark mode and provides a more consistent behavior with conhost's selection experience overall.
- "why not hardcode `Enter` as a way to copy when in mark mode?"
- In an effort to make this as configurable as possible, I decided to make it a configurable keybinding, but am open to suggestions.
3. selection markers
a. we now hide the selection markers when multi-clicking the terminal.
b. selection markers are now properly shown when a single cell selection exists
- Prior to this PR, any single cell selection would display both markers. Now, we actually track which endpoint we're moving and display the appropriate one.
4. ensures that when you use keyboard selection to move past the top/bottom of the scroll area, we clamp it to the origin/bottom-right respectively. The fix is also better here in that it provides consistent behavior across all of the `_MoveByX` functions.
5. adds `toggleBlockSelection` to the schema
## References
#13053
## Validation Steps Performed
Did a whole flowchart of expected behavior with copy on select:
- enable `copyOnSelect`
- make a selection with the mouse
- ✅ right-click should copy the text --> clear the selection --> paste
- use keyboard selection to quick-edit the existing selection
- ✅ `copy` action should clear the selection
- ✅ right-click should copy the text --> clear the selection --> paste
Played with selection markers a bit in mark mode and quick edit mode. Markers are updating appropriately.
## Summary of the Pull Request
This introduces a selection marker overlay that tells the user which endpoint is currently being moved by the keyboard. The selection markers are respect font size changes and `cursor` color.
## References
#715 - Keyboard Selection
#2840 - Keyboard Selection Spec
#5804 - Mark Mode Spec
## Detailed Description of the Pull Request / Additional comments
- `TermControl` layer:
- Use a canvas (similar to the one used for hyperlinks) to be able to draw the selection markers.
- If we are notified that the selection changed, update the selection markers appropriately.
- `UpdateSelectionMarkersEventArgs` lets us distinguish between mouse and keyboard selections. `ClearMarkers` is set to true in the following cases...
1. Mouse selection, via SetEndSelectionPoint
2. `LeftClickOnTerminal`, pretty self-explanatory
3. a selection created from searching for text
- `ControlCore` layer:
- Responsible for notifying `TermControl` to update the selection markers when a selection has changed.
- Transfers info (the selection endpoint positions and which endpoint we're moving) from the terminal core to the term control.
- `TerminalCore` layer:
- Provides the viewport position of the selection endpoints.
## Validation Steps Performed
- mouse selection (w/ and w/out shift) --> no markers
- keyboard selection --> markers
- markers update appropriately when we pivot the selection
- markers scroll when you hit a boundary
- markers take the color of the cursor color setting
- markers are resized when the font size changes
When I moved this into ControlCore, I forgot that UserScrollViewport is usually triggered by the scrollbar updating, so it doesn't ask the UI to update. Since this logic is in ControlCore, it's sorta in a weird place where it needs to communicate both up and down:
* update the `Terminal`'s viewport position
* update the `TermControl`'s scrollbar position
Checklist:
* [x] Closes a bug bash bug
* [x] Missed in #12948
* See also #11000
See also: #12799, the origin of much of this.
This change evolved over multiple phases.
### Part the first
When we create a defterm connection in `TerminalPage::_OnNewConnection`,
we don't have the hosting HWND yet, so the tab gets created without one.
We'll later get called with the owner, in `Initialize`.
To remedy this, we need to:
* In `Initialize`, make sure to update any existing controls with the
new owner.
* In `ControlCore`, actually propogate the new owner down to the
connection
### Part the second
DefTerm launches don't actually request focus mode, so the Terminal
never sends them focus events. We need those focus events so that the
console can request foreground rights.
To remedy this, we need to:
* pass `--win32input` to the commandline used to initialize OpenConsole
in ConPTY mode. We request focus events at the same time we request
win32-input-mode.
* I also added `--resizeQuirk`, because _by all accounts that should be
there_. Resizing in defterm windows should be _wacky_ without it, and
I'm a little surprised we haven't seen any bugs due to this yet.
### Part the third
`ConsoleSetForeground` expects a `HANDLE` to the process we want to give
foreground rights to. The problem is, the wire format we used _also_
decided that a HANDLE value was a good idea. It's not. If we pass the
literal value of the HANDLE to the process from OpenConsole to conhost,
so conhost can call that API, the value that conhost uses there will
most likely be an invalid handle. The HANDLE's value is its value in
_OpenConsole_, not in conhost.
To remedy this, we need to:
* Just not forward `ConsoleSetForeground`. Turns out, we _can_ just call
that in OpenConsole safely. There's no validation. So just instantiate
a static version of the Win32 version of ConsoleControl, just to use
for SetForeground. (thanks Dustin)
* [x] Tested manually - Win+R `powershell`, `notepad` spawns on top.
Closes#13211
Adds support for marks in the scrollbar. These marks can be added in 3
ways:
* Via the iterm2 `OSC 1337 ; SetMark` sequence
* Via the `addMark` action
* Automatically when the `experimental.autoMarkPrompts` per-profile
setting is enabled.
#11000 has more tracking for the big-picture for this feature, as well
as additional follow-ups. This set of functionality seemed complete
enough to send a review for now. That issue describes these how I wish
these actions to look in the fullness of time. This is simply the v0.1
from the hackathon last month.
#### Actions
* `addMark`: add a mark to the buffer. If there's a selection, use
place the mark covering at the selection. Otherwise, place the mark
on the cursor row.
- `color`: a color for the scrollbar mark. This is optional - defaults
to the `foreground` color of the current scheme if omitted.
* `scrollToMark`
- `direction`: `["first", "previous", "next", "last"]`
* `clearMark`: Clears marks at the current postition (either the
selection if there is one, or the cursor position.
* `clearAllMarks`: Don't think this needs explanation.
#### Per-profile settings
* `experimental.autoMarkPrompts`: `bool`, default `false`.
* `experimental.showMarksOnScrollbar`: `bool`
## PR Checklist
* [x] Closes#1527
* [x] Closes#6232
## Detailed Description of the Pull Request / Additional comments
This is basically hackathon code. It's experimental! That's okay! We'll
figure the rest of the design in post.
Theoretically, I should make these actions `experimental.` as well, but
it seemed like since the only way to see these guys was via the
`experimental.showMarksOnScrollbar` setting, you've already broken
yourself into experimental jail, and you know what you're doing.
Things that won't work as expected:
* resizing, ESPECIALLY reflowing
* Clearing the buffer with ED sequences / Clear Buffer
I could theoretically add velocity around this in the `TermControl`
layer. Always prevent marks from being visible, ignore all the actions.
Marks could still be set by VT and automark, but they'd be useless.
Next up priorities:
* Making this work with the FinalTerm sequences
* properly speccing
* adding support for `showMarksOnScrollbar: flags(categories)`, so you
can only display errors on the scrollbar
* adding the `category` flag to the `addMark` action
## Validation Steps Performed
I like using it quite a bit. The marks can get noisy if you have them
emitted on every prompt and the buffer has 9000 lines. But that's the
beautiful thing, the actions work even if the marks aren't visible, so
you can still scroll between prompts.
<details>
<summary>Settings blob</summary>
```jsonc
// actions
{ "keys": "ctrl+up", "command": { "action": "scrollToMark", "direction": "previous" }, "name": "Previous mark" },
{ "keys": "ctrl+down", "command": { "action": "scrollToMark", "direction": "next" }, "name": "Next mark" },
{ "keys": "ctrl+pgup", "command": { "action": "scrollToMark", "direction": "first" }, "name": "First mark" },
{ "keys": "ctrl+pgdn", "command": { "action": "scrollToMark", "direction": "last" }, "name": "Last mark" },
{ "command": { "action": "addMark" } },
{ "command": { "action": "addMark", "color": "#ff00ff" } },
{ "command": { "action": "addMark", "color": "#0000ff" } },
{ "command": { "action": "clearAllMarks" } },
// profiles.defaults
"experimental.autoMarkPrompts": true,
"experimental.showMarksOnScrollbar": true,
```
</details>
## Summary of the Pull Request
This introduced the `toggleBlockSelection` action to allow users to create a block selection using only the keyboard. This is not bound to any keys by default, however it is added to the command palette.
## References
#4993 - Epic
#5804 - Spec
## Validation Steps Performed
- [X] Mark mode always starts in line selection mode
- [X] Mouse selections are always in line selection mode by default
- [X] Can toggle block selection for an existing selection (regardless of how it was created)
- [X] The selection is copied properly (aka, no rendering issues)
Previously this project used a great variety of types to present text buffer
coordinates: `short`, `unsigned short`, `int`, `unsigned int`, `size_t`,
`ptrdiff_t`, `COORD`/`SMALL_RECT` (aka `short`), and more.
This massive commit migrates almost all use of those types over to the
centralized types `til::point`/`size`/`rect`/`inclusive_rect` and their
underlying type `til::CoordType` (aka `int32_t`).
Due to the size of the changeset and statistics I expect it to contain bugs.
The biggest risk I see is that some code potentially, maybe implicitly, expected
arithmetic to be mod 2^16 and that this code now allows it to be mod 2^32.
Any narrowing into `short` later on would then throw exceptions.
## PR Checklist
* [x] Closes#4015
* [x] I work here
* [x] Tests added/passed
## Validation Steps Performed
Casual usage of OpenConsole and Windows Terminal. ✅
## Summary of the Pull Request
The `DECPS` (Play Sound) escape sequence provides applications with a
way to play a basic sequence of musical notes. This emulates
functionality that was originally supported on the DEC VT520 and VT525
hardware terminals.
## PR Checklist
* [x] Closes#8687
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. Issue number
where discussion took place: #8687
## Detailed Description of the Pull Request / Additional comments
When a `DECPS` control is executed, any further output is blocked until
all the notes have finished playing. So to prevent the UI from hanging
during this period, we have to temporarily release the console/terminal
lock, and then reacquire it before returning.
The problem we then have is how to deal with the terminal being closed
during that unlocked interval. The way I've dealt with that is with a
promise that is set to indicate a shutdown. This immediately aborts any
sound that is in progress, but also signals the thread that it needs to
exit as soon as possible.
The thread exit is achieved by throwing a custom exception which is
recognised by the state machine and rethrown instead of being logged.
This gets it all the way up to the root of the write operation, so it
won't attempt to process anything further output that might still be
buffered.
## Validation Steps Performed
Thanks to the testing done by @jerch on a real VT525 terminal, we have a
good idea of how this sequence is supposed to work, and I'm fairly
confident that our implementation is reasonably compatible.
The only significant difference I'm aware of is that we support multiple
notes in a sequence. That was a feature that was documented in the
VT520/VT525 manual, but didn't appear to be supported on the actual
device.
## Summary of the Pull Request
Introduces a non-configurable version of mark mode to Windows Terminal. It has the following interactions defined:
- <kbd>ctrl+shift+m</kbd> --> Enter Mark Mode
- when in Mark Mode...
- <kbd>ESC</kbd> --> Exit Mark Mode
- arrow keys --> move "start"
- <kbd>shift</kbd> + arrow keys --> anchor "start", move "end"
- <kbd>ctrl+a</kbd> --> select all
- when a selection is active...
When in mark mode, the cursor does not blink.
## References
#4993 - [Epic] Keyboard Selection
## PR Checklist
* [X] Closes#715
* [X] Provides a resolution for #11985
## Detailed Description of the Pull Request / Additional comments
- `TermControl`:
- `TermControl.cpp` just adds logic to prevent the cursor from blinking when in mark mode
- `ControlCore`
- in the same place we handle quick edit, we add an entry point to mark mode
- `TerminalCore`
- this leverages `UpdateSelection()` and other quick edit functions to make mark mode happen
## Validation Steps Performed
- [x] Make selection, split pane, close pane
- NOTE: A similar scenario caused a crash at one point. Really weird. Keep an eye on it.
- [x] Cursor is off when in mark mode
- [x] general movement/selection
- [x] general movement/selection that forces the viewport to move
- [x] In mark mode, selectAll...
- [x] arrow keys --> move start
- [x] shift + arrow keys --> move end
- [x] (regardless of mark mode) if selection active, enter --> copy to clipboard
A bad merge, that actually revealed a horrible bug.
There was a secret conflict between the code in #12526 and #12515. 69b77ca was a bad merge that hid just how bad the issue was. Fixing the one line `nullptr`->`this` in `InteractivityFactory` resulted in a window that would flash uncontrollably, as it minimized and restored itself in a loop. Great.
This can seemingly be fixed by making sure that the conpty window is initially created with the owner already set, rather than relying on a `SetParent` call in post. This does pose some complications for the #1256 future we're approaching. However, this is a blocking bug _now_, and we can figure out the tearout/`SetParent` thing in post.
* fixes#13066.
* Tested with the script in that issue.
* Window doesn't flash uncontrollably.
* `gci | ogv` still works right
* I work here.
* Opening a new tab doesn't spontaneously cause the window to minimize
* Restoring from minimized doesn't yeet focus to an invisible window
* Opening a new tab doesn't yeet focus to an invisible window
* There _is_ a viable way to call `GetAncestor` s.t. it returns the Terminal's hwnd in Terminal, and the console's in Conhost
The `SW_SHOWNOACTIVATE` change is also quite load bearing. With just `SW_NORMAL`, the pseudo window (which is invisible!) gets activated whenever the terminal window is restored from minimized. That's BAD.
There's actually more to this as well.
Calling `SetParent` on a window that is `WS_VISIBLE` will cause the OS to hide the window, make it a _child_ window, then call `SW_SHOW` on the window to re-show it. `SW_SHOW`, however, will cause the OS to also set that window as the _foreground_ window, which would result in the pty's hwnd stealing the foreground away from the owning terminal window. That's bad.
`SetWindowLongPtr` seems to do the job of changing who the window owner is, without all the other side effects of reparenting the window.
Without `SetParent`, however, the pty HWND is no longer a descendant of the Terminal HWND, so that means `GA_ROOT` can no longer be used to find the owner's hwnd. For even more insanity, without `WS_POPUP`, none of the values of `GetAncestor` will actually get the terminal HWND. So, now we also need `WS_POPUP` on the pty hwnd. To get at the Terminal hwnd, you'll need
```c++
GetAncestor(GetConsoleWindow(), GA_ROOTOWNER)
```