Compare commits

..

37 Commits

Author SHA1 Message Date
Dustin L. Howett
22a7a5bc5f Migrate spelling-0.0.21 changes from main 2022-06-13 15:03:41 -07:00
Pankaj Bhojwani
b3997a3dad preview connection ctor takes preview string 2022-06-13 15:03:41 -07:00
HO-COOH
d866908eaf Fix mis-aligned Navigationview footer and save buttons in the settings page (#13282)
This improves the layout/appearance of the settings UI footer, by reducing
the height of the main content footer to match the navigation view footer.
2022-06-13 15:12:18 +00:00
Leonard Hecker
2e7a95ddb5 Fix uninitialized memory bug in GdiEngine (#13271)
ed27737 contains a regression were a `RECT` in `GdiEngine` wasn't properly
initialized anymore. Due to this, rendering during scrolling behaved erratic.

To find other cases of this bug in ed27737 the following regex was used:
```
^-.* = \{\s*\d*\s*\};
```

It appears that only `GdiEngine` was affected by a bug of this kind,
but just to be sure, this PR reverts all other instances.
This bug was likely caused when I tried to undo some of the changes in
ed27737 to make the PR smaller, but failed to revert the code properly.

## PR Checklist
* [x] Closes #13270
* [x] I work here

## Validation Steps Performed
I'm unable to reproduce the issue on my hardware and am unable to test
this change, but the uninitialized struct is clearly a bug regardless.

Co-authored-by: James Holderness <j4_james@hotmail.com>
2022-06-13 11:18:53 +00:00
Ian O'Neill
75e462441d Add an accelerator key for the shell extension (#13080)
Adds an accelerator key for the shell extension: `T` for stable, `P` for preview and `D` for dev.

# Validation
Ran a dev build and saw the keyboard accelerator assigned.

Closes #13061
2022-06-10 21:05:02 +00:00
James Holderness
c754f4d22d Restore the DECCTR color table report over conpty (#13227)
## Summary of the Pull Request

Up to now we haven't supported passing `DCS` sequences over conpty, so
any `DCS` operations would just be ignored in Windows Terminal. This PR
introduces a mechanism whereby we can selectively pass through
operations that could reasonably be handled by the connected terminal
without interfering with the regular conpty renderer. For now this is
just used to support restoring the `DECCTR` color table report.

## References

Support for `DECCTR` was originally added to conhost in PR #13139.

## PR Checklist
* [x] Closes #13223
* [x] CLA signed.
* [x] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] 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

## Detailed Description of the Pull Request / Additional comments

The way this works is we have a helper method in `AdaptDispatch` that
`DCS` operations can use to create a passthrough `StringHandler` for the
incoming data instead of their usual handler. To make this passthrough
process more efficient, the handler buffers the data before forwarding
it to conpty.

However, it's important that we aren't holding back data if output is
paused before the string terminator, so we need to flush the buffer
whenever we reach the end of the current write operation. This is
achieved by querying a new method in the `StateMachine` that lets us
know if we're currently dealing with the last character.

Another issue that came up was with the way the `StateMachine` caches
sequences that it might later need to forward to conpty. In the case of
string sequences like `DCS`, we don't want the actual string data cached
here, because that will just waste memory, and can also result in the
data being mistakenly output. So I've now disabled that caching when
we're in any of the string processing states.

## Validation Steps Performed

I've manually confirmed that the `DECCTR` sequence can now update the
color table in Windows Terminal. I've also added a new unit test in
`ConptyRoundtripTests` to verify the sequence is being passed through
successfully.
2022-06-10 19:13:17 +00:00
Mike Griese
b22684e697 Filter focus events that came from the API (#13260)
As described in #13238. libuv sends a focus event to jiggle the handle. Now that we support focus events as VT input (#12900), we'd translate those focus events to VT input as well. That combination of things caused exiting neovim to emit a `\x1b[O` to the input line of the shell when exited. 

To fix this, we're going to secretly filter out any focus events that came from the API, before translating to VT. We're fortunate here, the `FOCUS_EVENT_RECORD` version of the ctor is only called by the API. 

* [x] Closes #13238
2022-06-10 18:38:47 +00:00
Matthew Daly
1b630ab6f6 Accept color name "magenta" instead of "purple" (#13261)
When loading color schemes from Json, check if GetValueForKey failed. If
it did, and we were searching for the colors "purple"/"brightPurple",
swap out the color name with "magenta"/"brightMagenta" and try again.
This was tested manually by creating a new color scheme using the colors
"magenta"/"brightMagenta" and loading it, then printing colored text in
the terminal to assure that the colors were correctly assigned as
purple.

This changes the color loader to use an index pair table to add support
for alternate color names.

## Validation Steps Performed
- Manually edit settings.json, creating a new color scheme with colors
  "magenta" and "brightMagenta"
- View the color scheme in settings - the colors will appear as "purple"
  and "brightPurple" in the UI
- Run a program that prints colored text, purple and brightPurple text
  will appear in the colors defined as magenta and brightMagenta
- Modify the color scheme in the settings UI and save it, the colors
  will be saved as "purple" and "brightPurple" *(I think this is the
  right behavior, since calling them "magenta" is technically a
  mistake)*
- Repeat the steps above with a normal color scheme - colors named
  "purple" and "brightPurple" still load properly

Closes #11456
2022-06-10 18:37:39 +00:00
Hui Yoo
cac52a9e30 Initialize Terminal Preview settings from Terminal settings (#12907)
Windows Terminal Preview gets existing settings from Release build if
Preview settings are empty

This ensures that when settings are empty or not existent, we check if
we're currently in a preview build and if we are, we attempt to grab
settings from the Release build's setting path instead. We tested it
manually by changing settings in Release build and confirming that
changes migrated to Preview when settings are empty or not existent.
Additionally, we tested that settings.json of the running build changed.
We also ran existing TAEF testing locally and it passed.

In LoadAll() function in
src\cascadia\TerminalSettingsModel\CascadiaSettingsSerialization.cpp, we
first checked if the settings file us empty/exists via settingsString.
If it does not and we are in the Preview build, we try loading the
Release build's settings. We created modified versions of
CascadiaSettings::_settingsPath() and GetBaseSettingsPath() to get the
path for the Release build's settings. If the Release build settings do
exist and firstTimeSetup is true, we set it to settingsString so it can
be written to disk via WriteSettingsToDisk(). Note that currently we
hardcode the path of the Release build. This pull request was worked on
with @Dannihu01.

## Validation Steps Performed Test1: Setting to firstTimeSetup is true
and loading settings.json from WT release when release exists -> Result:
settings.json AND GUI reflected WT release’s settings

Test2: Setting to firstTimeSetup is true and loading settings.json from
WT release when release doesn’t exist -> Result: settings.json AND GUI
reflected DEFAULT settings

Test3: (After running Test1) Setting to firstTimeSetup is false and
seeing if current settings.json matches WT release. (See if it doesn’t
change) -> Result: settings.json AND GUI reflected WT release’s settings

Closes #6855 

Co-authored-by: Danniell Hu <dannihu@umich.edu>
2022-06-10 18:35:00 +00:00
Carlos Zamora
94e1697a48 Fix a11y crash in alt buffer apps (#13250)
## Summary of the Pull Request
This fixes the crashes caused by using a screen reader when in an app that uses the alt buffer via two changes:
1. Fix `Terminal::ViewEndIndex()`
   - `UiaTextRangeBase` receives a coordinate that is outside of the bounds of the text buffer via the following chain of functions... `_getDocumentEnd()` --> `GetLastNonSpaceCharacter()` --> `_getOptimizedBufferSize()` --> `GetTextBufferEndPoisition()` --> `ViewEndIndex()`
   - Since support for the alt buffer was added recently, `ViewEndIndex()` was recently changed, so that explains why this issue came up recently. We were accidentally setting the view end index to `height` instead of `height-1`. Thanks @j4james for finding this!
   - The UIA code would get the "exclusive end" of the alt buffer. Since it was using `ViewEndIndex()` to calculate that, it was one more than it should be. The UIA code has explicit allowance for "one past the end of the viewport" in its `IsInBounds()` check. Since the `ViewEndIndex()` is way beyond that, it's not allowed, hitting the fail fast.
2. Replace `FAIL_FAST_IF` with `assert`
   - These fail fast calls have caused so many issues with our UIA code. Those checks still provide value, but they shouldn't take the whole app down. This change replaces the `Viewport` and `UiaTextRangeBase` fail fasts with asserts to still perform those checks, but not take down the entire app in release builds.

Closes #13183 

## Validation Steps Performed
While using Narrator...
- opened nano in bash
- generated text and scrolled in nano
- generated text and scrolled in PowerShell
2022-06-10 18:09:09 +00:00
Mike Griese
1de1325cd1 Update roadmap for 1.13, 1.14. (#13234)
I threw that Gannt chart that I whipped up a few weeks ago in here, but if we feel that should be pulled because it's more a set of [guidelines than actual rules](https://c.tenor.com/aeV80XD4CSgAAAAC/guidlines-pirates-of-the-caribbean.gif), then we can pull it. 

* [x] Closes #13222
2022-06-10 18:07:53 +00:00
Mike Griese
dc8183a525 Add "JSON" to the "Open settings file" command names (#13265)
![image](https://user-images.githubusercontent.com/18356694/173090336-3512ee36-ae8b-41d5-8823-09cb1354c7ef.png)

* [x] Closes #13225
2022-06-10 16:41:13 +00:00
Mike Griese
2a7dd8b730 Make sure foreground access works for DefTerm (#13247)
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
2022-06-09 23:12:26 +00:00
Mike Griese
f685720cac Implement the FTCS_PROMPT sequence for marking the start of the prompt (#13163)
Implements the **FTCS_PROMPT** sequence, `OSC 133 ; A ST`. In this PR, it's just used to set a simple Prompt mark on the current line, in the same way that the iTerm2 sequence works.

There's rumination in #11000 on how to implement the rest of the FTCS sequences. 

This is broken into its own PR at the moment. [Quoth j4james](https://github.com/microsoft/terminal/pull/12948#issuecomment-1136360132):

> That should be just as easy, and I've noticed a couple of other terminals that are doing that, so it's not unprecedented. If we don't have any immediate use for the other options, there shouldn't be any harm in ignoring them initially.
> 
> And the benefit of going with the more widely supported sequence is that we're more likely to benefit from any shells that have this functionality built in. Otherwise they're forced to try and detect the terminal, which is practically impossible for Windows Terminal. Even iTerm2 supports the `OSC 133` sequence, so we'd probably be the only odd one out.

This part of the plumbing is super easy, so I thought it would be valuable to add regardless if we get to the whole of FTCS in 1.15.

* [x] I work here
* [x] Tested manually - in my pwsh `$PROFILE`:
  ```pwsh
  function prompt {
    $loc = $($executionContext.SessionState.Path.CurrentLocation);
    $out = "PS $loc$('>' * ($nestedPromptLevel + 1)) ";
    $out += "$([char]27)]9;9;`"$loc`"$([char]07)";
    $out += "$([char]27)]133;A;$([char]7)"; # add the FTCS_PROMPT to the... well, end, but you get the point
    return $out
  }
  ```
* See also #11000
2022-06-09 22:42:44 +00:00
Dustin L. Howett
205c09ccb8 Hide the use of MIDI behind velocity (#13258)
* conhost requires an additional dependency in Windows, which might
  cause us trouble in WPG
* Terminal requires an additional *package* dependency, which *will*
  cause us trouble in WPG (since GmDls is about 3MB)

I chose to scope the feature checks to MidiOut directly, as I wanted to
keep the delay behavior in MidiAudio::PlayNote. This is negotiable.

References #13252
2022-06-09 17:40:15 -05:00
Mike Griese
799b5d4add Experimental: add support for scrollbar marks (#12948)
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>
2022-06-09 16:10:16 -05:00
Leonard Hecker
730eb5fafd Fix signed/unsigned arithmetic bug during scrolling (#13256)
ed27737 contains a regression where (pseudocode)
```c
unsigned long ulActualDelta;
short ScreenInfo.WheelDelta;
delta *= (ScreenInfo.WheelDelta / (short)ulActualDelta);
//                                ^^^^^^^
```
was changed to
```c
delta *= (ScreenInfo.WheelDelta / ulActualDelta);
```

Due to `ulActualDelta` being unsigned, the new code casts the signed integer
to a unsigned one first, before doing the division. This causes scrolling
downwards (`WheelDelta` is negative) to appear as a large positive `delta`.

## PR Checklist
* [x] Closes #13253
* [x] I work here

## Validation Steps Performed
* Scrolling up/down works in OpenConsole again 
2022-06-09 19:55:04 +00:00
Dustin L. Howett
cd35bc5989 Restore OpenConsoleProxyStub's dependency on the CRT to fix QI (#13254)
When #13160 introduced a new interface to the IConsoleHandoff idl, it
changed midl's RPC proxy stub lookup algorithm from a direct GUID
comparison to an unrolled binary search. Now, that would ordinarily not
be a problem...

However, in #11610, we took a shortcut and replaced `memcmp` -- used
only by RPC for GUID comparison -- with a direct GUID-only equality
comparator. This worked totally fine, and ordinarily would not be a
problem...

The unrolled binary search unfortunately _relies on memcmp's contract_:
it uses memcmp to match against a fully sorted set. Our memcmp only
returned 0 or 1 (equal or not), and it knew nothing about ordering.

When a package that contains a PackagedCOM proxy stub is installed, it
is selected as the primary proxy stub for any interfaces it can proxy.
After all, interfaces are immutable, so it doesn't matter whose proxy
you're using. Now, given that we installed a *broken* proxy... *all*
IIDs that got whacked by our memcmp issue broke for every consumer.

To fix it: instead of implementing memcmp ourselves, we're just going to
take a page out of WinAppSDK's book and link this binary using the
"Hybrid CRT" model. It will statically link any parts of the STL it uses
(none) and dynamically link the ucrt (which is guaranteed to be present
on Windows.)

Sure, the binary size goes up from 8k to 24k, but... the cost is never
having to worry about this again.

Closes #13251
2022-06-09 19:48:28 +00:00
PankajBhojwani
4e20a8631c Fix deleting the last profile crashing Terminal (#13242)
## Summary of the Pull Request
When a profile gets deleted, we were navigating to the next item assuming it was a profile when it may not be. This commit fixes this by checking the tag of the next menu item before we navigate to it. 

## PR Checklist
* [x] Closes #13125 
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] 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.
* [x] I work here

## Validation Steps Performed
Deleting the last profile in the SUI doesn't cause a crash
2022-06-08 02:46:43 +00:00
Leonard Hecker
1b81c6540f Use feature markers during terminal-by-default handoff (#13160)
If we want to make Windows Terminal the default terminal under Windows,
we'll have to make conhost "handoff" incoming connections by default.
But this poses a problem: How can the seldomly updated conhost know
whether the routinely updated Windows Terminal version is actually willing
to accept such handoffs by default (it might be unwilling due to bugs, etc.)?

This commit solves the issue by introducing:
* A marker interface (`IDefaultTerminalMarker`): If it exists,
  Windows Terminal indicates its willingness to accept the handoff.
* Turning the all-0 GUID from being synonymous for conhost,
  to being synonymous for "Let Windows decide". Without this we wouldn't
  be able to differentiate between users who consciously chose conhost
  as their default terminal, vs. users who want the standard behavior.

## Validation Steps Performed
Testing fallback behavior:
* Install "Terminal" 1.13
* Delete the 2 keys below `HKCU\Console\%%Startup`
* Enable `Feature_AttemptHandoff` in `features.xml`
  Return `true` from `DefaultApp::CheckShouldTerminalBeDefault`
* Replace `conhost.exe` and `console.dll` with `sfpcopy` after building
* Launching `cmd.exe` launches as a conhost window 
  (because "Terminal" 1.13 lacks the marker interface)
* Open properties page in `conhost.exe`
  "Let Windows decide" is select by default 
* Changing the selection writes the new value 

Testing the new behavior:
* Delete the 2 keys below `HKCU\Console\%%Startup`
* Enable `Feature_AttemptHandoff` in `features.xml`
  Return `true` from `DefaultApp::CheckShouldTerminalBeDefault`
* Use `CLSID_WindowsTerminalConsoleDev` and `CLSID_WindowsTerminalTerminalDev`
  for the initialization of `TerminalDelegationPair`
* Replace `conhost.exe` and `console.dll` with `sfpcopy` after building
* Deploy the "Terminal Dev" package
* Launching `cmd.exe` launches "Terminal Dev" 
  (because "Terminal Dev" has the marker interface)
* Open the settings tab
  "Let Windows decide" is select by default 
* Changing the selection and saving writes the new value 
2022-06-07 19:31:02 +00:00
PankajBhojwani
f4c4efc7f0 Reorder "save" and "discard changes" buttons in the SUI (#13237)
"Save" is now the first button, "Discard changes" is now the second button

## PR Checklist
* [x] Closes #13174
2022-06-07 10:56:04 +00:00
Carlos Zamora
d76c70c470 Introduce toggleBlockSelection action (#13219)
## 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)
2022-06-07 10:55:17 +00:00
Leonard Hecker
ed27737233 Use 32-bit coordinates throughout the project (#13025)
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. 
2022-06-03 23:02:46 +00:00
James Holderness
c157f6346a Add support for restoring a DECCTR color table report (#13139)
This PR introduces the framework for the `DECRSTS` sequence which is
used to restore terminal state reports. But to start with, I've just
implemented the `DECCTR` color table report, which provides a way for
applications to alter the terminal's color scheme.

## PR Checklist
* [x] Closes #13132
* [x] CLA signed.
* [x] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] 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

## Detailed Description of the Pull Request / Additional comments

I've added the functions for parsing DEC RGB and HLS color formats into
the `Utils` class, where we've got all our other color parsing routines,
since this functionality will eventually be needed in other VT protocols
like Sixel and ReGIS.

Since `DECRSTS` is a `DCS` sequence, this only works in conhost for now,
or when using the experimental passthrough mode in Windows Terminal.

## Validation Steps Performed

I've added a number of unit tests to check that the `DECCTR` report is
being interpreted as expected. This includes various edge cases (e.g.
omitted and out-of-range parameters), which I have confirmed to match
the color parsing on a real VT240 terminal.
2022-06-03 01:52:00 +00:00
James Holderness
9dca6c27ee Add support for the DECPS (Play Sound) escape sequence (#13208)
## 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.
2022-06-01 17:53:56 +00:00
Leonard Hecker
7dbe741e1a Fix SetConsoleWindowInfo being able to crash ConPTY (#13212)
MSFT-33471786 is one of the most common crashes we have right now.
Memory dumps suggest that `VtEngine::UpdateViewport` is called with a rectangle
like `(0, 46, 119, 29)` (left, top, right, bottom), which is a rectangle of
negative height. When the `_invalidMap` is resized the negative size gets
turned into a very large unsigned integer, which results in an OOM exception,
crashing OpenConsole.

`VtEngine::UpdateViewport` is called by `Renderer::_CheckViewportAndScroll`
which holds a (cached) old and a new viewport. The old viewport was
`(0, 46, 119, 75)` which is exceedingly similar to the invalid, new viewport.
It's bottom coordinate is also coincidentally larger by exactly 46 (top).

The viewport comes from the `SCREEN_INFORMATION` class whose `SetViewport`
function was highly suspicious as it has a branch which updates the bottom
to be the buffer height, but leaves the top unmodified.

`SCREEN_INFORMATION::SetViewport` is called by `SetConsoleWindowInfo` which
processes user-provided data. A repro of the crash can be constructed with:
```
SMALL_RECT rect{0, 46, 119, 75};
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &rect);
```

Closes #13193
Closes MSFT-33471786

## Validation Steps Performed
Ensured the following code doesn't crash when run under Windows Terminal:
```
SMALL_RECT rect{0, 46, 119, 75};
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &rect);
```
2022-06-01 17:47:22 +00:00
leejy12
fb1491a4af Hide "Open in Terminal" context menu option appropriately (#13206)
This commit hides the "Open in Terminal" context menu option when the
context menu is opened in a non-filesystem path like "Quick Actions".

Closes #12578
2022-05-31 17:47:52 +00:00
Mike Griese
11b810e403 Prevent ConPTY from de-snapping the terminal (#13164)
This is a big hammer to put out this fire. We're keeping the hiding around for now, cause we think that's likely the one that the internal tests use that we really care about here. If we need to bring this back, we can. 

* [x] Closes #13158
* [x] Closes #13162
* [x] Validated these both manually 
* [x] `[Native]::ShowWindow([Native]::GetConsoleWindow(), 6)` still works
2022-05-25 15:01:36 -05:00
Ian O'Neill
6439b4d807 Don't attempt to split settings tabs (#13172)
Prevents a null pointer dereference when attempting to split a settings tab, due to it not being a terminal tab.

* [x] Closes #13166

## Validation Steps Performed
Manually tested.
2022-05-25 19:58:04 +00:00
Dustin L. Howett
78852e04ec Remove location-specific suffixes from the zh- lang specifiers (#13148)
This only impacts the UI. We can take a workitem to rename the loc data
later. When the user specifies zh-Hans/zh-Hant, the resource mapper does
the right thing.

Related to #8984
2022-05-24 16:57:54 +00:00
Mike Griese
c5fad74c54 Debounce window state changes caused by the PTY (#13147)
Use a throttled update to update our window state. Throttling should prevent scenarios where the Terminal window state and PTY window state get de-sync'd, and cause the window to minimize/restore constantly in a loop.  "Should" is doing a lot of work in this sentence. 

A 200ms delay was chosen because it's the typical animation timeout in Windows. This does result in a delay between the PTY requesting a change to the window state and the Terminal realizing it, but should mitigate issues where the Terminal and PTY get desync'd.


I think we're overall not super confident that this fixes the root causes of the issue. Rather, we're hopeful that a small amount of throttling here should leave time for the Terminal and pty to sync back up. We're comfortable enough with that as a bandaid for 1.14 preview, to see how this behaves in the wild.
2022-05-23 23:04:26 +00:00
Mike Griese
bb03b00ebf Fix the SUI being in the wrong Theme color (#13145)
ThemeResources are a persistent pain.

Regressed in #13083. See also #12775 et. al.

We can't just put those here though as StaticResources, because XAML will evaluate their values when the App is first loaded, and we'll always use the value from the OS theme, regarless of the requested theme. Kinda the same thing we've had to do with TabViewBackground in the past.

* [x] Fixes something we noticed right before shipping
2022-05-23 18:01:13 +00:00
Mike Griese
4c333536b4 Add the background back to showTabsInTitlebar: false's tab row. (#13144)
We're doing it this way because ThemeResources are tricky. We
default in XAML to using the appropriate ThemeResource background
color for our TabRow. When tabs in the titlebar are _disabled_,
this will ensure that the tab row has the correct theme-dependent
value. When tabs in the titlebar are _enabled_ (the default),
we'll switch the BG to Transparent, to let the Titlebar Control's
background be used as the BG for the tab row.

We can't do it the other way around (default to Transparent, only
switch to a color when disabling tabs in the titlebar), because
looking up the correct ThemeResource from and App dictionary is a
capital-H Hard problem.

* [x] Closes #13143
* [x] I work here
* [x] validated manually:
  - [x] showTabsInTitlebar: false, true
  - [x] useAcrylicInTabRow: false, true
  - [x] theme: light, dark
* [x] Need to check if this is regressed the same in 1.13. I suspect it is.
2022-05-23 16:16:57 +00:00
Carlos Zamora
bf41a90ad8 Introduce Mark Mode (#13053)
## 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
2022-05-20 23:04:53 +00:00
Mike Griese
d82af9367f Fis ShowHide for DefTerm (#13129)
Well this one feels dumb.

Make sure to also initially set the visibility of ConPTY windows created for DefTerm connections.

* [x] Closes #13066 for real.
* [x] tested manually.
2022-05-19 15:11:52 -05:00
Mike Griese
77215d9d77 Fix ShowWindow(GetConsoleWindow()) (#13118)
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)
```
2022-05-18 17:25:06 -05:00
James Holderness
0154da5d33 Add support for line renditions in the DX renderer (#13102)
This PR adds support for the VT line rendition attributes in the DirectX
renderer, which allows for double-width and double-height line
renditions.

Line renditions were first implemented in conhost (with the GDI
renderer) in PR #8664.  Supporting them in the DX renderer now is a
small step towards #11595.

The DX implementation is very similar to the GDI one. When a particular
line rendition is requested, we create a transform that is applied to
the render target. And in the case of double-height renditions, we also
initialize some clipping offsets to allow for the fact that we only
render half of a line at a time.

One additional complication exists when drawing the cursor, which
requires a two part process where it first renders to a command list,
and then draw the command list in a second step. We need to temporarily
reset the transform in that first stage otherwise it ends up being
applied twice.

I've manually tested the renderer in conhost by setting the `UseDx`
registry entry and confirmed that it passes the _Vttest_ double-size
tests as well as several of my own tests. I've also checked that the
renderer can now handle horizontal scrolling, which is a feature we get
for free with the transforms.
2022-05-18 18:17:06 +00:00
349 changed files with 6888 additions and 4867 deletions

View File

@@ -410,6 +410,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InteractivityOneCore", "src
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererWddmCon", "src\renderer\wddmcon\lib\wddmcon.vcxproj", "{75C6F576-18E9-4566-978A-F0A301CAC090}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Audio", "Audio", "{40BD8415-DD93-4200-8D82-498DDDC08CC8}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MidiAudio", "src\audio\midi\lib\midi.vcxproj", "{3C67784E-1453-49C2-9660-483E2CC7F7AD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@@ -3449,6 +3453,46 @@ Global
{75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x64.Build.0 = Release|x64
{75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x86.ActiveCfg = Release|Win32
{75C6F576-18E9-4566-978A-F0A301CAC090}.Release|x86.Build.0 = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x64.ActiveCfg = AuditMode|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x64.Build.0 = AuditMode|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.AuditMode|x86.Build.0 = AuditMode|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|Any CPU.ActiveCfg = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|ARM.ActiveCfg = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|ARM64.Build.0 = Debug|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x64.ActiveCfg = Debug|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x64.Build.0 = Debug|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x86.ActiveCfg = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Debug|x86.Build.0 = Debug|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x64.Build.0 = Fuzzing|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Fuzzing|x86.Build.0 = Fuzzing|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|Any CPU.ActiveCfg = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|ARM.ActiveCfg = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|ARM64.ActiveCfg = Release|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|ARM64.Build.0 = Release|ARM64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x64.ActiveCfg = Release|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x64.Build.0 = Release|x64
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x86.ActiveCfg = Release|Win32
{3C67784E-1453-49C2-9660-483E2CC7F7AD}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3552,6 +3596,8 @@ Global
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {05500DEF-2294-41E3-AF9A-24E580B82836}
{06EC74CB-9A12-428C-B551-8537EC964726} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{75C6F576-18E9-4566-978A-F0A301CAC090} = {05500DEF-2294-41E3-AF9A-24E580B82836}
{40BD8415-DD93-4200-8D82-498DDDC08CC8} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
{3C67784E-1453-49C2-9660-483E2CC7F7AD} = {40BD8415-DD93-4200-8D82-498DDDC08CC8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

View File

@@ -111,10 +111,10 @@ repository.
---
## Windows Terminal 2.0 Roadmap
## Windows Terminal Roadmap
The plan for delivering Windows Terminal 2.0 [is described
here](/doc/terminal-v2-roadmap.md) and will be updated as the project proceeds.
The plan for the Windows Terminal [is described here](/doc/roadmap-2022.md) and
will be updated as the project proceeds.
## Project Build Status

View File

@@ -318,6 +318,7 @@
"moveFocus",
"movePane",
"swapPane",
"markMode",
"moveTab",
"multipleActions",
"newTab",

View File

@@ -22,21 +22,61 @@ Below is the schedule for when milestones will be included in release builds of
| Milestone End Date | Milestone Name | Preview Release Blog Post |
| ------------------ | -------------- | ------------------------- |
| 2020-06-18 | [1.1] in Windows Terminal Preview | [Windows Terminal Preview 1.1 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-1-release/) |
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | [Windows Terminal Preview 1.2 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-2-release/) |
| 2020-08-31 | [1.3] in Windows Terminal Preview<br>[1.2] in Windows Terminal | [Windows Terminal Preview 1.3 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-3-release/) |
| 2020-09-30 | [1.4] in Windows Terminal Preview<br>[1.3] in Windows Terminal | [Windows Terminal Preview 1.4 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-4-release/) |
| 2020-11-30 | [1.5] in Windows Terminal Preview<br>[1.4] in Windows Terminal | [Windows Terminal Preview 1.5 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-5-release/) |
| 2021-01-31 | [1.6] in Windows Terminal Preview<br>[1.5] in Windows Terminal | [Windows Terminal Preview 1.6 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-6-release/) |
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/) |
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
| 2021-10-20 | [1.12] in Windows Terminal Preview<br>[1.11] in Windows Terminal | [Windows Terminal Preview 1.12 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-12-release/) |
| | [1.13] in Windows Terminal Preview<br>[1.12] in Windows Terminal | |
| | [1.14] in Windows Terminal Preview<br>[1.13] in Windows Terminal | |
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | [Windows Terminal Preview 1.2 Release] |
| 2020-08-31 | [1.3] in Windows Terminal Preview<br>[1.2] in Windows Terminal | [Windows Terminal Preview 1.3 Release] |
| 2020-09-30 | [1.4] in Windows Terminal Preview<br>[1.3] in Windows Terminal | [Windows Terminal Preview 1.4 Release] |
| 2020-11-30 | [1.5] in Windows Terminal Preview<br>[1.4] in Windows Terminal | [Windows Terminal Preview 1.5 Release] |
| 2021-01-31 | [1.6] in Windows Terminal Preview<br>[1.5] in Windows Terminal | [Windows Terminal Preview 1.6 Release] |
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release] |
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release] |
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release] |
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release] |
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release] |
| 2021-10-20 | [1.12] in Windows Terminal Preview<br>[1.11] in Windows Terminal | [Windows Terminal Preview 1.12 Release] |
| 2022-02-03 | [1.13] in Windows Terminal Preview<br>[1.12] in Windows Terminal | [Windows Terminal Preview 1.13 Release] |
| 2022-05-24 | [1.14] in Windows Terminal Preview<br>[1.13] in Windows Terminal | [Windows Terminal Preview 1.14 Release] |
| | [1.15] in Windows Terminal Preview<br>[1.14] in Windows Terminal | |
| | [1.16] in Windows Terminal Preview<br>[1.15] in Windows Terminal | |
| | [1.17] in Windows Terminal Preview<br>[1.16] in Windows Terminal | |
### Release outline
Below is a VERY vague outline of the remaining calendar year that was drafted late May 2022. This was drafted for internal planning purposes, as a guide. It is not meant to represent official dates. More often than not, releases are synced to official features landing, rather than arbitrary dates. Drift from this initial draft is entirely expected.
```mermaid
gantt
title Proposed Terminal Releases 1.14-1.18
dateFormat YYYY-MM-DD
axisFormat %d %b
section Terminal 1.14
Lock down & bake :done, 2022-05-06, 2w
Release 1.14 :milestone, 2022-05-24
section Terminal 1.15
Features :done, a1, 2022-05-06, 4w
Bugfix :active, a2, after a1 , 1w
Lock down & bake :after a2 , 1w
Release 1.15 :milestone, 2022-06-21, 0
1.15 becomes Stable :milestone, after b3, 0
section Terminal 1.16
Features :b1, after a2, 4w
Bugfix :b2, after b1 , 2w
Lock down & bake :b3, after b2 , 2w
Release 1.16 :milestone, after b3, 0
1.16 becomes Stable :milestone, after c3, 0
section Terminal 1.17
Features :c1, after b2, 4w
Bugfix :c2, after c1 , 2w
Lock down & bake :c3, after c2 , 2w
Release 1.17 :milestone, after c3, 0
1.17 becomes Stable :milestone, after d3, 0
section Terminal 1.18
Features :d1, after c2, 4w
Bugfix :d2, after d1 , 2w
Lock down & bake :d3, after d2 , 2w
Release 1.18 :milestone, after d3, 0
```
## Issue Triage & Prioritization
Incoming issues/asks/etc. are triaged several times a week, labeled appropriately, and assigned to a milestone in priority order:
@@ -62,7 +102,9 @@ Incoming issues/asks/etc. are triaged several times a week, labeled appropriatel
[1.12]: https://github.com/microsoft/terminal/milestone/38
[1.13]: https://github.com/microsoft/terminal/milestone/39
[1.14]: https://github.com/microsoft/terminal/milestone/41
[1.15]: https://github.com/microsoft/terminal/milestone/47
[1.16]: https://github.com/microsoft/terminal/milestone/48
[1.17]: https://github.com/microsoft/terminal/milestone/49
[22H1]: https://github.com/microsoft/terminal/milestone/43
[22H2]: https://github.com/microsoft/terminal/milestone/44
@@ -70,3 +112,17 @@ Incoming issues/asks/etc. are triaged several times a week, labeled appropriatel
[Backlog]: https://github.com/microsoft/terminal/milestone/45
[Terminal v2 Roadmap]: https://github.com/microsoft/terminal/tree/main/doc/terminal-v2-roadmap.md
[Windows Terminal Preview 1.2 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-2-release/
[Windows Terminal Preview 1.3 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-3-release/
[Windows Terminal Preview 1.4 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-4-release/
[Windows Terminal Preview 1.5 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-5-release/
[Windows Terminal Preview 1.6 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-6-release/
[Windows Terminal Preview 1.7 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/
[Windows Terminal Preview 1.8 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/
[Windows Terminal Preview 1.9 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/
[Windows Terminal Preview 1.10 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/
[Windows Terminal Preview 1.11 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/
[Windows Terminal Preview 1.12 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-12-release/
[Windows Terminal Preview 1.13 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-13-release/
[Windows Terminal Preview 1.14 Release]: https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-14-release/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,274 +0,0 @@
---
author: Mike Griese @zadjii-msft
created on: 2022-05-18
last updated: 2022-10-28
issue id: n/a
---
# Markdown Notebooks in the Terminal
## Abstract
Notebooks have risen to popularity in recent years as a way of combining both
code and documentation in a single experience. They enable users to seamlessly
write code, and execute it to see the output, write documentation on the code,
and share that with others. However, there's not really anything like notebooks
for generic commandline experiences.
There are, however, markdown files. Markdown has become a bit of a lingua franca
of the developer experience. It's used prominently on GitHub - the "homepage" of
any repo on GitHub is now typically a markdown file. This file will have all
sorts of documentation, and notably these READMEs are often filled with commands
that one can execute for this project. Downloading, installing its dependencies,
building and running the project, etc, all commands that are already listed
todcay in READMEs across the world.
It would be a major convenience for users to be able to just load a pre-rendered
markdown file directly into their terminal windows. These files already include
marked blocks of code which identify sets of commands for the command line. It
should be as simple as clicking a button to run these commands in the Terminal,
or even to run a whole file worth of commands automatically.
## Background
### Inspiration
[Jupyter notebooks] ([a Jupyter example]) served as the primary inspiration for
this feature. Another shoutout to [this comment on HackerNews], which inspired a
lot of brainstorming on this topic over the last year since it was posted.
Many initial brainstorms were focused on more notebook-like features in the
Terminal. For example, finding ways to create individual terminal blocks inline
with commands, where each input command and its output would be separated from
one another, possibly separated by some sort of text/rich markup. This seemed to
precipitate the need for a new file syntax to be authored, where we could save
commands as they were run. The Terminal would then open this new file type as a
set of terminal blocks each pre-populated with these saved commands. Hoever,
this came with the drawback that projects which would like to leverage this
feature would have to author entirely new files, in a new syntax, just to make
use of this functionality. It seemed as though it was a niche enough UX that it
would be unlikely to broadly catch on.
The real inspiration here was that there's already a file type with broad
adoption that's already filled with commands like this. Markdown files. Take a
look at something like [building.md](../../../building.md). That file _already_
has a long set of commands for building the Terminal, running tests, deploying,
and various other helper scripts. Being able to immediately leverage this
existing ecosystem would undobtably lead to quicker adoption.
### User Stories
* **A**: The user can perform some commandline action (like `wt open
README.md`), which opens a new pane in the Terminal, with the markdown file
rendered into that pane.
* **B**: Markdown panes have buttons next to code blocks that allow the text of
that block to be sent directly to the adjacent terminal as input.
* **C**: The user can press different buttons to run some subset of all the
commands in the file
- **C.1**: Run all the commands in this file
- **C.2**: Run all the commands from (the currently selected block) to the end
of the file
- **C.1**: Run all the commands from (the currently selected block) to the
next header. (e.g., run all the commands in this section of the doc.)
* **D**: The user can edit the contents of the markdown file directly in the
Terminal.
* **E**: The Terminal could be configured to automatically open a markdown file
when `cd`ing to a directory
* **F**: The command for opening a markdown file also supports opening files
from the web (e.g. directly from GitHub)
* **G**: Code blocks in the markdown file are automatically assigned
autoincrementing IDs. The user can perform an action (via keybinding, command
palette, whatever) to execute a command block from the file, based on it's
assigned ID.
* **H**: ...
## Solution Design
The Terminal will allow for non-terminal content in a pane. This is something
that's been prototyped before, just needs a stronger justification for
finishing.
We'll leverage the Terminal's existing `sendInput` command to handle a lot of
this. That can be used to send keystrokes to the Terminal. Figuring out which
pane to send the `sendInput` command to might be a bit tricky. We'll need to
figure out what an action like that does when the active pane is not a terminal
pane.
Below are two different structures that could be used for imlpementing notebooks
in the Terminal. These are **Side-by-side** notebooks, and **Inline** notebooks.
Side-by-side notebooks consist of two panes - a standard terminal control in
one, and rendered markdown in the other. Inline notebooks are more like
traditional notebooks, with the terminal output rendered as a block within the
rendered markdown content.
### Side-by-side notebooks
Opening Markdown side-by-side with the Terminal output is certainly a little
different than the way a notebook traditionally works. Notebooks typically have
the code in a block, with output inline, below the block. Blocks could also just
be dedicated to text, for documentation mixed between the code. The feature
proposed here is different from that, for sure. For this proposal, the Terminal
still exists side-by-side from the source markdown. Running commands from the
markdown text would then send the command as a string of input to the connected
terminal. This approach was elected over attempting to create artificial
boundaries between different blocks.
Oftentimes, the command line is a very stateful experience. Set some environment
variables, run some script, use the errorlevel from the previous command, etc.
Running each block in wholly separate console instances would likely not be
useful.
Additionally, finding the separation between command line input and its output,
and the separation between individual commands is not an entirely trivial
process. Should we try to separate out the command input line into one buffer,
then the output into another buffer sounds great on paper. Consider, however,
something like `cmd.exe`, which does not provide any sort of distinction between
its input line and its output. Or `python.exe`, as an interactive REPL, which
certainly doesn't tell the terminal the difference. How would we be able to
detect something like a multi-line command at the REPL?
By keeing the command blocks out-of-band from the terminal output, we keep the
familiar terminal experience. It acts just as you'd expect, with no additional
configuration on the user's side. The commands are something that are already
written down, just waiting for the user to run them. They could even be sent to
something that isn't necessarily a shell - like pasting a bit of configuration
into a text editor like `vim` or `emacs`. The commands in the markdown side are
just strings of text to send to the terminal side - nothing more.
### Inline notebooks
Is there a way to have a more traditional notebook experience, where the
terminal output is rendered as blocks within the markdown itself? I believe
there is, by making agressive use of the FTCS mark VT sequences. These are
sequences that enable identifying the parts of the buffer as a prompt, the
commandline, or the output. We can use these to pull out individual rows of the
buffer, and display them as miniature terminal controls within the notebook.
This involves a single terminal insance backing the entire notebook. When the
user runs a code block in the notebook, we'll `sendInput` the text to the
backing terminal the same as before. We'll then use the `FTCS_COMMAND_EXECUTED`
sequence to know where the actual start of the output of the command is. We'll
gather all output from that moment on, and tee that to a separate "front"
control, which we use as the display for the output of the command. We'll render
that "front" control inline with the rest of the markdown content, immediately
below the code block for that command.
## UI/UX Design
### Side-by-side
![A rough mockup of what this feature might look like](./mockup-000.png)
### Inline
![](./inline-blocks-000.png)
## Tenents
<table>
<tr><td><strong>Accessibility</strong></td><td>
[comment]: # How will the proposed change impact accessibility for users of screen readers, assistive input devices, etc.
</td></tr>
<tr><td><strong>Security</strong></td><td>
[comment]: # How will the proposed change impact security?
Opening a file like this will _never_ auto-run commands. Commands must always be
intentionally interacted with, to provide a positive confirmation from the user
"yes, I intended to run `curl some.website.com/foo.txt`".
</td></tr>
<tr><td><strong>Reliability</strong></td><td>
[comment]: # Will the proposed change improve reliability? If not, why make the change?
</td></tr>
<tr><td><strong>Compatibility</strong></td><td>
[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"?
It's critically important that nothing about this feature be necessarily Windows
Terminal-dependent. These features shouldn't be powered by some new undocumented
escape sequence that only we support. They should NOT be powered by new Windows
APIs, especially not any extensions to the Console API. There's no reason other
terminals couldn't also implement similar functionality.
</td></tr>
<tr><td><strong>Performance, Power, and Efficiency</strong></td><td>
[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"?
</td></tr>
</table>
## Potential Issues
For rendering markdown, we'll either need:
* A way to display a WebView in a WinUI2 XAML Island
- This is something that's on the backlog currently for MUX 2.x. Theoretically
not too hard to add an `IInitializeWithWindow` to `WebView2` which should
enable XAML Islands, but needs more research.
* To migrate to WinUI 3
- In WinUI 3 I believe we should be able to get WebViews for free.
- We might still be a XAML Island in WinUI 3, which may complicate that.
* A C++ based method of rendering Markdown to UWP XAML
- There's a Windows Community Toolkit control for rendering to XAML currently,
but that is backed by C#, so we can't use that.
We'll also need the markdown rendering to be extensible, so that we can insert
"play" buttons alongside the blocks.
## Future considerations
### Tighter GitHub integration
GitHub already has the helpful "Open In GitHub" button for opening a repo in the
GitHub desktop client, or in Visual Studio.
![GitHub's "Clone, open or download" flyout](GitHub-open-with.png)
It'd be cool if there was a similar button for opening it up in the Terminal. It
could open the README immediately as a new tab, and then provide some sort of
InfoBar with a button that would allow the user to immediately clone the repo to
some location on their PC. This would likely need a protocol handler installed
by the Terminal to help connect the browser to the Terminal.
### Collapsible Blocks
One of the key features of notebooks is the ability to easily collapse regions
of the notebook. With the command output being out of band from the input of the
command, not as independent blocks, this becomes a bit trickier. To try and
reproduce a similar ability to collapse regions of the buffer, we'll look to
[Marks] in the terminal as a potential solution. The FinalTerm sequences allow a
client to mark up the region of the buffer that's the prompt, the command line,
and the output. Using those marks would provide an easy heuristic to allow users
to collapse the output of commands. These sequences however do require manual
configuration by the user, and are not expected to be able to work in all
environments (and shells). While powerful, because of this limitation, we didn't
want to architect the entire experience around something that wouldn't always
work.
## Resources
[comment]: # Be sure to add links to references, resources, footnotes, etc.
### Footnotes
<a name="footnote-1"><a>[1]:
[Jupyter notebooks]: https://jupyter.org/
[a Jupyter example]: https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb
[this comment on HackerNews]: https://news.ycombinator.com/item?id=26617656
[Marks]: https://github.com/microsoft/terminal/issues/11000

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 KiB

View File

@@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "MidiAudio.hpp"
#include "../terminal/parser/stateMachine.hpp"
namespace
{
class MidiOut
{
public:
static constexpr auto NOTE_OFF = 0x80;
static constexpr auto NOTE_ON = 0x90;
static constexpr auto PROGRAM_CHANGE = 0xC0;
// We're using a square wave as an approximation of the sound that the
// original VT525 terminals might have produced. This is probably not
// quite right, but it works reasonably well.
static constexpr auto SQUARE_WAVE_SYNTH = 80;
MidiOut() noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutOpen(&handle, MIDI_MAPPER, NULL, NULL, CALLBACK_NULL);
OutputMessage(PROGRAM_CHANGE, SQUARE_WAVE_SYNTH);
}
}
~MidiOut() noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutClose(handle);
}
}
void OutputMessage(const int b1, const int b2, const int b3 = 0, const int b4 = 0) noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutShortMsg(handle, MAKELONG(MAKEWORD(b1, b2), MAKEWORD(b3, b4)));
}
}
MidiOut(const MidiOut&) = delete;
MidiOut(MidiOut&&) = delete;
MidiOut& operator=(const MidiOut&) = delete;
MidiOut& operator=(MidiOut&&) = delete;
private:
HMIDIOUT handle = nullptr;
};
}
using namespace std::chrono_literals;
MidiAudio::~MidiAudio() noexcept
{
try
{
#pragma warning(suppress : 26447)
// We acquire the lock here so the class isn't destroyed while in use.
// If this throws, we'll catch it, so the C26447 warning is bogus.
_inUseMutex.lock();
}
catch (...)
{
// If the lock fails, we'll just have to live with the consequences.
}
}
void MidiAudio::Initialize()
{
_shutdownFuture = _shutdownPromise.get_future();
}
void MidiAudio::Shutdown()
{
// Once the shutdown promise is set, any note that is playing will stop
// immediately, and the Unlock call will exit the thread ASAP.
_shutdownPromise.set_value();
}
void MidiAudio::Lock()
{
_inUseMutex.lock();
}
void MidiAudio::Unlock()
{
// We need to check the shutdown status before releasing the mutex,
// because after that the class could be destroyed.
const auto shutdownStatus = _shutdownFuture.wait_for(0s);
_inUseMutex.unlock();
// If the wait didn't timeout, that means the shutdown promise was set,
// so we need to exit the thread ASAP by throwing an exception.
if (shutdownStatus != std::future_status::timeout)
{
throw Microsoft::Console::VirtualTerminal::StateMachine::ShutdownException{};
}
}
void MidiAudio::PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept
try
{
// The MidiOut is a local static because we can only have one instance,
// and we only want to construct it when it's actually needed.
static MidiOut midiOut;
if (velocity)
{
midiOut.OutputMessage(MidiOut::NOTE_ON, noteNumber, velocity);
}
// By waiting on the shutdown future with the duration of the note, we'll
// either be paused for the appropriate amount of time, or we'll break out
// of the wait early if we've been shutdown.
_shutdownFuture.wait_for(duration);
if (velocity)
{
midiOut.OutputMessage(MidiOut::NOTE_OFF, noteNumber, velocity);
}
}
CATCH_LOG()

View File

@@ -0,0 +1,36 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- MidiAudio.hpp
Abstract:
This modules provide basic MIDI support with blocking sound output.
*/
#pragma once
#include <future>
#include <mutex>
class MidiAudio
{
public:
MidiAudio() = default;
MidiAudio(const MidiAudio&) = delete;
MidiAudio(MidiAudio&&) = delete;
MidiAudio& operator=(const MidiAudio&) = delete;
MidiAudio& operator=(MidiAudio&&) = delete;
~MidiAudio() noexcept;
void Initialize();
void Shutdown();
void Lock();
void Unlock();
void PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept;
private:
std::promise<void> _shutdownPromise;
std::future<void> _shutdownFuture;
std::mutex _inUseMutex;
};

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{3c67784e-1453-49c2-9660-483e2cc7f7ad}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>midi</RootNamespace>
<ProjectName>MidiAudio</ProjectName>
<TargetName>MidiAudio</TargetName>
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
<ItemGroup>
<ClCompile Include="..\MidiAudio.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\MidiAudio.hpp" />
<ClInclude Include="..\precomp.h" />
</ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
</Project>

View File

@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"

30
src/audio/midi/precomp.h Normal file
View File

@@ -0,0 +1,30 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- precomp.h
Abstract:
- Contains external headers to include in the precompile phase of console build process.
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
--*/
#pragma once
// clang-format off
// This includes support libraries from the CRT, STL, WIL, and GSL
#include "LibraryIncludes.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define NOMCX
#define NOHELP
#define NOCOMM
#endif
// Windows Header Files:
#include <windows.h>
// clang-format on

View File

@@ -11,8 +11,8 @@
// - attr - the default text attribute
// Return Value:
// - constructed object
ATTR_ROW::ATTR_ROW(const uint16_t width, const TextAttribute attr) :
_data(width, attr) {}
ATTR_ROW::ATTR_ROW(const til::CoordType width, const TextAttribute attr) :
_data(gsl::narrow_cast<uint16_t>(width), attr) {}
// Routine Description:
// - Sets all properties of the ATTR_ROW to default values
@@ -32,9 +32,9 @@ void ATTR_ROW::Reset(const TextAttribute attr)
// - newWidth - The new width of the row.
// Return Value:
// - <none>, throws exceptions on failures.
void ATTR_ROW::Resize(const uint16_t newWidth)
void ATTR_ROW::Resize(const til::CoordType newWidth)
{
_data.resize_trailing_extent(newWidth);
_data.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
}
// Routine Description:
@@ -45,9 +45,9 @@ void ATTR_ROW::Resize(const uint16_t newWidth)
// - the text attribute at column
// Note:
// - will throw on error
TextAttribute ATTR_ROW::GetAttrByColumn(const uint16_t column) const
TextAttribute ATTR_ROW::GetAttrByColumn(const til::CoordType column) const
{
return _data.at(column);
return _data.at(gsl::narrow<uint16_t>(column));
}
// Routine Description:
@@ -74,7 +74,7 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks() const
// - attr - Attribute (color) to fill remaining characters with
// Return Value:
// - <none>
bool ATTR_ROW::SetAttrToEnd(const uint16_t beginIndex, const TextAttribute attr)
bool ATTR_ROW::SetAttrToEnd(const til::CoordType beginIndex, const TextAttribute attr)
{
_data.replace(gsl::narrow<uint16_t>(beginIndex), _data.size(), attr);
return true;
@@ -103,9 +103,9 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
// - newAttr: The attribute to merge into this row.
// Return Value:
// - <none>
void ATTR_ROW::Replace(const uint16_t beginIndex, const uint16_t endIndex, const TextAttribute& newAttr)
void ATTR_ROW::Replace(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
{
_data.replace(beginIndex, endIndex, newAttr);
_data.replace(gsl::narrow<uint16_t>(beginIndex), gsl::narrow<uint16_t>(endIndex), newAttr);
}
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept

View File

@@ -30,7 +30,7 @@ class ATTR_ROW final
public:
using const_iterator = rle_vector::const_iterator;
ATTR_ROW(uint16_t width, TextAttribute attr);
ATTR_ROW(til::CoordType width, TextAttribute attr);
~ATTR_ROW() = default;
@@ -40,13 +40,13 @@ public:
noexcept = default;
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
TextAttribute GetAttrByColumn(uint16_t column) const;
TextAttribute GetAttrByColumn(til::CoordType column) const;
std::vector<uint16_t> GetHyperlinks() const;
bool SetAttrToEnd(uint16_t beginIndex, TextAttribute attr);
bool SetAttrToEnd(til::CoordType beginIndex, TextAttribute attr);
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith);
void Resize(uint16_t newWidth);
void Replace(uint16_t beginIndex, uint16_t endIndex, const TextAttribute& newAttr);
void Resize(til::CoordType newWidth);
void Replace(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
const_iterator begin() const noexcept;
const_iterator end() const noexcept;

View File

@@ -17,7 +17,7 @@
// Note: will through if unable to allocate char/attribute buffers
#pragma warning(push)
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
CharRow::CharRow(til::CoordType rowWidth, ROW* const pParent) noexcept :
_data(rowWidth, value_type()),
_pParent{ FAIL_FAST_IF_NULL(pParent) }
{
@@ -30,9 +30,9 @@ CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
// - <none>
// Return Value:
// - the size of the row
size_t CharRow::size() const noexcept
til::CoordType CharRow::size() const noexcept
{
return _data.size();
return gsl::narrow_cast<til::CoordType>(_data.size());
}
// Routine Description:
@@ -55,7 +55,7 @@ void CharRow::Reset() noexcept
// - newSize - the new width of the character and attributes rows
// Return Value:
// - S_OK on success, otherwise relevant error code
[[nodiscard]] HRESULT CharRow::Resize(const size_t newSize) noexcept
[[nodiscard]] HRESULT CharRow::Resize(const til::CoordType newSize) noexcept
{
try
{
@@ -93,14 +93,14 @@ typename CharRow::const_iterator CharRow::cend() const noexcept
// - <none>
// Return Value:
// - The calculated left boundary of the internal string.
size_t CharRow::MeasureLeft() const noexcept
til::CoordType CharRow::MeasureLeft() const noexcept
{
auto it = _data.cbegin();
while (it != _data.cend() && it->IsSpace())
{
++it;
}
return it - _data.cbegin();
return gsl::narrow_cast<til::CoordType>(it - _data.cbegin());
}
// Routine Description:
@@ -109,17 +109,17 @@ size_t CharRow::MeasureLeft() const noexcept
// - <none>
// Return Value:
// - The calculated right boundary of the internal string.
size_t CharRow::MeasureRight() const
til::CoordType CharRow::MeasureRight() const
{
auto it = _data.crbegin();
while (it != _data.crend() && it->IsSpace())
{
++it;
}
return _data.crend() - it;
return gsl::narrow_cast<til::CoordType>(_data.crend() - it);
}
void CharRow::ClearCell(const size_t column)
void CharRow::ClearCell(const til::CoordType column)
{
_data.at(column).Reset();
}
@@ -149,7 +149,7 @@ bool CharRow::ContainsText() const noexcept
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
const DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column) const
{
return _data.at(column).DbcsAttr();
}
@@ -161,7 +161,7 @@ const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column)
{
return _data.at(column).DbcsAttr();
}
@@ -173,7 +173,7 @@ DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
// Return Value:
// - <none>
// Note: will throw exception if column is out of bounds
void CharRow::ClearGlyph(const size_t column)
void CharRow::ClearGlyph(const til::CoordType column)
{
_data.at(column).EraseChars();
}
@@ -185,9 +185,9 @@ void CharRow::ClearGlyph(const size_t column)
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
const CharRow::reference CharRow::GlyphAt(const size_t column) const
const CharRow::reference CharRow::GlyphAt(const til::CoordType column) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
return { const_cast<CharRow&>(*this), column };
}
@@ -198,9 +198,9 @@ const CharRow::reference CharRow::GlyphAt(const size_t column) const
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
CharRow::reference CharRow::GlyphAt(const size_t column)
CharRow::reference CharRow::GlyphAt(const til::CoordType column)
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
return { *this, column };
}
@@ -209,7 +209,7 @@ std::wstring CharRow::GetText() const
std::wstring wstr;
wstr.reserve(_data.size());
for (size_t i = 0; i < _data.size(); ++i)
for (til::CoordType i = 0; i < gsl::narrow_cast<til::CoordType>(_data.size()); ++i)
{
const auto glyph = GlyphAt(i);
if (!DbcsAttrAt(i).IsTrailing())
@@ -231,9 +231,9 @@ std::wstring CharRow::GetText() const
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass CharRow::DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
const DelimiterClass CharRow::DelimiterClassAt(const til::CoordType column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
const auto glyph = *GlyphAt(column).begin();
if (glyph <= UNICODE_SPACE)
@@ -265,10 +265,10 @@ const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
// Arguments:
// - column - the column to generate the key for
// Return Value:
// - the COORD key for data access from UnicodeStorage for the column
COORD CharRow::GetStorageKey(const size_t column) const noexcept
// - the til::point key for data access from UnicodeStorage for the column
til::point CharRow::GetStorageKey(const til::CoordType column) const noexcept
{
return { gsl::narrow<SHORT>(column), _pParent->GetId() };
return { column, _pParent->GetId() };
}
// Routine Description:

View File

@@ -54,22 +54,22 @@ public:
using const_reverse_iterator = typename boost::container::small_vector_base<value_type>::const_reverse_iterator;
using reference = typename CharRowCellReference;
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
CharRow(til::CoordType rowWidth, ROW* const pParent) noexcept;
size_t size() const noexcept;
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
size_t MeasureLeft() const noexcept;
size_t MeasureRight() const;
til::CoordType size() const noexcept;
[[nodiscard]] HRESULT Resize(const til::CoordType newSize) noexcept;
til::CoordType MeasureLeft() const noexcept;
til::CoordType MeasureRight() const;
bool ContainsText() const noexcept;
const DbcsAttribute& DbcsAttrAt(const size_t column) const;
DbcsAttribute& DbcsAttrAt(const size_t column);
void ClearGlyph(const size_t column);
const DbcsAttribute& DbcsAttrAt(const til::CoordType column) const;
DbcsAttribute& DbcsAttrAt(const til::CoordType column);
void ClearGlyph(const til::CoordType column);
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const;
const DelimiterClass DelimiterClassAt(const til::CoordType column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const reference GlyphAt(const size_t column) const;
reference GlyphAt(const size_t column);
const reference GlyphAt(const til::CoordType column) const;
reference GlyphAt(const til::CoordType column);
// iterators
iterator begin() noexcept;
@@ -82,7 +82,7 @@ public:
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
COORD GetStorageKey(const size_t column) const noexcept;
til::point GetStorageKey(const til::CoordType column) const noexcept;
void UpdateParent(ROW* const pParent);
@@ -91,7 +91,7 @@ public:
private:
void Reset() noexcept;
void ClearCell(const size_t column);
void ClearCell(const til::CoordType column);
std::wstring GetText() const;
protected:

View File

@@ -25,7 +25,7 @@ class CharRowCellReference final
public:
using const_iterator = const wchar_t*;
CharRowCellReference(CharRow& parent, const size_t index) noexcept :
CharRowCellReference(CharRow& parent, const til::CoordType index) noexcept :
_parent{ parent },
_index{ index }
{
@@ -51,7 +51,7 @@ private:
// what char row the object belongs to
CharRow& _parent;
// the index of the cell in the parent char row
const size_t _index;
til::CoordType _index;
CharRowCell& _cellData();
const CharRowCell& _cellData() const;

View File

@@ -21,16 +21,16 @@ enum class LineRendition
DoubleHeightBottom
};
constexpr SMALL_RECT ScreenToBufferLine(const SMALL_RECT& line, const LineRendition lineRendition)
constexpr til::inclusive_rect ScreenToBufferLine(const til::inclusive_rect& line, const LineRendition lineRendition)
{
// Use shift right to quickly divide the Left and Right by 2 for double width lines.
const SHORT scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
return { line.Left >> scale, line.Top, line.Right >> scale, line.Bottom };
}
constexpr SMALL_RECT BufferToScreenLine(const SMALL_RECT& line, const LineRendition lineRendition)
constexpr til::inclusive_rect BufferToScreenLine(const til::inclusive_rect& line, const LineRendition lineRendition)
{
// Use shift left to quickly multiply the Left and Right by 2 for double width lines.
const SHORT scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
return { line.Left << scale, line.Top, (line.Right << scale) + scale, line.Bottom };
}

View File

@@ -531,16 +531,16 @@ OutputCellView OutputCellIterator::s_GenerateView(const OutputCell& cell)
// - Gets the distance between two iterators relative to the input data given in.
// Return Value:
// - The number of items of the input run consumed between these two iterators.
ptrdiff_t OutputCellIterator::GetInputDistance(OutputCellIterator other) const noexcept
til::CoordType OutputCellIterator::GetInputDistance(OutputCellIterator other) const noexcept
{
return _pos - other._pos;
return gsl::narrow_cast<til::CoordType>(_pos - other._pos);
}
// Routine Description:
// - Gets the distance between two iterators relative to the number of cells inserted.
// Return Value:
// - The number of cells in the backing buffer filled between these two iterators.
ptrdiff_t OutputCellIterator::GetCellDistance(OutputCellIterator other) const noexcept
til::CoordType OutputCellIterator::GetCellDistance(OutputCellIterator other) const noexcept
{
return _distance - other._distance;
return gsl::narrow_cast<til::CoordType>(_distance - other._distance);
}

View File

@@ -29,7 +29,7 @@ class OutputCellIterator final
public:
using iterator_category = std::input_iterator_tag;
using value_type = OutputCellView;
using difference_type = ptrdiff_t;
using difference_type = til::CoordType;
using pointer = OutputCellView*;
using reference = OutputCellView&;
@@ -48,9 +48,9 @@ public:
operator bool() const noexcept;
ptrdiff_t GetCellDistance(OutputCellIterator other) const noexcept;
ptrdiff_t GetInputDistance(OutputCellIterator other) const noexcept;
friend ptrdiff_t operator-(OutputCellIterator one, OutputCellIterator two) = delete;
til::CoordType GetCellDistance(OutputCellIterator other) const noexcept;
til::CoordType GetInputDistance(OutputCellIterator other) const noexcept;
friend til::CoordType operator-(OutputCellIterator one, OutputCellIterator two) = delete;
OutputCellIterator& operator++();
OutputCellIterator operator++(int);

View File

@@ -21,14 +21,11 @@ OutputCellRect::OutputCellRect() noexcept :
// Arguments:
// - rows - Rows in the rectangle (height)
// - cols - Columns in the rectangle (width)
OutputCellRect::OutputCellRect(const size_t rows, const size_t cols) :
OutputCellRect::OutputCellRect(const til::CoordType rows, const til::CoordType cols) :
_rows(rows),
_cols(cols)
{
size_t totalCells;
THROW_IF_FAILED(SizeTMult(rows, cols, &totalCells));
_storage.resize(totalCells);
_storage.resize(gsl::narrow<size_t>(rows * cols));
}
// Routine Description:
@@ -37,7 +34,7 @@ OutputCellRect::OutputCellRect(const size_t rows, const size_t cols) :
// - row - The Y position or row index in the buffer.
// Return Value:
// - Read/write span of OutputCells
gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
gsl::span<OutputCell> OutputCellRect::GetRow(const til::CoordType row)
{
return gsl::span<OutputCell>(_FindRowOffset(row), _cols);
}
@@ -48,7 +45,7 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
// - row - The Y position or row index in the buffer.
// Return Value:
// - Read-only iterator of OutputCells
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
OutputCellIterator OutputCellRect::GetRowIter(const til::CoordType row) const
{
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
@@ -62,9 +59,9 @@ OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
// - row - The Y position or row index in the buffer.
// Return Value:
// - Pointer to the location in the rectangle that represents the start of the requested row.
OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
OutputCell* OutputCellRect::_FindRowOffset(const til::CoordType row)
{
return &_storage.at(row * _cols);
return &_storage.at(gsl::narrow_cast<size_t>(row * _cols));
}
// Routine Description:
@@ -74,16 +71,16 @@ OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
// - row - The Y position or row index in the buffer.
// Return Value:
// - Pointer to the location in the rectangle that represents the start of the requested row.
const OutputCell* OutputCellRect::_FindRowOffset(const size_t row) const
const OutputCell* OutputCellRect::_FindRowOffset(const til::CoordType row) const
{
return &_storage.at(row * _cols);
return &_storage.at(gsl::narrow_cast<size_t>(row * _cols));
}
// Routine Description:
// - Gets the height of the rectangle
// Return Value:
// - Height
size_t OutputCellRect::Height() const noexcept
til::CoordType OutputCellRect::Height() const noexcept
{
return _rows;
}
@@ -92,7 +89,7 @@ size_t OutputCellRect::Height() const noexcept
// - Gets the width of the rectangle
// Return Value:
// - Width
size_t OutputCellRect::Width() const noexcept
til::CoordType OutputCellRect::Width() const noexcept
{
return _cols;
}

View File

@@ -30,20 +30,20 @@ class OutputCellRect final
{
public:
OutputCellRect() noexcept;
OutputCellRect(const size_t rows, const size_t cols);
OutputCellRect(const til::CoordType rows, const til::CoordType cols);
gsl::span<OutputCell> GetRow(const size_t row);
OutputCellIterator GetRowIter(const size_t row) const;
gsl::span<OutputCell> GetRow(const til::CoordType row);
OutputCellIterator GetRowIter(const til::CoordType row) const;
size_t Height() const noexcept;
size_t Width() const noexcept;
til::CoordType Height() const noexcept;
til::CoordType Width() const noexcept;
private:
std::vector<OutputCell> _storage;
OutputCell* _FindRowOffset(const size_t row);
const OutputCell* _FindRowOffset(const size_t row) const;
OutputCell* _FindRowOffset(const til::CoordType row);
const OutputCell* _FindRowOffset(const til::CoordType row) const;
size_t _cols;
size_t _rows;
til::CoordType _cols;
til::CoordType _rows;
};

View File

@@ -38,7 +38,7 @@ OutputCellView::OutputCellView(const std::wstring_view view,
// - Reports how many columns we expect the Chars() text data to consume
// Return Value:
// - Count of column cells on the screen
size_t OutputCellView::Columns() const noexcept
til::CoordType OutputCellView::Columns() const noexcept
{
if (DbcsAttr().IsSingle())
{

View File

@@ -31,7 +31,7 @@ public:
const TextAttributeBehavior behavior) noexcept;
const std::wstring_view& Chars() const noexcept;
size_t Columns() const noexcept;
til::CoordType Columns() const noexcept;
DbcsAttribute DbcsAttr() const noexcept;
TextAttribute TextAttr() const noexcept;
TextAttributeBehavior TextAttrBehavior() const noexcept;

View File

@@ -16,7 +16,7 @@
// - pParent - the text buffer that this row belongs to
// Return Value:
// - constructed object
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
ROW::ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
_id{ rowId },
_rowWidth{ rowWidth },
_charRow{ rowWidth, this },
@@ -58,7 +58,7 @@ bool ROW::Reset(const TextAttribute Attr)
// - width - the new width, in cells
// Return Value:
// - S_OK if successful, otherwise relevant error
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
[[nodiscard]] HRESULT ROW::Resize(const til::CoordType width)
{
RETURN_IF_FAILED(_charRow.Resize(width));
try
@@ -78,7 +78,7 @@ bool ROW::Reset(const TextAttribute Attr)
// - column - 0-indexed column index
// Return Value:
// - <none>
void ROW::ClearColumn(const size_t column)
void ROW::ClearColumn(const til::CoordType column)
{
THROW_HR_IF(E_INVALIDARG, column >= _charRow.size());
_charRow.ClearCell(column);
@@ -103,7 +103,7 @@ const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
// - limitRight - right inclusive column ID for the last write in this row. (optional, will just write to the end of row if nullopt)
// Return Value:
// - iterator to first cell that was not written to this row.
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight)
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType index, const std::optional<bool> wrap, std::optional<til::CoordType> limitRight)
{
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());

View File

@@ -32,9 +32,9 @@ class TextBuffer;
class ROW final
{
public:
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
size_t size() const noexcept { return _rowWidth; }
til::CoordType size() const noexcept { return _rowWidth; }
void SetWrapForced(const bool wrap) noexcept { _wrapForced = wrap; }
bool WasWrapForced() const noexcept { return _wrapForced; }
@@ -51,19 +51,19 @@ public:
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
SHORT GetId() const noexcept { return _id; }
void SetId(const SHORT id) noexcept { _id = id; }
til::CoordType GetId() const noexcept { return _id; }
void SetId(const til::CoordType id) noexcept { _id = id; }
bool Reset(const TextAttribute Attr);
[[nodiscard]] HRESULT Resize(const unsigned short width);
[[nodiscard]] HRESULT Resize(const til::CoordType width);
void ClearColumn(const size_t column);
void ClearColumn(const til::CoordType column);
std::wstring GetText() const { return _charRow.GetText(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
OutputCellIterator WriteCells(OutputCellIterator it, const til::CoordType index, const std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
#ifdef UNIT_TESTING
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
@@ -74,8 +74,8 @@ private:
CharRow _charRow;
ATTR_ROW _attrRow;
LineRendition _lineRendition;
SHORT _id;
unsigned short _rowWidth;
til::CoordType _id;
til::CoordType _rowWidth;
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
bool _wrapForced;
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line

View File

@@ -47,7 +47,7 @@ void UnicodeStorage::Erase(const key_type key) noexcept
// - rowMap - A map of the old row IDs to the new row IDs.
// - width - The width of the new row. Remove any items that are beyond the row width.
// - Use nullopt if we're not resizing the width of the row, just renumbering the rows.
void UnicodeStorage::Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width)
void UnicodeStorage::Remap(const std::unordered_map<til::CoordType, til::CoordType>& rowMap, const std::optional<til::CoordType> width)
{
// Make a temporary map to hold all the new row positioning
std::unordered_map<key_type, mapped_type> newMap;
@@ -87,7 +87,7 @@ void UnicodeStorage::Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const
const auto newRowId = mapIter->second;
// Generate a new coordinate with the same X as the old one, but a new Y value.
const auto newCoord = COORD{ oldCoord.X, newRowId };
const auto newCoord = til::point{ oldCoord.X, newRowId };
// Put the adjusted coordinate into the map with the original value.
newMap.emplace(newCoord, pair.second);

View File

@@ -20,11 +20,11 @@ Author(s):
#include <til/bit.h>
#include <til/hash.h>
// std::unordered_map needs help to know how to hash a COORD
// std::unordered_map needs help to know how to hash a til::point
namespace std
{
template<>
struct hash<COORD>
struct hash<til::point>
{
// Routine Description:
// - hashes a coord. coord will be hashed by storing the x and y values consecutively in the lower
@@ -33,9 +33,9 @@ namespace std
// - coord - the coord to hash
// Return Value:
// - the hashed coord
constexpr size_t operator()(const COORD& coord) const noexcept
constexpr size_t operator()(const til::point coord) const noexcept
{
return til::hash(til::bit_cast<uint32_t>(coord));
return til::hash(til::bit_cast<uint64_t>(coord));
}
};
}
@@ -43,7 +43,7 @@ namespace std
class UnicodeStorage final
{
public:
using key_type = typename COORD;
using key_type = typename til::point;
using mapped_type = typename std::vector<wchar_t>;
UnicodeStorage() noexcept;
@@ -54,7 +54,7 @@ public:
void Erase(const key_type key) noexcept;
void Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width);
void Remap(const std::unordered_map<til::CoordType, til::CoordType>& rowMap, const std::optional<til::CoordType> width);
private:
std::unordered_map<key_type, mapped_type> _map;

View File

@@ -13,7 +13,6 @@
// - ulSize - The height of the cursor within this buffer
Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
_parentBuffer{ parentBuffer },
_cPosition{ 0 },
_fHasMoved(false),
_fIsVisible(true),
_fIsOn(true),
@@ -23,7 +22,6 @@ Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
_fIsConversionArea(false),
_fIsPopupShown(false),
_fDelayedEolWrap(false),
_coordDelayedAt{ 0 },
_fDeferCursorRedraw(false),
_fHaveDeferredCursorRedraw(false),
_ulSize(ulSize),
@@ -35,7 +33,7 @@ Cursor::~Cursor()
{
}
COORD Cursor::GetPosition() const noexcept
til::point Cursor::GetPosition() const noexcept
{
return _cPosition;
}
@@ -192,59 +190,58 @@ void Cursor::_RedrawCursorAlways() noexcept
CATCH_LOG();
}
void Cursor::SetPosition(const COORD cPosition) noexcept
void Cursor::SetPosition(const til::point cPosition) noexcept
{
_RedrawCursor();
_cPosition.X = cPosition.X;
_cPosition.Y = cPosition.Y;
_cPosition = cPosition;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::SetXPosition(const int NewX) noexcept
void Cursor::SetXPosition(const til::CoordType NewX) noexcept
{
_RedrawCursor();
_cPosition.X = gsl::narrow<SHORT>(NewX);
_cPosition.X = NewX;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::SetYPosition(const int NewY) noexcept
void Cursor::SetYPosition(const til::CoordType NewY) noexcept
{
_RedrawCursor();
_cPosition.Y = gsl::narrow<SHORT>(NewY);
_cPosition.Y = NewY;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::IncrementXPosition(const int DeltaX) noexcept
void Cursor::IncrementXPosition(const til::CoordType DeltaX) noexcept
{
_RedrawCursor();
_cPosition.X += gsl::narrow<SHORT>(DeltaX);
_cPosition.X += DeltaX;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::IncrementYPosition(const int DeltaY) noexcept
void Cursor::IncrementYPosition(const til::CoordType DeltaY) noexcept
{
_RedrawCursor();
_cPosition.Y += gsl::narrow<SHORT>(DeltaY);
_cPosition.Y += DeltaY;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::DecrementXPosition(const int DeltaX) noexcept
void Cursor::DecrementXPosition(const til::CoordType DeltaX) noexcept
{
_RedrawCursor();
_cPosition.X -= gsl::narrow<SHORT>(DeltaX);
_cPosition.X -= DeltaX;
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::DecrementYPosition(const int DeltaY) noexcept
void Cursor::DecrementYPosition(const til::CoordType DeltaY) noexcept
{
_RedrawCursor();
_cPosition.Y -= gsl::narrow<SHORT>(DeltaY);
_cPosition.Y -= DeltaY;
_RedrawCursor();
ResetDelayEOLWrap();
}
@@ -284,7 +281,7 @@ void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept
_cursorType = OtherCursor._cursorType;
}
void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept
void Cursor::DelayEOLWrap(const til::point coordDelayedAt) noexcept
{
_coordDelayedAt = coordDelayedAt;
_fDelayedEolWrap = true;
@@ -292,11 +289,11 @@ void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept
void Cursor::ResetDelayEOLWrap() noexcept
{
_coordDelayedAt = { 0 };
_coordDelayedAt = {};
_fDelayedEolWrap = false;
}
COORD Cursor::GetDelayedAtPosition() const noexcept
til::point Cursor::GetDelayedAtPosition() const noexcept
{
return _coordDelayedAt;
}

View File

@@ -47,7 +47,7 @@ public:
bool IsPopupShown() const noexcept;
bool GetDelay() const noexcept;
ULONG GetSize() const noexcept;
COORD GetPosition() const noexcept;
til::point GetPosition() const noexcept;
const CursorType GetType() const noexcept;
@@ -66,19 +66,19 @@ public:
void SetSize(const ULONG ulSize) noexcept;
void SetStyle(const ULONG ulSize, const CursorType type) noexcept;
void SetPosition(const COORD cPosition) noexcept;
void SetXPosition(const int NewX) noexcept;
void SetYPosition(const int NewY) noexcept;
void IncrementXPosition(const int DeltaX) noexcept;
void IncrementYPosition(const int DeltaY) noexcept;
void DecrementXPosition(const int DeltaX) noexcept;
void DecrementYPosition(const int DeltaY) noexcept;
void SetPosition(const til::point cPosition) noexcept;
void SetXPosition(const til::CoordType NewX) noexcept;
void SetYPosition(const til::CoordType NewY) noexcept;
void IncrementXPosition(const til::CoordType DeltaX) noexcept;
void IncrementYPosition(const til::CoordType DeltaY) noexcept;
void DecrementXPosition(const til::CoordType DeltaX) noexcept;
void DecrementYPosition(const til::CoordType DeltaY) noexcept;
void CopyProperties(const Cursor& OtherCursor) noexcept;
void DelayEOLWrap(const COORD coordDelayedAt) noexcept;
void DelayEOLWrap(const til::point coordDelayedAt) noexcept;
void ResetDelayEOLWrap() noexcept;
COORD GetDelayedAtPosition() const noexcept;
til::point GetDelayedAtPosition() const noexcept;
bool IsDelayedEOLWrap() const noexcept;
void SetType(const CursorType type) noexcept;
@@ -90,7 +90,7 @@ private:
// NOTE: If you are adding a property here, go add it to CopyProperties.
COORD _cPosition; // current position on screen (in screen buffer coords).
til::point _cPosition; // current position on screen (in screen buffer coords).
bool _fHasMoved;
bool _fIsVisible; // whether cursor is visible (set only through the API)
@@ -102,7 +102,7 @@ private:
bool _fIsPopupShown; // if a popup is being shown, turn off, stop blinking.
bool _fDelayedEolWrap; // don't wrap at EOL till the next char comes in.
COORD _coordDelayedAt; // coordinate the EOL wrap was delayed at.
til::point _coordDelayedAt; // coordinate the EOL wrap was delayed at.
bool _fDeferCursorRedraw; // whether we should defer redrawing the cursor or not
bool _fHaveDeferredCursorRedraw; // have we been asked to redraw the cursor while it was being deferred?

View File

@@ -35,7 +35,6 @@ Abstract:
#include <intsafe.h>
// private dependencies
#include "../inc/operators.hpp"
#include "../inc/unicode.hpp"
#pragma warning(pop)

View File

@@ -50,7 +50,7 @@ Search::Search(IUiaData& uiaData,
const std::wstring& str,
const Direction direction,
const Sensitivity sensitivity,
const COORD anchor) :
const til::point anchor) :
_direction(direction),
_sensitivity(sensitivity),
_needle(s_CreateNeedleFromString(str)),
@@ -124,7 +124,7 @@ void Search::Color(const TextAttribute attr) const
// been called and returned true.
// Return Value:
// - pair containing [start, end] coord positions of text found by search
std::pair<COORD, COORD> Search::GetFoundLocation() const noexcept
std::pair<til::point, til::point> Search::GetFoundLocation() const noexcept
{
return { _coordSelStart, _coordSelEnd };
}
@@ -140,7 +140,7 @@ std::pair<COORD, COORD> Search::GetFoundLocation() const noexcept
// - direction - The intended direction of the search
// Return Value:
// - Coordinate to start the search from.
COORD Search::s_GetInitialAnchor(const IUiaData& uiaData, const Direction direction)
til::point Search::s_GetInitialAnchor(const IUiaData& uiaData, const Direction direction)
{
const auto& textBuffer = uiaData.GetTextBuffer();
const auto textBufferEndPosition = uiaData.GetTextBufferEndPosition();
@@ -187,10 +187,10 @@ COORD Search::s_GetInitialAnchor(const IUiaData& uiaData, const Direction direct
// - end - If we found it, this is filled with the coordinate of the last character of the needle.
// Return Value:
// - True if we found it. False if not.
bool Search::_FindNeedleInHaystackAt(const COORD pos, COORD& start, COORD& end) const
bool Search::_FindNeedleInHaystackAt(const til::point pos, til::point& start, til::point& end) const
{
start = { 0 };
end = { 0 };
start = {};
end = {};
auto bufferPos = pos;
@@ -269,7 +269,7 @@ wchar_t Search::_ApplySensitivity(const wchar_t wch) const noexcept
// - Helper to increment a coordinate in respect to the associated screen buffer
// Arguments
// - coord - Updated by function to increment one position (will wrap X and Y direction)
void Search::_IncrementCoord(COORD& coord) const noexcept
void Search::_IncrementCoord(til::point& coord) const noexcept
{
_uiaData.GetTextBuffer().GetSize().IncrementInBoundsCircular(coord);
}
@@ -278,7 +278,7 @@ void Search::_IncrementCoord(COORD& coord) const noexcept
// - Helper to decrement a coordinate in respect to the associated screen buffer
// Arguments
// - coord - Updated by function to decrement one position (will wrap X and Y direction)
void Search::_DecrementCoord(COORD& coord) const noexcept
void Search::_DecrementCoord(til::point& coord) const noexcept
{
_uiaData.GetTextBuffer().GetSize().DecrementInBoundsCircular(coord);
}
@@ -314,7 +314,7 @@ void Search::_UpdateNextPosition()
{
if (_direction == Direction::Forward)
{
_coordNext = { 0 };
_coordNext = {};
}
else
{

View File

@@ -49,33 +49,33 @@ public:
const std::wstring& str,
const Direction dir,
const Sensitivity sensitivity,
const COORD anchor);
const til::point anchor);
bool FindNext();
void Select() const;
void Color(const TextAttribute attr) const;
std::pair<COORD, COORD> GetFoundLocation() const noexcept;
std::pair<til::point, til::point> GetFoundLocation() const noexcept;
private:
wchar_t _ApplySensitivity(const wchar_t wch) const noexcept;
bool _FindNeedleInHaystackAt(const COORD pos, COORD& start, COORD& end) const;
bool _FindNeedleInHaystackAt(const til::point pos, til::point& start, til::point& end) const;
bool _CompareChars(const std::wstring_view one, const std::wstring_view two) const noexcept;
void _UpdateNextPosition();
void _IncrementCoord(COORD& coord) const noexcept;
void _DecrementCoord(COORD& coord) const noexcept;
void _IncrementCoord(til::point& coord) const noexcept;
void _DecrementCoord(til::point& coord) const noexcept;
static COORD s_GetInitialAnchor(const Microsoft::Console::Types::IUiaData& uiaData, const Direction dir);
static til::point s_GetInitialAnchor(const Microsoft::Console::Types::IUiaData& uiaData, const Direction dir);
static std::vector<std::vector<wchar_t>> s_CreateNeedleFromString(const std::wstring& wstr);
bool _reachedEnd = false;
COORD _coordNext = { 0 };
COORD _coordSelStart = { 0 };
COORD _coordSelEnd = { 0 };
til::point _coordNext;
til::point _coordSelStart;
til::point _coordSelEnd;
const COORD _coordAnchor;
const til::point _coordAnchor;
const std::vector<std::vector<wchar_t>> _needle;
const Direction _direction;
const Sensitivity _sensitivity;

View File

@@ -30,7 +30,7 @@ using PointTree = interval_tree::IntervalTree<til::point, size_t>;
// Return Value:
// - constructed object
// Note: may throw exception
TextBuffer::TextBuffer(const COORD screenBufferSize,
TextBuffer::TextBuffer(const til::size screenBufferSize,
const TextAttribute defaultAttributes,
const UINT cursorSize,
const bool isActiveBuffer,
@@ -47,10 +47,10 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
_currentPatternId{ 0 }
{
// initialize ROWs
_storage.reserve(static_cast<size_t>(screenBufferSize.Y));
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
_storage.reserve(gsl::narrow<size_t>(screenBufferSize.Y));
for (til::CoordType i = 0; i < screenBufferSize.Y; ++i)
{
_storage.emplace_back(static_cast<SHORT>(i), screenBufferSize.X, _currentAttributes, this);
_storage.emplace_back(i, screenBufferSize.X, _currentAttributes, this);
}
_UpdateSize();
@@ -74,9 +74,9 @@ void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) noexcept
// - <none>
// Return Value:
// - Total number of rows in the buffer
UINT TextBuffer::TotalRowCount() const noexcept
til::CoordType TextBuffer::TotalRowCount() const noexcept
{
return gsl::narrow<UINT>(_storage.size());
return gsl::narrow_cast<til::CoordType>(_storage.size());
}
// Routine Description:
@@ -86,13 +86,11 @@ UINT TextBuffer::TotalRowCount() const noexcept
// - Number of rows down from the first row of the buffer.
// Return Value:
// - const reference to the requested row. Asserts if out of bounds.
const ROW& TextBuffer::GetRowByOffset(const size_t index) const
const ROW& TextBuffer::GetRowByOffset(const til::CoordType index) const noexcept
{
const size_t totalRows = TotalRowCount();
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const auto offsetIndex = (_firstRow + index) % totalRows;
return _storage.at(offsetIndex);
const auto offsetIndex = gsl::narrow_cast<size_t>(_firstRow + index) % _storage.size();
return til::at(_storage, offsetIndex);
}
// Routine Description:
@@ -102,13 +100,11 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
// - Number of rows down from the first row of the buffer.
// Return Value:
// - reference to the requested row. Asserts if out of bounds.
ROW& TextBuffer::GetRowByOffset(const size_t index)
ROW& TextBuffer::GetRowByOffset(const til::CoordType index) noexcept
{
const size_t totalRows = TotalRowCount();
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const auto offsetIndex = (_firstRow + index) % totalRows;
return _storage.at(offsetIndex);
const auto offsetIndex = gsl::narrow_cast<size_t>(_firstRow + index) % _storage.size();
return til::at(_storage, offsetIndex);
}
// Routine Description:
@@ -117,7 +113,7 @@ ROW& TextBuffer::GetRowByOffset(const size_t index)
// - at - X,Y position in buffer for iterator start position
// Return Value:
// - Read-only iterator of text data only.
TextBufferTextIterator TextBuffer::GetTextDataAt(const COORD at) const
TextBufferTextIterator TextBuffer::GetTextDataAt(const til::point at) const
{
return TextBufferTextIterator(GetCellDataAt(at));
}
@@ -128,7 +124,7 @@ TextBufferTextIterator TextBuffer::GetTextDataAt(const COORD at) const
// - at - X,Y position in buffer for iterator start position
// Return Value:
// - Read-only iterator of cell data.
TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at) const
TextBufferCellIterator TextBuffer::GetCellDataAt(const til::point at) const
{
return TextBufferCellIterator(*this, at);
}
@@ -140,7 +136,7 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at) const
// - at - X,Y position in buffer for iterator start position
// Return Value:
// - Read-only iterator of text data only.
TextBufferTextIterator TextBuffer::GetTextLineDataAt(const COORD at) const
TextBufferTextIterator TextBuffer::GetTextLineDataAt(const til::point at) const
{
return TextBufferTextIterator(GetCellLineDataAt(at));
}
@@ -152,9 +148,9 @@ TextBufferTextIterator TextBuffer::GetTextLineDataAt(const COORD at) const
// - at - X,Y position in buffer for iterator start position
// Return Value:
// - Read-only iterator of cell data.
TextBufferCellIterator TextBuffer::GetCellLineDataAt(const COORD at) const
TextBufferCellIterator TextBuffer::GetCellLineDataAt(const til::point at) const
{
SMALL_RECT limit;
til::inclusive_rect limit;
limit.Top = at.Y;
limit.Bottom = at.Y;
limit.Left = 0;
@@ -171,7 +167,7 @@ TextBufferCellIterator TextBuffer::GetCellLineDataAt(const COORD at) const
// - limit - boundaries for the iterator to operate within
// Return Value:
// - Read-only iterator of text data only.
TextBufferTextIterator TextBuffer::GetTextDataAt(const COORD at, const Viewport limit) const
TextBufferTextIterator TextBuffer::GetTextDataAt(const til::point at, const Viewport limit) const
{
return TextBufferTextIterator(GetCellDataAt(at, limit));
}
@@ -184,7 +180,7 @@ TextBufferTextIterator TextBuffer::GetTextDataAt(const COORD at, const Viewport
// - limit - boundaries for the iterator to operate within
// Return Value:
// - Read-only iterator of cell data.
TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at, const Viewport limit) const
TextBufferCellIterator TextBuffer::GetCellDataAt(const til::point at, const Viewport limit) const
{
return TextBufferCellIterator(*this, at, limit);
}
@@ -337,7 +333,7 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt)
// Return Value:
// - The final position of the iterator
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
const COORD target,
const til::point target,
const std::optional<bool> wrap)
{
// Make mutable copy so we can walk.
@@ -374,9 +370,9 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
// Return Value:
// - The iterator, but advanced to where we stopped writing. Use to find input consumed length or cells written length.
OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
const COORD target,
const til::point target,
const std::optional<bool> wrap,
std::optional<size_t> limitRight)
std::optional<til::CoordType> limitRight)
{
// If we're not in bounds, exit early.
if (!GetSize().IsInBounds(target))
@@ -390,7 +386,7 @@ OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
// Take the cell distance written and notify that it needs to be repainted.
const auto written = newIt.GetCellDistance(givenIt);
const auto paint = Viewport::FromDimensions(target, { gsl::narrow<SHORT>(written), 1 });
const auto paint = Viewport::FromDimensions(target, { written, 1 });
TriggerRedraw(paint);
return newIt;
@@ -467,7 +463,7 @@ bool TextBuffer::InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttr
// - <none> - Always sets to wrap
//Return Value:
// - <none>
void TextBuffer::_SetWrapOnCurrentRow()
void TextBuffer::_SetWrapOnCurrentRow() noexcept
{
_AdjustWrapOnCurrentRow(true);
}
@@ -479,10 +475,10 @@ void TextBuffer::_SetWrapOnCurrentRow()
// - fSet - True if this row has a wrap. False otherwise.
//Return Value:
// - <none>
void TextBuffer::_AdjustWrapOnCurrentRow(const bool fSet)
void TextBuffer::_AdjustWrapOnCurrentRow(const bool fSet) noexcept
{
// The vertical position of the cursor represents the current row we're manipulating.
const UINT uiCurrentRowOffset = GetCursor().GetPosition().Y;
const auto uiCurrentRowOffset = GetCursor().GetPosition().Y;
// Set the wrap status as appropriate
GetRowByOffset(uiCurrentRowOffset).SetWrapForced(fSet);
@@ -501,7 +497,7 @@ bool TextBuffer::IncrementCursor()
// Cursor position is stored as logical array indices (starts at 0) for the window
// Buffer Size is specified as the "length" of the array. It would say 80 for valid values of 0-79.
// So subtract 1 from buffer size in each direction to find the index of the final column in the buffer
const short iFinalColumnIndex = GetLineWidth(GetCursor().GetPosition().Y) - 1;
const auto iFinalColumnIndex = GetLineWidth(GetCursor().GetPosition().Y) - 1;
// Move the cursor one position to the right
GetCursor().IncrementXPosition(1);
@@ -604,17 +600,17 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode)
// - The viewport
//Return value:
// - Coordinate position (relative to the text buffer)
COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional) const
til::point TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional) const
{
const auto viewport = viewOptional.has_value() ? viewOptional.value() : GetSize();
COORD coordEndOfText = { 0 };
til::point coordEndOfText;
// Search the given viewport by starting at the bottom.
coordEndOfText.Y = viewport.BottomInclusive();
const auto& currRow = GetRowByOffset(coordEndOfText.Y);
// The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character)
coordEndOfText.X = gsl::narrow<short>(currRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = currRow.GetCharRow().MeasureRight() - 1;
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
const auto viewportTop = viewport.Top();
@@ -625,13 +621,13 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
const auto& backupRow = GetRowByOffset(coordEndOfText.Y);
// We need to back up to the previous row if this line is empty, AND there are more rows
coordEndOfText.X = gsl::narrow<short>(backupRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = backupRow.GetCharRow().MeasureRight() - 1;
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
}
// don't allow negative results
coordEndOfText.Y = std::max(coordEndOfText.Y, 0i16);
coordEndOfText.X = std::max(coordEndOfText.X, 0i16);
coordEndOfText.Y = std::max(coordEndOfText.Y, 0);
coordEndOfText.X = std::max(coordEndOfText.X, 0);
return coordEndOfText;
}
@@ -643,7 +639,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
// Return Value:
// - Coordinate position in screen coordinates of the character just before the cursor.
// - NOTE: Will return 0,0 if already in the top left corner
COORD TextBuffer::_GetPreviousFromCursor() const
til::point TextBuffer::_GetPreviousFromCursor() const noexcept
{
auto coordPosition = GetCursor().GetPosition();
@@ -668,7 +664,7 @@ COORD TextBuffer::_GetPreviousFromCursor() const
return coordPosition;
}
const SHORT TextBuffer::GetFirstRowIndex() const noexcept
const til::CoordType TextBuffer::GetFirstRowIndex() const noexcept
{
return _firstRow;
}
@@ -680,15 +676,15 @@ const Viewport TextBuffer::GetSize() const noexcept
void TextBuffer::_UpdateSize()
{
_size = Viewport::FromDimensions({ 0, 0 }, { gsl::narrow<SHORT>(_storage.at(0).size()), gsl::narrow<SHORT>(_storage.size()) });
_size = Viewport::FromDimensions({ _storage.at(0).size(), gsl::narrow<til::CoordType>(_storage.size()) });
}
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) noexcept
void TextBuffer::_SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept
{
_firstRow = FirstRowIndex;
}
void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta)
void TextBuffer::ScrollRows(const til::CoordType firstRow, const til::CoordType size, const til::CoordType delta)
{
// If we don't have to move anything, leave early.
if (delta == 0)
@@ -825,9 +821,9 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
const auto fillChar = L' ';
auto fillAttrs = GetCurrentAttributes();
fillAttrs.SetStandardErase();
const size_t fillOffset = GetLineWidth(rowIndex);
const auto fillLength = GetSize().Width() - fillOffset;
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
const auto fillOffset = GetLineWidth(rowIndex);
const auto fillLength = gsl::narrow<size_t>(GetSize().Width() - fillOffset);
const OutputCellIterator fillData{ fillChar, fillAttrs, fillLength };
row.WriteCells(fillData, fillOffset, false);
// We also need to make sure the cursor is clamped within the new width.
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
@@ -836,7 +832,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
}
}
void TextBuffer::ResetLineRenditionRange(const size_t startRow, const size_t endRow)
void TextBuffer::ResetLineRenditionRange(const til::CoordType startRow, const til::CoordType endRow) noexcept
{
for (auto row = startRow; row < endRow; row++)
{
@@ -844,40 +840,40 @@ void TextBuffer::ResetLineRenditionRange(const size_t startRow, const size_t end
}
}
LineRendition TextBuffer::GetLineRendition(const size_t row) const
LineRendition TextBuffer::GetLineRendition(const til::CoordType row) const noexcept
{
return GetRowByOffset(row).GetLineRendition();
}
bool TextBuffer::IsDoubleWidthLine(const size_t row) const
bool TextBuffer::IsDoubleWidthLine(const til::CoordType row) const noexcept
{
return GetLineRendition(row) != LineRendition::SingleWidth;
}
SHORT TextBuffer::GetLineWidth(const size_t row) const
til::CoordType TextBuffer::GetLineWidth(const til::CoordType row) const noexcept
{
// Use shift right to quickly divide the width by 2 for double width lines.
const SHORT scale = IsDoubleWidthLine(row) ? 1 : 0;
const auto scale = IsDoubleWidthLine(row) ? 1 : 0;
return GetSize().Width() >> scale;
}
COORD TextBuffer::ClampPositionWithinLine(const COORD position) const
til::point TextBuffer::ClampPositionWithinLine(const til::point position) const noexcept
{
const SHORT rightmostColumn = GetLineWidth(position.Y) - 1;
const auto rightmostColumn = GetLineWidth(position.Y) - 1;
return { std::min(position.X, rightmostColumn), position.Y };
}
COORD TextBuffer::ScreenToBufferPosition(const COORD position) const
til::point TextBuffer::ScreenToBufferPosition(const til::point position) const noexcept
{
// Use shift right to quickly divide the X pos by 2 for double width lines.
const SHORT scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
return { position.X >> scale, position.Y };
}
COORD TextBuffer::BufferToScreenPosition(const COORD position) const
til::point TextBuffer::BufferToScreenPosition(const til::point position) const noexcept
{
// Use shift left to quickly multiply the X pos by 2 for double width lines.
const SHORT scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
return { position.X << scale, position.Y };
}
@@ -900,7 +896,7 @@ void TextBuffer::Reset()
// - newSize - new size of screen.
// Return Value:
// - Success if successful. Invalid parameter if screen buffer size is unexpected. No memory if allocation failed.
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize) noexcept
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const til::size newSize) noexcept
{
RETURN_HR_IF(E_INVALIDARG, newSize.X < 0 || newSize.Y < 0);
@@ -909,12 +905,12 @@ void TextBuffer::Reset()
const auto currentSize = GetSize().Dimensions();
const auto attributes = GetCurrentAttributes();
SHORT TopRow = 0; // new top row of the screen buffer
til::CoordType TopRow = 0; // new top row of the screen buffer
if (newSize.Y <= GetCursor().GetPosition().Y)
{
TopRow = GetCursor().GetPosition().Y - newSize.Y + 1;
}
const SHORT TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
const auto TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
// rotate rows until the top row is at index 0
for (auto i = 0; i < TopRowIndex; i++)
@@ -934,7 +930,7 @@ void TextBuffer::Reset()
// add rows if we're growing
while (_storage.size() < static_cast<size_t>(newSize.Y))
{
_storage.emplace_back(static_cast<short>(_storage.size()), newSize.X, attributes, this);
_storage.emplace_back(gsl::narrow_cast<til::CoordType>(_storage.size()), newSize.X, attributes, this);
}
// Now that we've tampered with the row placement, refresh all the row IDs.
@@ -983,7 +979,7 @@ void TextBuffer::TriggerRedraw(const Viewport& viewport)
}
}
void TextBuffer::TriggerRedrawCursor(const COORD position)
void TextBuffer::TriggerRedrawCursor(const til::point position)
{
if (_isActiveBuffer)
{
@@ -1007,7 +1003,7 @@ void TextBuffer::TriggerScroll()
}
}
void TextBuffer::TriggerScroll(const COORD delta)
void TextBuffer::TriggerScroll(const til::point delta)
{
if (_isActiveBuffer)
{
@@ -1032,10 +1028,10 @@ void TextBuffer::TriggerNewTextNotification(const std::wstring_view newText)
// any high unicode (UnicodeStorage) runs while we're already looping through the rows.
// Arguments:
// - newRowWidth - Optional new value for the row width.
void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
void TextBuffer::_RefreshRowIDs(std::optional<til::CoordType> newRowWidth)
{
std::unordered_map<SHORT, SHORT> rowMap;
SHORT i = 0;
std::unordered_map<til::CoordType, til::CoordType> rowMap;
til::CoordType i = 0;
for (auto& it : _storage)
{
// Build a map so we can update Unicode Storage
@@ -1065,7 +1061,7 @@ void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
// - <none>
// Return Value:
// - reference to the first row.
ROW& TextBuffer::_GetFirstRow()
ROW& TextBuffer::_GetFirstRow() noexcept
{
return GetRowByOffset(0);
}
@@ -1099,23 +1095,23 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const
DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const
{
return GetRowByOffset(pos.Y).GetCharRow().DelimiterClassAt(pos.X, wordDelimiters);
}
// Method Description:
// - Get the COORD for the beginning of the word you are on
// - Get the til::point for the beginning of the word you are on
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - accessibilityMode - when enabled, we continue expanding left until we are at the beginning of a readable word.
// Otherwise, expand left until a character of a new delimiter class is found
// (or a row boundary is encountered)
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - The COORD for the first character on the "word" (inclusive)
const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
// - The til::point for the first character on the "word" (inclusive)
til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
// Consider a buffer with this text in it:
// " word other "
@@ -1130,7 +1126,7 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
#pragma warning(suppress : 26496)
auto copy{ target };
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (target == bufferSize.Origin())
{
// can't expand left
@@ -1140,12 +1136,12 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
{
// GH#7664: Treat EndExclusive as EndInclusive so
// that it actually points to a space in the buffer
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
copy = bufferSize.BottomRightInclusive();
}
else if (bufferSize.CompareInBounds(target, limit.to_win32_coord(), true) >= 0)
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
// if at/past the limit --> clamp to limit
copy = limitOptional->to_win32_coord();
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
}
if (accessibilityMode)
@@ -1159,13 +1155,13 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
}
// Method Description:
// - Helper method for GetWordStart(). Get the COORD for the beginning of the word (accessibility definition) you are on
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (accessibility definition) you are on
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - The COORD for the first character on the current/previous READABLE "word" (inclusive)
const COORD TextBuffer::_GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const
// - The til::point for the first character on the current/previous READABLE "word" (inclusive)
til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const
{
auto result = target;
const auto bufferSize = GetSize();
@@ -1204,13 +1200,13 @@ const COORD TextBuffer::_GetWordStartForAccessibility(const COORD target, const
}
// Method Description:
// - Helper method for GetWordStart(). Get the COORD for the beginning of the word (selection definition) you are on
// - Helper method for GetWordStart(). Get the til::point for the beginning of the word (selection definition) you are on
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - The COORD for the first character on the current word or delimiter run (stopped by the left margin)
const COORD TextBuffer::_GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const
// - The til::point for the first character on the current word or delimiter run (stopped by the left margin)
til::point TextBuffer::_GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const
{
auto result = target;
const auto bufferSize = GetSize();
@@ -1233,17 +1229,17 @@ const COORD TextBuffer::_GetWordStartForSelection(const COORD target, const std:
}
// Method Description:
// - Get the COORD for the beginning of the NEXT word
// - Get the til::point for the beginning of the NEXT word
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - accessibilityMode - when enabled, we continue expanding right until we are at the beginning of the next READABLE word
// Otherwise, expand right until a character of a new delimiter class is found
// (or a row boundary is encountered)
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - The COORD for the last character on the "word" (inclusive)
const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
// - The til::point for the last character on the "word" (inclusive)
til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
// Consider a buffer with this text in it:
// " word other "
@@ -1257,15 +1253,15 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
// Already at/past the limit. Can't move forward.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
if (bufferSize.CompareInBounds(target, limit.to_win32_coord(), true) >= 0)
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
return target;
}
if (accessibilityMode)
{
return _GetWordEndForAccessibility(target, wordDelimiters, limit.to_win32_coord());
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
}
else
{
@@ -1274,14 +1270,14 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
}
// Method Description:
// - Helper method for GetWordEnd(). Get the COORD for the beginning of the next READABLE word
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the next READABLE word
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - limit - the last "valid" position in the text buffer (to improve performance)
// Return Value:
// - The COORD for the first character of the next readable "word". If no next word, return one past the end of the buffer
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const
// - The til::point for the first character of the next readable "word". If no next word, return one past the end of the buffer
til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const
{
const auto bufferSize{ GetSize() };
auto result{ target };
@@ -1325,13 +1321,13 @@ const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const st
}
// Method Description:
// - Helper method for GetWordEnd(). Get the COORD for the beginning of the NEXT word
// - Helper method for GetWordEnd(). Get the til::point for the beginning of the NEXT word
// Arguments:
// - target - a COORD on the word you are currently on
// - target - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - The COORD for the last character of the current word or delimiter run (stopped by right margin)
const COORD TextBuffer::_GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const
// - The til::point for the last character of the current word or delimiter run (stopped by right margin)
til::point TextBuffer::_GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const
{
const auto bufferSize = GetSize();
@@ -1380,7 +1376,7 @@ void TextBuffer::_PruneHyperlinks()
// we have found all hyperlink references in the first row and put them in refs,
// now we need to search the rest of the buffer (i.e. all the rows except the first)
// to see if those references are anywhere else
for (size_t i = 1; i != total; ++i)
for (til::CoordType i = 1; i < total; ++i)
{
const auto nextRowRefs = GetRowByOffset(i).GetAttrRow().GetHyperlinks();
for (auto id : nextRowRefs)
@@ -1408,22 +1404,22 @@ void TextBuffer::_PruneHyperlinks()
// Method Description:
// - Update pos to be the position of the first character of the next word. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first character on the "word" (inclusive)
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional) const
// - pos - The til::point for the first character on the "word" (inclusive)
bool TextBuffer::MoveToNextWord(til::point& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional) const
{
// move to the beginning of the next word
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
// This is also the inclusive start of the next word.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit.to_win32_coord()) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
if (bufferSize.CompareInBounds(copy, limit.to_win32_coord(), true) >= 0)
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
{
return false;
}
@@ -1435,12 +1431,12 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
// Method Description:
// - Update pos to be the position of the first character of the previous word. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first character on the "word" (inclusive)
bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters) const
// - pos - The til::point for the first character on the "word" (inclusive)
bool TextBuffer::MoveToPreviousWord(til::point& pos, std::wstring_view wordDelimiters) const
{
// move to the beginning of the current word
auto copy{ GetWordStart(pos, wordDelimiters, true) };
@@ -1459,51 +1455,51 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
// Method Description:
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
// Return Value:
// - pos - The COORD for the first cell of the current glyph (inclusive)
const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional) const
// - pos - The til::point for the first cell of the current glyph (inclusive)
til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional) const
{
auto resultPos = pos.to_win32_coord();
auto resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit.to_win32_coord(), true) > 0)
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
resultPos = limit.to_win32_coord();
resultPos = limit;
}
// limit is exclusive, so we need to move back to be within valid bounds
if (resultPos != limit.to_win32_coord() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
{
bufferSize.DecrementInBounds(resultPos, true);
}
return til::point{ resultPos };
return resultPos;
}
// Method Description:
// - Update pos to be the end of the current glyph/character.
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// - accessibilityMode - this is being used for accessibility; make the end exclusive.
// Return Value:
// - pos - The COORD for the last cell of the current glyph (exclusive)
const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode, std::optional<til::point> limitOptional) const
// - pos - The til::point for the last cell of the current glyph (exclusive)
til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
auto resultPos = pos.to_win32_coord();
auto resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit.to_win32_coord(), true) > 0)
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
resultPos = limit.to_win32_coord();
resultPos = limit;
}
if (resultPos != limit.to_win32_coord() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
{
bufferSize.IncrementInBounds(resultPos, true);
}
@@ -1513,24 +1509,24 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilit
{
bufferSize.IncrementInBounds(resultPos, true);
}
return til::point{ resultPos };
return resultPos;
}
// Method Description:
// - Update pos to be the beginning of the next glyph/character. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// - allowExclusiveEnd - allow result to be the exclusive limit (one past limit)
// - limit - boundaries for the iterator to operate within
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first cell of the current glyph (inclusive)
// - pos - The til::point for the first cell of the current glyph (inclusive)
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
{
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos.to_win32_coord(), limit.to_win32_coord(), true) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
if (distanceToLimit >= 0)
{
// Corner Case: we're on/past the limit
@@ -1547,7 +1543,7 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
}
// Try to move forward, but if we hit the buffer boundary, we fail to move.
auto iter{ GetCellDataAt(pos.to_win32_coord(), bufferSize) };
auto iter{ GetCellDataAt(pos, bufferSize) };
const bool success{ ++iter };
// Move again if we're on a wide glyph
@@ -1556,24 +1552,24 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
++iter;
}
pos = til::point{ iter.Pos() };
pos = iter.Pos();
return success;
}
// Method Description:
// - Update pos to be the beginning of the previous glyph/character. This is used for accessibility
// Arguments:
// - pos - a COORD on the word you are currently on
// - pos - a til::point on the word you are currently on
// Return Value:
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
// - pos - The COORD for the first cell of the previous glyph (inclusive)
// - pos - The til::point for the first cell of the previous glyph (inclusive)
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional) const
{
auto resultPos = pos.to_win32_coord();
auto resultPos = pos;
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(pos.to_win32_coord(), limit.to_win32_coord(), true) > 0)
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
{
// we're past the end
// clamp us to the limit
@@ -1588,7 +1584,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
bufferSize.DecrementInBounds(resultPos, true);
}
pos = til::point{ resultPos };
pos = resultPos;
return success;
}
@@ -1606,9 +1602,9 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
// the buffer rather than the screen.
// Return Value:
// - the delimiter class for the given char
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const
const std::vector<til::inclusive_rect> TextBuffer::GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const
{
std::vector<SMALL_RECT> textRects;
std::vector<til::inclusive_rect> textRects;
const auto bufferSize = GetSize();
@@ -1619,11 +1615,11 @@ const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, b
std::make_tuple(start, end) :
std::make_tuple(end, start);
const auto textRectSize = base::ClampedNumeric<short>(1) + lowerCoord.Y - higherCoord.Y;
const auto textRectSize = 1 + lowerCoord.Y - higherCoord.Y;
textRects.reserve(textRectSize);
for (auto row = higherCoord.Y; row <= lowerCoord.Y; row++)
{
SMALL_RECT textRow;
til::inclusive_rect textRow;
textRow.Top = row;
textRow.Bottom = row;
@@ -1661,12 +1657,12 @@ const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, b
// - selectionRow: the selection row to be expanded
// Return Value:
// - modifies selectionRow's Left and Right values to expand properly
void TextBuffer::_ExpandTextRow(SMALL_RECT& textRow) const
void TextBuffer::_ExpandTextRow(til::inclusive_rect& textRow) const
{
const auto bufferSize = GetSize();
// expand left side of rect
COORD targetPoint{ textRow.Left, textRow.Top };
til::point targetPoint{ textRow.Left, textRow.Top };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsTrailing())
{
if (targetPoint.X == bufferSize.Left())
@@ -1708,7 +1704,7 @@ void TextBuffer::_ExpandTextRow(SMALL_RECT& textRow) const
// - The text, background color, and foreground color data of the selected region of the text buffer.
const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& selectionRects,
const std::vector<til::inclusive_rect>& selectionRects,
std::function<std::pair<COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors,
const bool formatWrappedRows) const
{
@@ -1725,9 +1721,9 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
}
// for each row in the selection
for (UINT i = 0; i < rows; i++)
for (size_t i = 0; i < rows; i++)
{
const UINT iRow = selectionRects.at(i).Top;
const auto iRow = selectionRects.at(i).Top;
const auto highlight = Viewport::FromInclusive(selectionRects.at(i));
@@ -2243,22 +2239,22 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
const auto cOldCursorPos = oldCursor.GetPosition();
const auto cOldLastChar = oldBuffer.GetLastNonSpaceCharacter(lastCharacterViewport);
const short cOldRowsTotal = cOldLastChar.Y + 1;
const auto cOldRowsTotal = cOldLastChar.Y + 1;
COORD cNewCursorPos = { 0 };
til::point cNewCursorPos;
auto fFoundCursorPos = false;
auto foundOldMutable = false;
auto foundOldVisible = false;
auto hr = S_OK;
// Loop through all the rows of the old buffer and reprint them into the new buffer
short iOldRow = 0;
til::CoordType iOldRow = 0;
for (; iOldRow < cOldRowsTotal; iOldRow++)
{
// Fetch the row and its "right" which is the last printable character.
const auto& row = oldBuffer.GetRowByOffset(iOldRow);
const auto cOldColsTotal = oldBuffer.GetLineWidth(iOldRow);
const auto& charRow = row.GetCharRow();
auto iRight = gsl::narrow_cast<short>(charRow.MeasureRight());
auto iRight = charRow.MeasureRight();
// If we're starting a new row, try and preserve the line rendition
// from the row in the original buffer.
@@ -2296,7 +2292,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// Loop through every character in the current row (up to
// the "right" boundary, which is one past the final valid
// character)
short iOldCol = 0;
til::CoordType iOldCol = 0;
const auto copyRight = iRight;
for (; iOldCol < copyRight; iOldCol++)
{
@@ -2439,7 +2435,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
const auto coordNewCursor = newCursor.GetPosition();
if (coordNewCursor.X == 0 && coordNewCursor.Y > 0)
{
if (newBuffer.GetRowByOffset(gsl::narrow_cast<size_t>(coordNewCursor.Y) - 1).WasWrapForced())
if (newBuffer.GetRowByOffset(coordNewCursor.Y - 1).WasWrapForced())
{
hr = newBuffer.NewlineCursor() ? hr : E_OUTOFMEMORY;
}
@@ -2693,17 +2689,17 @@ void TextBuffer::CopyPatterns(const TextBuffer& OtherBuffer)
// - The lastRow to search
// Return value:
// - An interval tree containing the patterns found
PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const
PointTree TextBuffer::GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const
{
PointTree::interval_vector intervals;
std::wstring concatAll;
const auto rowSize = GetRowByOffset(0).size();
concatAll.reserve(rowSize * (lastRow - firstRow + 1));
concatAll.reserve(gsl::narrow_cast<size_t>(rowSize) * gsl::narrow_cast<size_t>(lastRow - firstRow + 1));
// to deal with text that spans multiple lines, we will first concatenate
// all the text into one string and find the patterns in that string
for (auto i = firstRow; i <= lastRow; ++i)
for (til::CoordType i = firstRow; i <= lastRow; ++i)
{
auto& row = GetRowByOffset(i);
concatAll += row.GetText();
@@ -2718,21 +2714,21 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
auto words_begin = std::wsregex_iterator(concatAll.begin(), concatAll.end(), regexObj);
auto words_end = std::wsregex_iterator();
size_t lenUpToThis = 0;
til::CoordType lenUpToThis = 0;
for (auto i = words_begin; i != words_end; ++i)
{
// record the locations -
// when we find a match, the prefix is text that is between this
// match and the previous match, so we use the size of the prefix
// along with the size of the match to determine the locations
size_t prefixSize = 0;
til::CoordType prefixSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->prefix().str()))
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
prefixSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto start = lenUpToThis + prefixSize;
size_t matchSize = 0;
til::CoordType matchSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->str()))
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
@@ -2741,8 +2737,8 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
const auto end = start + matchSize;
lenUpToThis = end;
const til::point startCoord{ gsl::narrow<SHORT>(start % rowSize), gsl::narrow<SHORT>(start / rowSize) };
const til::point endCoord{ gsl::narrow<SHORT>(end % rowSize), gsl::narrow<SHORT>(end / rowSize) };
const til::point startCoord{ start % rowSize, start / rowSize };
const til::point endCoord{ end % rowSize, end / rowSize };
// store the intervals
// NOTE: these intervals are relative to the VIEWPORT not the buffer

View File

@@ -68,7 +68,7 @@ namespace Microsoft::Console::Render
class TextBuffer final
{
public:
TextBuffer(const COORD screenBufferSize,
TextBuffer(const til::size screenBufferSize,
const TextAttribute defaultAttributes,
const UINT cursorSize,
const bool isActiveBuffer,
@@ -79,27 +79,27 @@ public:
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
// row manipulation
const ROW& GetRowByOffset(const size_t index) const;
ROW& GetRowByOffset(const size_t index);
const ROW& GetRowByOffset(const til::CoordType index) const noexcept;
ROW& GetRowByOffset(const til::CoordType index) noexcept;
TextBufferCellIterator GetCellDataAt(const COORD at) const;
TextBufferCellIterator GetCellLineDataAt(const COORD at) const;
TextBufferCellIterator GetCellDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
TextBufferTextIterator GetTextDataAt(const COORD at) const;
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
TextBufferCellIterator GetCellDataAt(const til::point at) const;
TextBufferCellIterator GetCellLineDataAt(const til::point at) const;
TextBufferCellIterator GetCellDataAt(const til::point at, const Microsoft::Console::Types::Viewport limit) const;
TextBufferTextIterator GetTextDataAt(const til::point at) const;
TextBufferTextIterator GetTextLineDataAt(const til::point at) const;
TextBufferTextIterator GetTextDataAt(const til::point at, const Microsoft::Console::Types::Viewport limit) const;
// Text insertion functions
OutputCellIterator Write(const OutputCellIterator givenIt);
OutputCellIterator Write(const OutputCellIterator givenIt,
const COORD target,
const til::point target,
const std::optional<bool> wrap = true);
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
const COORD target,
const til::point target,
const std::optional<bool> setWrap = std::nullopt,
const std::optional<size_t> limitRight = std::nullopt);
const std::optional<til::CoordType> limitRight = std::nullopt);
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
@@ -109,36 +109,36 @@ public:
// Scroll needs access to this to quickly rotate around the buffer.
bool IncrementCircularBuffer(const bool inVtMode = false);
COORD GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional = std::nullopt) const;
til::point GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional = std::nullopt) const;
Cursor& GetCursor() noexcept;
const Cursor& GetCursor() const noexcept;
const SHORT GetFirstRowIndex() const noexcept;
const til::CoordType GetFirstRowIndex() const noexcept;
const Microsoft::Console::Types::Viewport GetSize() const noexcept;
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
void ScrollRows(const til::CoordType firstRow, const til::CoordType size, const til::CoordType delta);
UINT TotalRowCount() const noexcept;
til::CoordType TotalRowCount() const noexcept;
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
void SetCurrentLineRendition(const LineRendition lineRendition);
void ResetLineRenditionRange(const size_t startRow, const size_t endRow);
LineRendition GetLineRendition(const size_t row) const;
bool IsDoubleWidthLine(const size_t row) const;
void ResetLineRenditionRange(const til::CoordType startRow, const til::CoordType endRow) noexcept;
LineRendition GetLineRendition(const til::CoordType row) const noexcept;
bool IsDoubleWidthLine(const til::CoordType row) const noexcept;
SHORT GetLineWidth(const size_t row) const;
COORD ClampPositionWithinLine(const COORD position) const;
COORD ScreenToBufferPosition(const COORD position) const;
COORD BufferToScreenPosition(const COORD position) const;
til::CoordType GetLineWidth(const til::CoordType row) const noexcept;
til::point ClampPositionWithinLine(const til::point position) const noexcept;
til::point ScreenToBufferPosition(const til::point position) const noexcept;
til::point BufferToScreenPosition(const til::point position) const noexcept;
void Reset();
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
[[nodiscard]] HRESULT ResizeTraditional(const til::size newSize) noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
UnicodeStorage& GetUnicodeStorage() noexcept;
@@ -149,23 +149,23 @@ public:
Microsoft::Console::Render::Renderer& GetRenderer() noexcept;
void TriggerRedraw(const Microsoft::Console::Types::Viewport& viewport);
void TriggerRedrawCursor(const COORD position);
void TriggerRedrawCursor(const til::point position);
void TriggerRedrawAll();
void TriggerScroll();
void TriggerScroll(const COORD delta);
void TriggerScroll(const til::point delta);
void TriggerNewTextNotification(const std::wstring_view newText);
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
til::point GetWordStart(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
til::point GetWordEnd(const til::point target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextWord(til::point& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToPreviousWord(til::point& pos, const std::wstring_view wordDelimiters) const;
const til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
const til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional = std::nullopt) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
const std::vector<til::inclusive_rect> GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const;
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
@@ -184,7 +184,7 @@ public:
const TextAndColor GetText(const bool includeCRLF,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& textRects,
const std::vector<til::inclusive_rect>& textRects,
std::function<std::pair<COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors = nullptr,
const bool formatWrappedRows = false) const;
@@ -200,8 +200,8 @@ public:
struct PositionInformation
{
short mutableViewportTop{ 0 };
short visibleViewportTop{ 0 };
til::CoordType mutableViewportTop{ 0 };
til::CoordType visibleViewportTop{ 0 };
};
static HRESULT Reflow(TextBuffer& oldBuffer,
@@ -212,7 +212,7 @@ public:
const size_t AddPatternRecognizer(const std::wstring_view regexString);
void ClearPatternRecognizers() noexcept;
void CopyPatterns(const TextBuffer& OtherBuffer);
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const;
private:
void _UpdateSize();
@@ -220,7 +220,7 @@ private:
std::vector<ROW> _storage;
Cursor _cursor;
SHORT _firstRow; // indexes top row (not necessarily 0)
til::CoordType _firstRow; // indexes top row (not necessarily 0)
TextAttribute _currentAttributes;
@@ -234,29 +234,29 @@ private:
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
uint16_t _currentHyperlinkId;
void _RefreshRowIDs(std::optional<SHORT> newRowWidth);
void _RefreshRowIDs(std::optional<til::CoordType> newRowWidth);
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
COORD _GetPreviousFromCursor() const;
til::point _GetPreviousFromCursor() const noexcept;
void _SetWrapOnCurrentRow();
void _AdjustWrapOnCurrentRow(const bool fSet);
void _SetWrapOnCurrentRow() noexcept;
void _AdjustWrapOnCurrentRow(const bool fSet) noexcept;
// Assist with maintaining proper buffer state for Double Byte character sequences
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
ROW& _GetFirstRow();
ROW& _GetFirstRow() noexcept;
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
void _ExpandTextRow(til::inclusive_rect& selectionRow) const;
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const;
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const;
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const;
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
void _PruneHyperlinks();

View File

@@ -19,7 +19,7 @@ using namespace Microsoft::Console::Types;
// Arguments:
// - buffer - Text buffer to seek through
// - pos - Starting position to retrieve text data from (within screen buffer bounds)
TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, COORD pos) :
TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, til::point pos) :
TextBufferCellIterator(buffer, pos, buffer.GetSize())
{
}
@@ -30,7 +30,7 @@ TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, COORD p
// - buffer - Pointer to screen buffer to seek through
// - pos - Starting position to retrieve text data from (within screen buffer bounds)
// - limits - Viewport limits to restrict the iterator within the buffer bounds (smaller than the buffer itself)
TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, COORD pos, const Viewport limits) :
TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, til::point pos, const Viewport limits) :
_buffer(buffer),
_pos(pos),
_pRow(s_GetRow(buffer, pos)),
@@ -126,7 +126,7 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
const auto oldX = _pos.X;
const auto oldY = _pos.Y;
// Under MSVC writing the individual members of a COORD generates worse assembly
// Under MSVC writing the individual members of a til::point generates worse assembly
// compared to having them be local variables. This causes a performance impact.
auto newX = oldX;
auto newY = oldY;
@@ -289,7 +289,7 @@ ptrdiff_t TextBufferCellIterator::operator-(const TextBufferCellIterator& it)
// - Sets the coordinate position that this iterator will inspect within the text buffer on dereference.
// Arguments:
// - newPos - The new coordinate position.
void TextBufferCellIterator::_SetPos(const COORD newPos)
void TextBufferCellIterator::_SetPos(const til::point newPos)
{
if (newPos.Y != _pos.Y)
{
@@ -317,7 +317,7 @@ void TextBufferCellIterator::_SetPos(const COORD newPos)
// - pos - Position inside screen buffer bounds to retrieve row
// Return Value:
// - Pointer to the underlying CharRow structure
const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const COORD pos)
const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const til::point pos) noexcept
{
return &buffer.GetRowByOffset(pos.Y);
}
@@ -354,7 +354,7 @@ const OutputCellView* TextBufferCellIterator::operator->() const noexcept
return &_view;
}
COORD TextBufferCellIterator::Pos() const noexcept
til::point TextBufferCellIterator::Pos() const noexcept
{
return _pos;
}

View File

@@ -25,8 +25,8 @@ class TextBuffer;
class TextBufferCellIterator
{
public:
TextBufferCellIterator(const TextBuffer& buffer, COORD pos);
TextBufferCellIterator(const TextBuffer& buffer, COORD pos, const Microsoft::Console::Types::Viewport limits);
TextBufferCellIterator(const TextBuffer& buffer, til::point pos);
TextBufferCellIterator(const TextBuffer& buffer, til::point pos, const Microsoft::Console::Types::Viewport limits);
operator bool() const noexcept;
@@ -47,12 +47,12 @@ public:
const OutputCellView& operator*() const noexcept;
const OutputCellView* operator->() const noexcept;
COORD Pos() const noexcept;
til::point Pos() const noexcept;
protected:
void _SetPos(const COORD newPos);
void _SetPos(const til::point newPos);
void _GenerateView();
static const ROW* s_GetRow(const TextBuffer& buffer, const COORD pos);
static const ROW* s_GetRow(const TextBuffer& buffer, const til::point pos) noexcept;
OutputCellView _view;
@@ -61,7 +61,7 @@ protected:
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;
COORD _pos;
til::point _pos;
#if UNIT_TESTING
friend class TextBufferIteratorTests;

View File

@@ -36,9 +36,9 @@ namespace
struct TestBuffer
{
COORD size;
til::size size;
std::vector<TestRow> rows;
COORD cursor;
til::point cursor;
};
struct TestCase
@@ -737,7 +737,7 @@ class ReflowTests
{
auto buffer = std::make_unique<TextBuffer>(testBuffer.size, TextAttribute{ 0x7 }, 0, false, renderer);
size_t i{};
til::CoordType i{};
for (const auto& testRow : testBuffer.rows)
{
auto& row{ buffer->GetRowByOffset(i) };
@@ -745,7 +745,7 @@ class ReflowTests
auto& charRow{ row.GetCharRow() };
row.SetWrapForced(testRow.wrap);
size_t j{};
til::CoordType j{};
for (auto it{ charRow.begin() }; it != charRow.end(); ++it)
{
// Yes, we're about to manually create a buffer. It is unpleasant.
@@ -771,7 +771,7 @@ class ReflowTests
return buffer;
}
static std::unique_ptr<TextBuffer> _textBufferByReflowingTextBuffer(TextBuffer& originalBuffer, const COORD newSize)
static std::unique_ptr<TextBuffer> _textBufferByReflowingTextBuffer(TextBuffer& originalBuffer, const til::size newSize)
{
auto buffer = std::make_unique<TextBuffer>(newSize, TextAttribute{ 0x7 }, 0, false, renderer);
TextBuffer::Reflow(originalBuffer, *buffer, std::nullopt, std::nullopt);
@@ -783,7 +783,7 @@ class ReflowTests
VERIFY_ARE_EQUAL(testBuffer.cursor, buffer.GetCursor().GetPosition());
VERIFY_ARE_EQUAL(testBuffer.size, buffer.GetSize().Dimensions());
size_t i{};
til::CoordType i{};
for (const auto& testRow : testBuffer.rows)
{
NoThrowString indexString;
@@ -794,7 +794,7 @@ class ReflowTests
indexString.Format(L"[Row %d]", i);
VERIFY_ARE_EQUAL(testRow.wrap, row.WasWrapForced(), indexString);
size_t j{};
til::CoordType j{};
for (auto it{ charRow.begin() }; it != charRow.end(); ++it)
{
indexString.Format(L"[Cell %d, %d; Text line index %d]", it - charRow.begin(), i, j);

View File

@@ -18,7 +18,7 @@ class UnicodeStorageTests
TEST_METHOD(CanOverwriteEmoji)
{
UnicodeStorage storage;
const COORD coord{ 1, 3 };
const til::point coord{ 1, 3 };
const std::vector<wchar_t> newMoon{ 0xD83C, 0xDF11 };
const std::vector<wchar_t> fullMoon{ 0xD83C, 0xDF15 };

View File

@@ -37,7 +37,6 @@ Abstract:
// private dependencies
#include "../host/conddkrefs.h"
#include "../inc/operators.hpp"
#include "../inc/unicode.hpp"
#pragma warning(pop)

View File

@@ -100,6 +100,7 @@
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">

View File

@@ -189,6 +189,7 @@
<com:ProxyStub Id="1833E661-CC81-4DD0-87C6-C2F74BD39EFA" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">

View File

@@ -189,6 +189,7 @@
<com:ProxyStub Id="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
</com:ComInterface>
</com:Extension>
<com:Extension Category="windows.comServer">

View File

@@ -230,7 +230,7 @@ HRESULT HwndTerminal::Initialize()
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
const COORD windowSize{ gsl::narrow<short>(windowRect.right - windowRect.left), gsl::narrow<short>(windowRect.bottom - windowRect.top) };
const til::size windowSize{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
// Fist set up the dx engine with the window size in pixels.
// Then, using the font, get the number of characters that can fit.
@@ -239,7 +239,7 @@ HRESULT HwndTerminal::Initialize()
_renderEngine = std::move(dxEngine);
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
_terminal->Create({ 80, 25 }, 1000, *_renderer);
_terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); });
localPointerToThread->EnablePainting();
@@ -343,7 +343,7 @@ IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
return _uiaProvider.Get();
}
HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
HRESULT HwndTerminal::Refresh(const til::size windowSize, _Out_ til::size* dimensions)
{
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
@@ -357,8 +357,7 @@ HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
_renderer->TriggerRedrawAll();
// Convert our new dimensions to characters
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
{ gsl::narrow<short>(windowSize.cx), gsl::narrow<short>(windowSize.cy) });
const auto viewInPixels = Viewport::FromDimensions(windowSize);
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
// If this function succeeds with S_FALSE, then the terminal didn't
@@ -435,7 +434,7 @@ void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data)
/// <param name="height">New height of the terminal in pixels</param>
/// <param name="dimensions">Out parameter containing the columns and rows that fit the new size.</param>
/// <returns>HRESULT of the attempted resize.</returns>
HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions)
HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions)
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
@@ -448,7 +447,7 @@ HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ short width, _I
static_cast<int>(height),
0));
const SIZE windowSize{ width, height };
const til::size windowSize{ width, height };
return publicTerminal->Refresh(windowSize, dimensions);
}
@@ -459,19 +458,19 @@ HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ short width, _I
/// <param name="dimensionsInCharacters">New terminal size in row and column count.</param>
/// <param name="dimensionsInPixels">Out parameter with the new size of the renderer.</param>
/// <returns>HRESULT of the attempted resize.</returns>
HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ COORD dimensionsInCharacters, _Out_ SIZE* dimensionsInPixels)
HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensionsInCharacters, _Out_ til::size* dimensionsInPixels)
{
RETURN_HR_IF_NULL(E_INVALIDARG, dimensionsInPixels);
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto viewInCharacters = Viewport::FromDimensions({ 0, 0 }, { (dimensionsInCharacters.X), (dimensionsInCharacters.Y) });
const auto viewInCharacters = Viewport::FromDimensions(dimensionsInCharacters);
const auto viewInPixels = publicTerminal->_renderEngine->GetViewportInPixels(viewInCharacters);
dimensionsInPixels->cx = viewInPixels.Width();
dimensionsInPixels->cy = viewInPixels.Height();
COORD unused{ 0, 0 };
til::size unused;
return TerminalTriggerResize(terminal, viewInPixels.Width(), viewInPixels.Height(), &unused);
}
@@ -484,7 +483,7 @@ HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ CO
/// <param name="height">Height of the terminal area to calculate.</param>
/// <param name="dimensions">Out parameter containing the columns and rows that fit the new size.</param>
/// <returns>HRESULT of the calculation.</returns>
HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions)
HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions)
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
@@ -548,11 +547,11 @@ try
if (multiClickMapper == 3)
{
_terminal->MultiClickSelection((cursorPosition / fontSize).to_win32_coord(), ::Terminal::SelectionExpansion::Line);
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Line);
}
else if (multiClickMapper == 2)
{
_terminal->MultiClickSelection((cursorPosition / fontSize).to_win32_coord(), ::Terminal::SelectionExpansion::Word);
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Word);
}
else
{
@@ -593,13 +592,13 @@ try
if (distanceSquared >= maxDistanceSquared)
{
_terminal->SetSelectionAnchor((touchdownPoint / fontSize).to_win32_coord());
_terminal->SetSelectionAnchor(touchdownPoint / fontSize);
// stop tracking the touchdown point
_singleClickTouchdownPos = std::nullopt;
}
}
this->_terminal->SetSelectionEnd((cursorPosition / fontSize).to_win32_coord());
this->_terminal->SetSelectionEnd(cursorPosition / fontSize);
this->_renderer->TriggerSelection();
return S_OK;
@@ -701,9 +700,7 @@ try
wheelDelta = HIWORD(wParam);
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
auto coordsToTransform = cursorPosition.to_win32_point();
ScreenToClient(_hwnd.get(), &coordsToTransform);
cursorPosition = til::point{ coordsToTransform };
ScreenToClient(_hwnd.get(), cursorPosition.as_win32_point());
}
const TerminalInput::MouseButtonState state{
@@ -712,7 +709,7 @@ try
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
};
return _terminal->SendMouseEvent((cursorPosition / fontSize).to_win32_coord(), uMsg, getControlKeyState(), wheelDelta, state);
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
}
catch (...)
{
@@ -780,7 +777,7 @@ void _stdcall DestroyTerminal(void* terminal)
}
// Updates the terminal font type, size, color, as well as the background/foreground colors to a specified theme.
void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi)
void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, til::CoordType fontSize, int newDpi)
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
{
@@ -810,8 +807,8 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
RECT windowRect;
GetWindowRect(publicTerminal->_hwnd.get(), &windowRect);
COORD dimensions = {};
const SIZE windowSize{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
til::size dimensions;
const til::size windowSize{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
publicTerminal->Refresh(windowSize, &dimensions);
}
@@ -984,21 +981,21 @@ void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
CATCH_LOG();
}
COORD HwndTerminal::GetFontSize() const noexcept
til::size HwndTerminal::GetFontSize() const noexcept
{
return _actualFont.GetSize();
}
RECT HwndTerminal::GetBounds() const noexcept
til::rect HwndTerminal::GetBounds() const noexcept
{
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
til::rect windowRect;
GetWindowRect(_hwnd.get(), windowRect.as_win32_rect());
return windowRect;
}
RECT HwndTerminal::GetPadding() const noexcept
til::rect HwndTerminal::GetPadding() const noexcept
{
return { 0 };
return {};
}
double HwndTerminal::GetScaleFactor() const noexcept
@@ -1006,7 +1003,7 @@ double HwndTerminal::GetScaleFactor() const noexcept
return static_cast<double>(_currentDpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
}
void HwndTerminal::ChangeViewport(const SMALL_RECT NewWindow)
void HwndTerminal::ChangeViewport(const til::inclusive_rect& NewWindow)
{
_terminal->UserScrollViewport(NewWindow.Top);
}

View File

@@ -27,16 +27,16 @@ extern "C" {
__declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
__declspec(dllexport) void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data);
__declspec(dllexport) void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int));
__declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions);
__declspec(dllexport) HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ COORD dimensions, _Out_ SIZE* dimensionsInPixels);
__declspec(dllexport) HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions);
__declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
__declspec(dllexport) HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensions, _Out_ til::size* dimensionsInPixels);
__declspec(dllexport) HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
__declspec(dllexport) const wchar_t* _stdcall TerminalGetSelection(void* terminal);
__declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, til::CoordType fontSize, int newDpi);
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD flags, WORD scanCode);
@@ -60,7 +60,7 @@ public:
HRESULT Initialize();
void Teardown() noexcept;
void SendOutput(std::wstring_view data);
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
HRESULT Refresh(const til::size windowSize, _Out_ til::size* dimensions);
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
void RegisterWriteCallback(const void _stdcall callback(wchar_t*));
::Microsoft::Console::Types::IUiaData* GetUiaData() const noexcept;
@@ -91,9 +91,9 @@ private:
std::optional<til::point> _singleClickTouchdownPos;
friend HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
friend HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions);
friend HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ COORD dimensions, _Out_ SIZE* dimensionsInPixels);
friend HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ short width, _In_ short height, _Out_ COORD* dimensions);
friend HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
friend HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensions, _Out_ til::size* dimensionsInPixels);
friend HRESULT _stdcall TerminalCalculateResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
friend void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
friend void _stdcall TerminalUserScroll(void* terminal, int viewTop);
friend void _stdcall TerminalClearSelection(void* terminal);
@@ -101,7 +101,7 @@ private:
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags);
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, til::CoordType fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
friend void _stdcall TerminalSetFocus(void* terminal);
@@ -128,10 +128,10 @@ private:
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const noexcept override;
RECT GetBounds() const noexcept override;
til::size GetFontSize() const noexcept override;
til::rect GetBounds() const noexcept override;
double GetScaleFactor() const noexcept override;
void ChangeViewport(const SMALL_RECT NewWindow) override;
void ChangeViewport(const til::inclusive_rect& NewWindow) override;
HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept override;
RECT GetPadding() const noexcept override;
til::rect GetPadding() const noexcept override;
};

View File

@@ -97,7 +97,7 @@ HRESULT OpenTerminalHere::GetTitle(IShellItemArray* /*psiItemArray*/,
return SHStrDup(resource.data(), ppszName);
}
HRESULT OpenTerminalHere::GetState(IShellItemArray* /*psiItemArray*/,
HRESULT OpenTerminalHere::GetState(IShellItemArray* psiItemArray,
BOOL /*fOkToBeSlow*/,
EXPCMDSTATE* pCmdState)
{
@@ -106,10 +106,25 @@ HRESULT OpenTerminalHere::GetState(IShellItemArray* /*psiItemArray*/,
// E_PENDING and this object will be called back on a background thread with
// fOkToBeSlow == TRUE
// We however don't need to bother with any of that, so we'll just return
// ECS_ENABLED.
// We however don't need to bother with any of that.
// If no item was selected when the context menu was opened and Explorer
// is not at a valid path (e.g. This PC or Quick Access), we should hide
// the verb from the context menu.
if (psiItemArray == nullptr)
{
const auto path = this->_GetPathFromExplorer();
*pCmdState = path.empty() ? ECS_HIDDEN : ECS_ENABLED;
}
else
{
winrt::com_ptr<IShellItem> psi;
psiItemArray->GetItemAt(0, psi.put());
SFGAOF attributes;
const bool isFileSystemItem = (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
}
*pCmdState = ECS_ENABLED;
return S_OK;
}

View File

@@ -153,10 +153,6 @@
<StaticResource x:Key="UnfocusedBorderBrush"
ResourceKey="ApplicationPageBackgroundThemeBrush" />
<StaticResource x:Key="SettingsPageBackground"
ResourceKey="SolidBackgroundFillColorTertiary" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
@@ -170,9 +166,6 @@
<StaticResource x:Key="UnfocusedBorderBrush"
ResourceKey="ApplicationPageBackgroundThemeBrush" />
<StaticResource x:Key="SettingsPageBackground"
ResourceKey="SolidBackgroundFillColorTertiary" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
@@ -190,9 +183,6 @@
-->
<StaticResource x:Key="TabViewBackground"
ResourceKey="SystemColorButtonFaceColorBrush" />
<StaticResource x:Key="SettingsPageBackground"
ResourceKey="SystemColorWindowColorBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

View File

@@ -278,6 +278,55 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
void TerminalPage::_HandleScrollToMark(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ScrollToMarkArgs>())
{
_ApplyToActiveControls([&realArgs](auto& control) {
control.ScrollToMark(realArgs.Direction());
});
}
args.Handled(true);
}
void TerminalPage::_HandleAddMark(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<AddMarkArgs>())
{
_ApplyToActiveControls([realArgs](auto& control) {
Control::ScrollMark mark;
if (realArgs.Color())
{
mark.Color.Color = realArgs.Color().Value();
mark.Color.HasValue = true;
}
else
{
mark.Color.HasValue = false;
}
control.AddMark(mark);
});
}
args.Handled(true);
}
void TerminalPage::_HandleClearMark(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ApplyToActiveControls([](auto& control) {
control.ClearMark();
});
args.Handled(true);
}
void TerminalPage::_HandleClearAllMarks(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_ApplyToActiveControls([](auto& control) {
control.ClearAllMarks();
});
args.Handled(true);
}
void TerminalPage::_HandleFindMatch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@@ -1043,4 +1092,24 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
}
void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& control{ _GetActiveControl() })
{
control.ToggleMarkMode();
args.Handled(true);
}
}
void TerminalPage::_HandleToggleBlockSelection(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& control{ _GetActiveControl() })
{
const auto handled = control.ToggleBlockSelection();
args.Handled(handled);
}
}
}

View File

@@ -158,15 +158,15 @@
<value>Windows Terminal with a preview of upcoming features</value>
</data>
<data name="ShellExtension_OpenInTerminalMenuItem_Dev" xml:space="preserve">
<value>Open in Terminal (Dev)</value>
<value>Open in Terminal (&amp;Dev)</value>
<comment>{Locked} The dev build will never be seen in multiple languages</comment>
</data>
<data name="ShellExtension_OpenInTerminalMenuItem_Preview" xml:space="preserve">
<value>Open in Terminal Preview</value>
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal</comment>
<value>Open in Terminal &amp;Preview</value>
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key.</comment>
</data>
<data name="ShellExtension_OpenInTerminalMenuItem" xml:space="preserve">
<value>Open in Terminal</value>
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal</comment>
<value>Open in &amp;Terminal</value>
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key.</comment>
</data>
</root>

View File

@@ -9,8 +9,11 @@
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
Background="{ThemeResource TabViewBackground}"
mc:Ignorable="d">
<!-- GH#13143: Make sure that the Background is actually TabViewBackground here, not Transparent. This is load bearing, for showTabsInTitlebar=false. -->
<mux:TabView x:Name="TabView"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Stretch"

View File

@@ -69,6 +69,29 @@ namespace winrt::TerminalApp::implementation
// - see GH#2988
HRESULT TerminalPage::Initialize(HWND hwnd)
{
if (!_hostingHwnd.has_value())
{
// GH#13211 - if we haven't yet set the owning hwnd, reparent all the controls now.
for (const auto& tab : _tabs)
{
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
{
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
if (const auto& term{ pane->GetTerminalControl() })
{
term.OwningHwnd(reinterpret_cast<uint64_t>(hwnd));
}
});
}
// We don't need to worry about resetting the owning hwnd for the
// SUI here. GH#13211 only repros for a defterm connection, where
// the tab is spawned before the window is created. It's not
// possible to make a SUI tab like that, before the window is
// created. The SUI could be spawned as a part of a window restore,
// but that would still work fine. The window would be created
// before restoring previous tabs in that scenario.
}
}
_hostingHwnd = hwnd;
return S_OK;
}
@@ -218,6 +241,24 @@ namespace winrt::TerminalApp::implementation
// Inform the host that our titlebar content has changed.
_SetTitleBarContentHandlers(*this, _tabRow);
// GH#13143 Manually set the tab row's background to transparent here.
//
// We're doing it this way because ThemeResources are tricky. We
// default in XAML to using the appropriate ThemeResource background
// color for our TabRow. When tabs in the titlebar are _disabled_,
// this will ensure that the tab row has the correct theme-dependent
// value. When tabs in the titlebar are _enabled_ (the default),
// we'll switch the BG to Transparent, to let the Titlebar Control's
// background be used as the BG for the tab row.
//
// We can't do it the other way around (default to Transparent, only
// switch to a color when disabling tabs in the titlebar), because
// looking up the correct ThemeResource from and App dictionary is a
// capital-H Hard problem.
const auto transparent = Media::SolidColorBrush();
transparent.Color(Windows::UI::Colors::Transparent());
_tabRow.Background(transparent);
}
// Hookup our event handlers to the ShortcutActionDispatch
@@ -1066,8 +1107,8 @@ namespace winrt::TerminalApp::implementation
L".",
L"Azure",
nullptr,
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
settings.InitialRows(),
settings.InitialCols(),
winrt::guid());
if constexpr (Feature_VtPassthroughMode::IsEnabled())
@@ -1117,8 +1158,8 @@ namespace winrt::TerminalApp::implementation
newWorkingDirectory,
settings.StartingTitle(),
envMap.GetView(),
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
settings.InitialRows(),
settings.InitialCols(),
winrt::guid());
valueSet.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough()));
@@ -1804,9 +1845,17 @@ namespace winrt::TerminalApp::implementation
// Instead, let's just promote this first split to be a tab instead.
// Crash avoided, and we don't need to worry about inserting a new-tab
// command in at the start.
if (!focusedTab && _tabs.Size() == 0)
if (!focusedTab)
{
_CreateNewTabFromPane(newPane);
if (_tabs.Size() == 0)
{
_CreateNewTabFromPane(newPane);
}
else
{
// The focused tab isn't a terminal tab
return;
}
}
else
{
@@ -2431,7 +2480,10 @@ namespace winrt::TerminalApp::implementation
TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection };
// GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now.
term.WindowVisibilityChanged(_visible);
if (_visible)
{
term.WindowVisibilityChanged(_visible);
}
if (_hostingHwnd.has_value())
{

View File

@@ -9,7 +9,7 @@ using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
static COORD GetConsoleScreenSize(HANDLE outputHandle)
static til::point GetConsoleScreenSize(HANDLE outputHandle)
{
CONSOLE_SCREEN_BUFFER_INFOEX csbiex{};
csbiex.cbSize = sizeof(csbiex);

View File

@@ -75,8 +75,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
if (settings)
{
_initialRows = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialRows").try_as<Windows::Foundation::IPropertyValue>(), _initialRows);
_initialCols = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols);
_initialRows = gsl::narrow<til::CoordType>(winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialRows").try_as<Windows::Foundation::IPropertyValue>(), _initialRows));
_initialCols = gsl::narrow<til::CoordType>(winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols));
}
}

View File

@@ -32,8 +32,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private:
uint32_t _initialRows{};
uint32_t _initialCols{};
til::CoordType _initialRows{};
til::CoordType _initialCols{};
enum class AzureState
{

View File

@@ -293,7 +293,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
_transitionToState(ConnectionState::Connecting);
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
const til::size dimensions{ gsl::narrow<til::CoordType>(_initialCols), gsl::narrow<til::CoordType>(_initialRows) };
// If we do not have pipes already, then this is a fresh connection... not an inbound one that is a received
// handoff from an already-started PTY process.
@@ -309,15 +309,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, flags, &_inPipe, &_outPipe, &_hPC));
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, &_inPipe, &_outPipe, &_hPC));
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
if (_initialParentHwnd != 0)
{
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
}
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
if (_initialVisibility)
{
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
}
THROW_IF_FAILED(_LaunchAttachedClient());
}
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
@@ -334,8 +338,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), til::unwrap_coord_size(dimensions)));
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
if (_initialVisibility)
{
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
}
}
_startTime = std::chrono::high_resolution_clock::now();

View File

@@ -67,13 +67,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void _indicateExitWithStatus(unsigned int status) noexcept;
void _ClientTerminated() noexcept;
uint32_t _initialRows{};
uint32_t _initialCols{};
til::CoordType _initialRows{};
til::CoordType _initialCols{};
uint64_t _initialParentHwnd{ 0 };
hstring _commandline{};
hstring _startingDirectory{};
hstring _startingTitle{};
bool _initialVisibility{ false };
bool _initialVisibility{ true };
Windows::Foundation::Collections::ValueSet _environment{ nullptr };
guid _guid{}; // A unique session identifier for connected client
hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).

View File

@@ -37,6 +37,28 @@ constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(
namespace winrt::Microsoft::Terminal::Control::implementation
{
static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const til::color& c)
{
Core::OptionalColor result;
result.Color = c;
result.HasValue = true;
return result;
}
static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const std::optional<til::color>& c)
{
Core::OptionalColor result;
if (c.has_value())
{
result.Color = *c;
result.HasValue = true;
}
else
{
result.HasValue = false;
}
return result;
}
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
// See microsoft/terminal#2066 for more info.
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */)
@@ -105,6 +127,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnShowWindowChanged = std::bind(&ControlCore::_terminalShowWindowChanged, this, std::placeholders::_1);
_terminal->SetShowWindowCallback(pfnShowWindowChanged);
auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
_terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -201,6 +226,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_renderer->TriggerTeardown();
}
_shutdownMidiAudio();
}
bool ControlCore::Initialize(const double actualWidth,
@@ -245,8 +272,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// and react accordingly.
_updateFont(true);
const COORD windowSize{ static_cast<short>(windowWidth),
static_cast<short>(windowHeight) };
const til::size windowSize{ static_cast<til::CoordType>(windowWidth),
static_cast<til::CoordType>(windowHeight) };
// First set up the dx engine with the window size in pixels.
// Then, using the font, get the number of characters that can fit.
@@ -262,11 +289,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto height = vp.Height();
_connection.Resize(height, width);
if (_OwningHwnd != 0)
if (_owningHwnd != 0)
{
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ReparentWindow(_OwningHwnd);
conpty.ReparentWindow(_owningHwnd);
}
}
@@ -390,11 +417,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
vkey != VK_SNAPSHOT &&
keyDown)
{
// try to update the selection
if (const auto updateSlnParams{ ::Terminal::ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
if (_terminal->IsInMarkMode() && modifiers.IsCtrlPressed() && vkey == 'A')
{
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second);
_terminal->SelectAll();
_renderer->TriggerSelection();
return true;
}
// try to update the selection
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
_renderer->TriggerSelection();
return true;
}
@@ -431,7 +466,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const short wheelDelta,
const TerminalInput::MouseButtonState state)
{
return _terminal->SendMouseEvent(viewportPos.to_win32_coord(), uiButton, states, wheelDelta, state);
return _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state);
}
void ControlCore::UserScrollViewport(const int viewTop)
@@ -555,12 +590,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_lastHoveredCell = terminalPosition;
uint16_t newId{ 0u };
// we can't use auto here because we're pre-declaring newInterval.
decltype(_terminal->GetHyperlinkIntervalFromPosition(COORD{})) newInterval{ std::nullopt };
decltype(_terminal->GetHyperlinkIntervalFromPosition({})) newInterval{ std::nullopt };
if (terminalPosition.has_value())
{
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
newId = _terminal->GetHyperlinkIdAtPosition(terminalPosition->to_win32_coord());
newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPosition->to_win32_coord());
newId = _terminal->GetHyperlinkIdAtPosition(*terminalPosition);
newInterval = _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition);
}
// If the hyperlink ID changed or the interval changed, trigger a redraw all
@@ -590,7 +625,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// Lock for the duration of our reads.
auto lock = _terminal->LockForReading();
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }.to_win32_coord()) };
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }) };
}
winrt::hstring ControlCore::HoveredUriText() const
@@ -598,7 +633,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
if (_lastHoveredCell.has_value())
{
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(_lastHoveredCell->to_win32_coord()) };
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
}
return {};
}
@@ -768,7 +803,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ControlCore::_setFontSizeUnderLock(int fontSize)
{
// Make sure we have a non-zero font size
const auto newSize = std::max<short>(gsl::narrow_cast<short>(fontSize), 1);
const auto newSize = std::max(fontSize, 1);
const auto fontFace = _settings->FontFace();
const auto fontWeight = _settings->FontWeight();
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false };
@@ -824,8 +859,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::_refreshSizeUnderLock()
{
auto cx = gsl::narrow_cast<short>(_panelWidth * _compositionScale);
auto cy = gsl::narrow_cast<short>(_panelHeight * _compositionScale);
auto cx = gsl::narrow_cast<til::CoordType>(_panelWidth * _compositionScale);
auto cy = gsl::narrow_cast<til::CoordType>(_panelHeight * _compositionScale);
// Don't actually resize so small that a single character wouldn't fit
// in either dimension. The buffer really doesn't like being size 0.
@@ -893,17 +928,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_refreshSizeUnderLock();
}
void ControlCore::SetSelectionAnchor(const til::point& position)
void ControlCore::SetSelectionAnchor(const til::point position)
{
auto lock = _terminal->LockForWriting();
_terminal->SetSelectionAnchor(position.to_win32_coord());
_terminal->SetSelectionAnchor(position);
}
// Method Description:
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
// Arguments:
// - position: the point in terminal coordinates (in cells, not pixels)
void ControlCore::SetEndSelectionPoint(const til::point& position)
void ControlCore::SetEndSelectionPoint(const til::point position)
{
if (!_terminal->IsSelectionActive())
{
@@ -920,7 +955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
};
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition.to_win32_coord());
_terminal->SetSelectionEnd(terminalPosition);
_renderer->TriggerSelection();
}
@@ -1002,6 +1037,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderer->TriggerSelection();
}
bool ControlCore::ToggleBlockSelection()
{
auto lock = _terminal->LockForWriting();
if (_terminal->IsSelectionActive())
{
_terminal->SetBlockSelection(!_terminal->IsBlockSelection());
_renderer->TriggerSelection();
return true;
}
return false;
}
void ControlCore::ToggleMarkMode()
{
auto lock = _terminal->LockForWriting();
_terminal->ToggleMarkMode();
_renderer->TriggerSelection();
}
bool ControlCore::IsInMarkMode() const
{
return _terminal->IsInMarkMode();
}
// Method Description:
// - Pre-process text pasted (presumably from the clipboard)
// before sending it over the terminal's connection.
@@ -1159,6 +1218,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const int viewHeight,
const int bufferSize)
{
if (!_initializedTerminal)
{
return;
}
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
// We're **NOT** taking the lock here unlike _scrollbarChangeHandler because
// we are already under lock (since this usually happens as a result of writing).
@@ -1196,8 +1259,71 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::_terminalShowWindowChanged(bool showOrHide)
{
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
_ShowWindowChangedHandlers(*this, *showWindow);
if (_initializedTerminal)
{
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
_ShowWindowChangedHandlers(*this, *showWindow);
}
}
// Method Description:
// - Plays a single MIDI note, blocking for the duration.
// Arguments:
// - noteNumber - The MIDI note number to be played (0 - 127).
// - velocity - The force with which the note should be played (0 - 127).
// - duration - How long the note should be sustained (in microseconds).
void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration)
{
// We create the audio instance on demand, and lock it for the duration
// of the note output so it can't be destroyed while in use.
auto& midiAudio = _getMidiAudio();
midiAudio.Lock();
// We then unlock the terminal, so the UI doesn't hang while we're busy.
auto& terminalLock = _terminal->GetReadWriteLock();
terminalLock.unlock();
// This call will block for the duration, unless shutdown early.
midiAudio.PlayNote(noteNumber, velocity, duration);
// Once complete, we reacquire the terminal lock and unlock the audio.
// If the terminal has shutdown in the meantime, the Unlock call
// will throw an exception, forcing the thread to exit ASAP.
terminalLock.lock();
midiAudio.Unlock();
}
// Method Description:
// - Returns the MIDI audio instance, created on demand.
// Arguments:
// - <none>
// Return Value:
// - a reference to the MidiAudio instance.
MidiAudio& ControlCore::_getMidiAudio()
{
if (!_midiAudio)
{
_midiAudio = std::make_unique<MidiAudio>();
_midiAudio->Initialize();
}
return *_midiAudio;
}
// Method Description:
// - Shuts down the MIDI audio system if previously instantiated.
// Arguments:
// - <none>
// Return Value:
// - <none>
void ControlCore::_shutdownMidiAudio()
{
if (_midiAudio)
{
// We lock the terminal here to make sure the shutdown promise is
// set before the audio is unlocked in the thread that is playing.
auto lock = _terminal->LockForWriting();
_midiAudio->Shutdown();
}
}
bool ControlCore::HasSelection() const
@@ -1400,7 +1526,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
auto lock = _terminal->LockForReading();
return til::point{ _terminal->GetCursorPosition() }.to_core_point();
return _terminal->GetCursorPosition().to_core_point();
}
// This one's really pushing the boundary of what counts as "encapsulation".
@@ -1452,7 +1578,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// If shift is pressed and there is a selection we extend it using
// the selection mode (expand the "end" selection point)
_terminal->SetSelectionEnd(terminalPosition.to_win32_coord(), mode);
_terminal->SetSelectionEnd(terminalPosition, mode);
selectionNeedsToBeCopied = true;
}
else if (mode != ::Terminal::SelectionExpansion::Char || shiftEnabled)
@@ -1460,7 +1586,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If we are handling a double / triple-click or shift+single click
// we establish selection using the selected mode
// (expand both "start" and "end" selection points)
_terminal->MultiClickSelection(terminalPosition.to_win32_coord(), mode);
_terminal->MultiClickSelection(terminalPosition, mode);
selectionNeedsToBeCopied = true;
}
@@ -1490,10 +1616,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
void ControlCore::_connectionOutputHandler(const hstring& hstr)
{
_terminal->Write(hstr);
try
{
_terminal->Write(hstr);
// Start the throttled update of where our hyperlinks are.
_updatePatternLocations->Run();
// Start the throttled update of where our hyperlinks are.
_updatePatternLocations->Run();
}
catch (...)
{
// We're expecting to receive an exception here if the terminal
// is closed while we're blocked playing a MIDI note.
}
}
// Method Description:
@@ -1703,10 +1837,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::WindowVisibilityChanged(const bool showOrHide)
{
// show is true, hide is false
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
if (_initializedTerminal)
{
conpty.ShowHide(showOrHide);
// show is true, hide is false
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ShowHide(showOrHide);
}
}
}
@@ -1743,4 +1880,157 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// transparency, or our acrylic, or our image.
return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty() || _settings->UseBackgroundImageForWindow();
}
uint64_t ControlCore::OwningHwnd()
{
return _owningHwnd;
}
void ControlCore::OwningHwnd(uint64_t owner)
{
if (owner != _owningHwnd && _connection)
{
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ReparentWindow(owner);
}
}
_owningHwnd = owner;
}
Windows::Foundation::Collections::IVector<Control::ScrollMark> ControlCore::ScrollMarks() const
{
auto internalMarks{ _terminal->GetScrollMarks() };
auto v = winrt::single_threaded_observable_vector<Control::ScrollMark>();
for (const auto& mark : internalMarks)
{
Control::ScrollMark m{};
// sneaky: always evaluate the color of the mark to a real value
// before shoving it into the optional. If the mark doesn't have a
// specific color set, we'll use the value from the color table
// that's appropriate for this category of mark. If we do have a
// color set, then great we'll use that. The TermControl can then
// always use the value in the Mark regardless if it was actually
// set or not.
m.Color = OptionalFromColor(_terminal->GetColorForMark(mark));
m.Start = mark.start.to_core_point();
m.End = mark.end.to_core_point();
v.Append(m);
}
return v;
}
void ControlCore::AddMark(const Control::ScrollMark& mark)
{
::Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark m{};
if (mark.Color.HasValue)
{
m.color = til::color{ mark.Color.Color };
}
if (HasSelection())
{
m.start = til::point{ _terminal->GetSelectionAnchor() };
m.end = til::point{ _terminal->GetSelectionEnd() };
}
else
{
m.start = m.end = til::point{ _terminal->GetTextBuffer().GetCursor().GetPosition() };
}
// The version of this that only accepts a ScrollMark will automatically
// set the start & end to the cursor position.
_terminal->AddMark(m, m.start, m.end);
}
void ControlCore::ClearMark() { _terminal->ClearMark(); }
void ControlCore::ClearAllMarks() { _terminal->ClearAllMarks(); }
void ControlCore::ScrollToMark(const Control::ScrollToMarkDirection& direction)
{
const auto currentOffset = ScrollOffset();
const auto& marks{ _terminal->GetScrollMarks() };
std::optional<DispatchTypes::ScrollMark> tgt;
switch (direction)
{
case ScrollToMarkDirection::Last:
{
int highest = currentOffset;
for (const auto& mark : marks)
{
const auto newY = mark.start.y;
if (newY > highest)
{
tgt = mark;
highest = newY;
}
}
break;
}
case ScrollToMarkDirection::First:
{
int lowest = currentOffset;
for (const auto& mark : marks)
{
const auto newY = mark.start.y;
if (newY < lowest)
{
tgt = mark;
lowest = newY;
}
}
break;
}
case ScrollToMarkDirection::Next:
{
int minDistance = INT_MAX;
for (const auto& mark : marks)
{
const auto delta = mark.start.y - currentOffset;
if (delta > 0 && delta < minDistance)
{
tgt = mark;
minDistance = delta;
}
}
break;
}
case ScrollToMarkDirection::Previous:
default:
{
int minDistance = INT_MAX;
for (const auto& mark : marks)
{
const auto delta = currentOffset - mark.start.y;
if (delta > 0 && delta < minDistance)
{
tgt = mark;
minDistance = delta;
}
}
break;
}
}
if (tgt.has_value())
{
UserScrollViewport(tgt->start.y);
}
else
{
if (direction == ScrollToMarkDirection::Last || direction == ScrollToMarkDirection::Next)
{
UserScrollViewport(BufferHeight());
}
else if (direction == ScrollToMarkDirection::First || direction == ScrollToMarkDirection::Previous)
{
UserScrollViewport(0);
}
}
}
}

View File

@@ -17,6 +17,7 @@
#include "ControlCore.g.h"
#include "ControlSettings.h"
#include "../../audio/midi/MidiAudio.hpp"
#include "../../renderer/base/Renderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../buffer/out/search.h"
@@ -81,6 +82,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void PasteText(const winrt::hstring& hstr);
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void SelectAll();
bool ToggleBlockSelection();
void ToggleMarkMode();
bool IsInMarkMode() const;
void GotFocus();
void LostFocus();
@@ -115,6 +119,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
int BufferHeight() const;
bool BracketedPasteEnabled() const noexcept;
Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
void AddMark(const Control::ScrollMark& mark);
void ClearMark();
void ClearAllMarks();
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
#pragma endregion
#pragma region ITerminalInput
@@ -148,8 +159,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool HasSelection() const;
bool CopyOnSelect() const;
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
void SetSelectionAnchor(const til::point& position);
void SetEndSelectionPoint(const til::point& position);
void SetSelectionAnchor(const til::point position);
void SetEndSelectionPoint(const til::point position);
void Search(const winrt::hstring& text,
const bool goForward,
@@ -175,10 +186,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void WindowVisibilityChanged(const bool showOrHide);
// TODO:GH#1256 - When a tab can be torn out or otherwise reparented to
// another window, this value will need a custom setter, so that we can
// also update the connection.
WINRT_PROPERTY(uint64_t, OwningHwnd, 0);
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
@@ -248,6 +257,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double _panelHeight{ 0 };
double _compositionScale{ 0 };
uint64_t _owningHwnd{ 0 };
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
@@ -271,8 +282,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalCursorPositionChanged();
void _terminalTaskbarProgressChanged();
void _terminalShowWindowChanged(bool showOrHide);
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);
#pragma endregion
std::unique_ptr<MidiAudio> _midiAudio;
MidiAudio& _getMidiAudio();
void _shutdownMidiAudio();
#pragma region RendererCallbacks
void _rendererWarning(const HRESULT hr);
void _renderEngineSwapChainChanged();

View File

@@ -22,7 +22,6 @@ namespace Microsoft.Terminal.Control
IsRightButtonDown = 0x4
};
enum ClearBufferType
{
Screen,
@@ -66,7 +65,10 @@ namespace Microsoft.Terminal.Control
void SendInput(String text);
void PasteText(String text);
void SelectAll();
Boolean ToggleBlockSelection();
void ToggleMarkMode();
void ClearBuffer(ClearBufferType clearType);
Boolean IsInMarkMode();
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
void ClearHoveredCell();

View File

@@ -615,7 +615,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get the size of the font, which is in pixels
const til::size fontSize{ _core->GetFont().GetSize() };
// Convert the location in pixels to characters within the current viewport.
return til::point{ pixelPosition / fontSize };
return pixelPosition / fontSize;
}
bool ControlInteractivity::_sendMouseEventHelper(const til::point terminalPosition,

View File

@@ -123,7 +123,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// terminal.
bool _selectionNeedsToBeCopied;
std::optional<COORD> _lastHoveredCell{ std::nullopt };
std::optional<til::point> _lastHoveredCell{ std::nullopt };
// Track the last hyperlink ID we hovered over
uint16_t _lastHoveredId{ 0 };

View File

@@ -56,6 +56,7 @@ namespace Microsoft.Terminal.Control
// Experimental Settings
Boolean ForceFullRepaintRendering { get; };
Boolean SoftwareRendering { get; };
Boolean ShowMarks { get; };
Boolean UseBackgroundImageForWindow { get; };
};
}

View File

@@ -3,6 +3,32 @@
namespace Microsoft.Terminal.Control
{
enum MarkCategory
{
Prompt = 0,
Error = 1,
Warning = 2,
Info = 3
};
struct ScrollMark
{
// There are other members of DispatchTypes::ScrollMark, but these are
// all we need to expose up and set downwards currently. Additional
// members can be bubbled as necessary.
Microsoft.Terminal.Core.Point Start;
Microsoft.Terminal.Core.Point End; // exclusive
Microsoft.Terminal.Core.OptionalColor Color;
};
enum ScrollToMarkDirection
{
Previous,
Next,
First,
Last
};
// These are properties of the TerminalCore that should be queryable by the
// rest of the app.
interface ICoreState
@@ -27,5 +53,11 @@ namespace Microsoft.Terminal.Control
UInt64 OwningHwnd;
void AddMark(ScrollMark mark);
void ClearMark();
void ClearAllMarks();
void ScrollToMark(ScrollToMarkDirection direction);
IVector<ScrollMark> ScrollMarks { get; };
};
}

View File

@@ -146,14 +146,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma endregion
#pragma region IControlAccessibilityInfo
COORD InteractivityAutomationPeer::GetFontSize() const noexcept
til::size InteractivityAutomationPeer::GetFontSize() const noexcept
{
return til::size{ til::math::rounding, _interactivity->Core().FontSize() }.to_win32_coord();
return { til::math::rounding, _interactivity->Core().FontSize() };
}
RECT InteractivityAutomationPeer::GetBounds() const noexcept
til::rect InteractivityAutomationPeer::GetBounds() const noexcept
{
return _controlBounds.to_win32_rect();
return _controlBounds;
}
HRESULT InteractivityAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
@@ -164,9 +164,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return S_OK;
}
RECT InteractivityAutomationPeer::GetPadding() const noexcept
til::rect InteractivityAutomationPeer::GetPadding() const noexcept
{
return _controlPadding.to_win32_rect();
return _controlPadding;
}
double InteractivityAutomationPeer::GetScaleFactor() const noexcept
@@ -174,7 +174,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}
void InteractivityAutomationPeer::ChangeViewport(const SMALL_RECT NewWindow)
void InteractivityAutomationPeer::ChangeViewport(const til::inclusive_rect& NewWindow)
{
_interactivity->UpdateScrollbar(NewWindow.Top);
}

View File

@@ -63,11 +63,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region IControlAccessibilityInfo Pattern
// Inherited via IControlAccessibilityInfo
virtual COORD GetFontSize() const noexcept override;
virtual RECT GetBounds() const noexcept override;
virtual RECT GetPadding() const noexcept override;
virtual til::size GetFontSize() const noexcept override;
virtual til::rect GetBounds() const noexcept override;
virtual til::rect GetPadding() const noexcept override;
virtual double GetScaleFactor() const noexcept override;
virtual void ChangeViewport(SMALL_RECT NewWindow) override;
virtual void ChangeViewport(const til::inclusive_rect& NewWindow) override;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override;
#pragma endregion

View File

@@ -124,20 +124,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
[weakThis = get_weak()](const auto& update) {
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
control->_isInternalScrollBarUpdate = true;
auto scrollBar = control->ScrollBar();
if (update.newValue)
{
scrollBar.Value(*update.newValue);
}
scrollBar.Maximum(update.newMaximum);
scrollBar.Minimum(update.newMinimum);
scrollBar.ViewportSize(update.newViewportSize);
// scroll one full screen worth at a time when the scroll bar is clicked
scrollBar.LargeChange(std::max(update.newViewportSize - 1, 0.));
control->_isInternalScrollBarUpdate = false;
control->_throttledUpdateScrollbar(update);
}
});
@@ -148,6 +135,57 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_ApplyUISettings();
}
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
{
// Assumptions:
// * we're already not closing
// * caller already checked weak ptr to make sure we're still alive
_isInternalScrollBarUpdate = true;
auto scrollBar = ScrollBar();
if (update.newValue)
{
scrollBar.Value(*update.newValue);
}
scrollBar.Maximum(update.newMaximum);
scrollBar.Minimum(update.newMinimum);
scrollBar.ViewportSize(update.newViewportSize);
// scroll one full screen worth at a time when the scroll bar is clicked
scrollBar.LargeChange(std::max(update.newViewportSize - 1, 0.));
_isInternalScrollBarUpdate = false;
if (_showMarksInScrollbar)
{
// Update scrollbar marks
ScrollBarCanvas().Children().Clear();
const auto marks{ _core.ScrollMarks() };
const auto fullHeight{ ScrollBarCanvas().ActualHeight() };
const auto totalBufferRows{ update.newMaximum + update.newViewportSize };
for (const auto m : marks)
{
Windows::UI::Xaml::Shapes::Rectangle r;
Media::SolidColorBrush brush{};
// Sneaky: technically, a mark doesn't need to have a color set,
// it might want to just use the color from the palette for that
// kind of mark. Fortunately, ControlCore is kind enough to
// pre-evaluate that for us, and shove the real value into the
// Color member, regardless if the mark has a literal value set.
brush.Color(static_cast<til::color>(m.Color.Color));
r.Fill(brush);
r.Width(16.0f / 3.0f); // pip width - 1/3rd of the scrollbar width.
r.Height(2);
const auto markRow = m.Start.Y;
const auto fractionalHeight = markRow / totalBufferRows;
const auto relativePos = fractionalHeight * fullHeight;
ScrollBarCanvas().Children().Append(r);
Windows::UI::Xaml::Controls::Canvas::SetTop(r, relativePos);
}
}
}
// Method Description:
// - Loads the search box from the xaml UI and focuses it.
void TermControl::CreateSearchBoxControl()
@@ -404,6 +442,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
newMargin.Right,
newMargin.Bottom });
}
_showMarksInScrollbar = settings.ShowMarks();
// Clear out all the current marks
ScrollBarCanvas().Children().Clear();
// When we hot reload the settings, the core will send us a scrollbar
// update. If we enabled scrollbar marks, then great, when we handle
// that message, we'll redraw them.
}
// Method Description:
@@ -637,7 +682,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// clever way around asking the core for this.
til::point TermControl::GetFontSize() const
{
return til::point{ til::math::rounding, _core.FontSize().Width, _core.FontSize().Height };
return { til::math::rounding, _core.FontSize().Width, _core.FontSize().Height };
}
const Windows::UI::Xaml::Thickness TermControl::GetPadding()
@@ -1137,7 +1182,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// Manually show the cursor when a key is pressed. Restarting
// the timer prevents flickering.
_core.CursorOn(true);
_core.CursorOn(!_core.IsInMarkMode());
_cursorTimer->Start();
}
@@ -1608,7 +1653,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_cursorTimer)
{
// When the terminal focuses, show the cursor immediately
_core.CursorOn(true);
_core.CursorOn(!_core.IsInMarkMode());
_cursorTimer->Start();
}
@@ -1859,6 +1904,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.SelectAll();
}
bool TermControl::ToggleBlockSelection()
{
return _core.ToggleBlockSelection();
}
void TermControl::ToggleMarkMode()
{
_core.ToggleMarkMode();
}
void TermControl::Close()
{
if (!_IsClosing())
@@ -1964,7 +2019,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// The family is only used to determine if the font is truetype or
// not, but DX doesn't use that info at all.
// The Codepage is additionally not actually used by the DX engine at all.
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, gsl::narrow_cast<short>(fontSize) }, CP_UTF8, false };
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontSize }, CP_UTF8, false };
FontInfoDesired desiredFont = { actualFont };
// Create a DX engine and initialize it with our font and DPI. We'll
@@ -2853,4 +2908,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.OwningHwnd();
}
void TermControl::AddMark(const Control::ScrollMark& mark)
{
_core.AddMark(mark);
}
void TermControl::ClearMark() { _core.ClearMark(); }
void TermControl::ClearAllMarks() { _core.ClearAllMarks(); }
void TermControl::ScrollToMark(const Control::ScrollToMarkDirection& direction) { _core.ScrollToMark(direction); }
Windows::Foundation::Collections::IVector<Control::ScrollMark> TermControl::ScrollMarks() const
{
return _core.ScrollMarks();
}
}

View File

@@ -38,6 +38,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void PasteTextFromClipboard();
void SelectAll();
bool ToggleBlockSelection();
void ToggleMarkMode();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();
@@ -65,6 +67,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);
Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
void AddMark(const Control::ScrollMark& mark);
void ClearMark();
void ClearAllMarks();
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
#pragma endregion
void ScrollViewport(int viewTop);
@@ -193,6 +202,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
bool _showMarksInScrollbar{ false };
inline bool _IsClosing() const noexcept
{
@@ -285,6 +295,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
};
}

View File

@@ -51,6 +51,8 @@ namespace Microsoft.Terminal.Control
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
void PasteTextFromClipboard();
void SelectAll();
Boolean ToggleBlockSelection();
void ToggleMarkMode();
void ClearBuffer(ClearBufferType clearType);
void Close();
Windows.Foundation.Size CharacterDimensions { get; };

View File

@@ -1243,6 +1243,30 @@
Style="{StaticResource ForkedScrollbarTemplate}"
ValueChanged="_ScrollbarChangeHandler"
ViewportSize="10" />
<Grid x:Name="ScrollMarksGrid"
Grid.Column="1"
Width="{StaticResource ScrollBarSize}"
HorizontalAlignment="Right"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="0"
Height="{StaticResource ScrollBarSize}" />
<Canvas x:Name="ScrollBarCanvas"
Grid.Row="1"
Width="{StaticResource ScrollBarSize}"
HorizontalAlignment="Right"
VerticalAlignment="Stretch" />
<Border Grid.Row="2"
Height="{StaticResource ScrollBarSize}" />
</Grid>
</Grid>
<local:TSFInputControl x:Name="TSFInputControl"

View File

@@ -147,9 +147,14 @@
<PRIResource Include="Resources\en-US\Resources.resw" />
<OCResourceDirectory Include="Resources" />
</ItemGroup>
<ItemGroup Condition="'$(WindowsTerminalBranding)'=='' or '$(WindowsTerminalBranding)'=='Dev' or '$(WindowsTerminalBranding)'=='Preview'">
<!-- GH#13252 Only vend this dependency for Dev and Preview builds. -->
<SDKReference Include="Microsoft.Midi.GmDls, Version=10.0.22000.0" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
<ProjectReference Include="..\..\types\lib\types.vcxproj" />
<ProjectReference Include="..\..\audio\midi\lib\midi.vcxproj" />
<ProjectReference Include="..\..\buffer\out\lib\bufferout.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\renderer\base\lib\base.vcxproj" />
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj" />

View File

@@ -45,6 +45,7 @@
#include <winrt/Windows.UI.Xaml.Input.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Windows.ui.xaml.markup.h>
#include <winrt/Windows.ui.xaml.shapes.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Windows.Storage.h>

View File

@@ -24,6 +24,15 @@ namespace Microsoft.Terminal.Core
UInt8 A;
};
// Yes, this is also just an IReference<Color>. However, IReference has some
// weird ownership semantics that just make it a pain for something as
// simple as "maybe this color doesn't have a value set".
struct OptionalColor
{
Boolean HasValue;
Microsoft.Terminal.Core.Color Color;
};
// TerminalCore declares its own Color struct to avoid depending on
// Windows.UI. Windows.Foundation.Point also exists, but it's composed of
// floating-point coordinates, when we almost always need integer coordinates.

View File

@@ -26,6 +26,9 @@ namespace Microsoft.Terminal.Core
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> TabColor;
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> StartingTabColor;
Boolean AutoMarkPrompts;
};
}

View File

@@ -17,10 +17,10 @@ namespace Microsoft::Terminal::Core
ITerminalInput& operator=(ITerminalInput&&) = default;
virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0;
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0;
virtual bool SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0;
virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0;
[[nodiscard]] virtual HRESULT UserResize(const COORD size) noexcept = 0;
[[nodiscard]] virtual HRESULT UserResize(const til::size size) noexcept = 0;
virtual void UserScrollViewport(const int viewTop) = 0;
virtual int GetScrollOffset() = 0;

View File

@@ -45,10 +45,12 @@ Terminal::Terminal() :
_snapOnInput{ true },
_altGrAliasing{ true },
_blockSelection{ false },
_markMode{ false },
_selection{ std::nullopt },
_taskbarState{ 0 },
_taskbarProgress{ 0 },
_trimBlockSelection{ false }
_trimBlockSelection{ false },
_autoMarkPrompts{ false }
{
auto passAlongInput = [&](std::deque<std::unique_ptr<IInputEvent>>& inEventsToWrite) {
if (!_pfnWriteInput)
@@ -65,12 +67,12 @@ Terminal::Terminal() :
_renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, RGB(0, 0, 0));
}
void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, Renderer& renderer)
void Terminal::Create(til::size viewportSize, til::CoordType scrollbackLines, Renderer& renderer)
{
_mutableViewport = Viewport::FromDimensions({ 0, 0 }, viewportSize);
_scrollbackLines = scrollbackLines;
const COORD bufferSize{ viewportSize.X,
Utils::ClampToShortMax(viewportSize.Y + scrollbackLines, 1) };
const til::size bufferSize{ viewportSize.X,
Utils::ClampToShortMax(viewportSize.Y + scrollbackLines, 1) };
const TextAttribute attr{};
const UINT cursorSize = 12;
_mainBuffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, true, renderer);
@@ -96,8 +98,8 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, Renderer& rende
void Terminal::CreateFromSettings(ICoreSettings settings,
Renderer& renderer)
{
const COORD viewportSize{ Utils::ClampToShortMax(settings.InitialCols(), 1),
Utils::ClampToShortMax(settings.InitialRows(), 1) };
const til::size viewportSize{ Utils::ClampToShortMax(settings.InitialCols(), 1),
Utils::ClampToShortMax(settings.InitialRows(), 1) };
// TODO:MSFT:20642297 - Support infinite scrollback here, if HistorySize is -1
Create(viewportSize, Utils::ClampToShortMax(settings.HistorySize(), 0), renderer);
@@ -120,6 +122,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
_suppressApplicationTitle = settings.SuppressApplicationTitle();
_startingTitle = settings.StartingTitle();
_trimBlockSelection = settings.TrimBlockSelection();
_autoMarkPrompts = settings.AutoMarkPrompts();
_terminalInput->ForceDisableWin32InputMode(settings.ForceVTInput());
@@ -210,6 +213,11 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance)
}
_defaultCursorShape = cursorShape;
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks whose color
// may have changed.
_NotifyScrollEvent();
}
void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle)
@@ -242,7 +250,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
// - S_OK if we successfully resized the terminal, S_FALSE if there was
// nothing to do (the viewportSize is the same as our current size), or an
// appropriate HRESULT for failing to resize.
[[nodiscard]] HRESULT Terminal::UserResize(const COORD viewportSize) noexcept
[[nodiscard]] HRESULT Terminal::UserResize(const til::size viewportSize) noexcept
{
const auto oldDimensions = _GetMutableViewport().Dimensions();
if (viewportSize == oldDimensions)
@@ -257,7 +265,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
if (_inAltBuffer())
{
// stash this resize for the future.
_deferredResize = til::size{ viewportSize };
_deferredResize = viewportSize;
_altBuffer->GetCursor().StartDeferDrawing();
// we're capturing `this` here because when we exit, we want to EndDefer on the (newly created) active buffer.
@@ -272,19 +280,19 @@ std::wstring_view Terminal::GetWorkingDirectory()
// Since the _mutableViewport is no longer the size of the actual
// viewport, then update our _altBufferSize tracker we're using to help
// us out here.
_altBufferSize = til::size{ viewportSize };
_altBufferSize = viewportSize;
return S_OK;
}
const auto dx = ::base::ClampSub(viewportSize.X, oldDimensions.X);
const short newBufferHeight = ::base::ClampAdd(viewportSize.Y, _scrollbackLines);
const auto dx = viewportSize.X - oldDimensions.X;
const auto newBufferHeight = std::clamp(viewportSize.Y + _scrollbackLines, 0, SHRT_MAX);
COORD bufferSize{ viewportSize.X, newBufferHeight };
til::size bufferSize{ viewportSize.X, newBufferHeight };
// This will be used to determine where the viewport should be in the new buffer.
const auto oldViewportTop = _mutableViewport.Top();
auto newViewportTop = oldViewportTop;
auto newVisibleTop = ::base::saturated_cast<short>(_VisibleStartIndex());
auto newVisibleTop = _VisibleStartIndex();
// If the original buffer had _no_ scroll offset, then we should be at the
// bottom in the new buffer as well. Track that case now.
@@ -328,7 +336,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
oldRows.mutableViewportTop = oldViewportTop;
oldRows.visibleViewportTop = newVisibleTop;
const std::optional<short> oldViewStart{ oldViewportTop };
const std::optional oldViewStart{ oldViewportTop };
RETURN_IF_FAILED(TextBuffer::Reflow(*_mainBuffer.get(),
*newTextBuffer.get(),
_mutableViewport,
@@ -382,7 +390,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
const auto maxRow = std::max(newLastChar.Y, newCursorPos.Y);
const short proposedTopFromLastLine = ::base::ClampAdd(::base::ClampSub(maxRow, viewportSize.Y), 1);
const auto proposedTopFromLastLine = maxRow - viewportSize.Y + 1;
const auto proposedTopFromScrollback = newViewportTop;
auto proposedTop = std::max(proposedTopFromLastLine,
@@ -432,7 +440,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
// Make sure the proposed viewport is within the bounds of the buffer.
// First make sure the top is >=0
proposedTop = std::max(static_cast<short>(0), proposedTop);
proposedTop = std::max(0, proposedTop);
// If the new bottom would be below the bottom of the buffer, then slide the
// top up so that we'll still fit within the buffer.
@@ -451,7 +459,7 @@ std::wstring_view Terminal::GetWorkingDirectory()
// Make sure that we don't scroll past the mutableViewport at the bottom of the buffer
newVisibleTop = std::min(newVisibleTop, _mutableViewport.Top());
// Make sure we don't scroll past the top of the scrollback
newVisibleTop = std::max<short>(newVisibleTop, 0);
newVisibleTop = std::max(newVisibleTop, 0);
// If the old scrolloffset was 0, then we weren't scrolled back at all
// before, and shouldn't be now either.
@@ -554,7 +562,7 @@ bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton,
// - Given a coord, get the URI at that location
// Arguments:
// - The position
std::wstring Terminal::GetHyperlinkAtPosition(const COORD position)
std::wstring Terminal::GetHyperlinkAtPosition(const til::point position)
{
auto attr = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr();
if (attr.IsHyperlink())
@@ -570,8 +578,8 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position)
const auto end = result->stop;
std::wstring uri;
const auto startIter = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(start.to_win32_coord()));
const auto endIter = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(end.to_win32_coord()));
const auto startIter = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(start));
const auto endIter = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(end));
for (auto iter = startIter; iter != endIter; ++iter)
{
uri += iter->Chars();
@@ -587,7 +595,7 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position)
// - The position of the text
// Return value:
// - The hyperlink ID
uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position)
uint16_t Terminal::GetHyperlinkIdAtPosition(const til::point position)
{
return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId();
}
@@ -598,9 +606,9 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position)
// - The position
// Return value:
// - The interval representing the start and end coordinates
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromPosition(const COORD position)
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromPosition(const til::point position)
{
const auto results = _patternIntervalTree.findOverlapping(til::point{ position.X + 1, position.Y }, til::point{ position });
const auto results = _patternIntervalTree.findOverlapping({ position.X + 1, position.Y }, position);
if (results.size() > 0)
{
for (const auto& result : results)
@@ -715,16 +723,14 @@ bool Terminal::SendKeyEvent(const WORD vkey,
// Return Value:
// - true if we translated the key event, and it should not be processed any further.
// - false if we did not translate the key, and it should be processed into a character.
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state)
bool Terminal::SendMouseEvent(til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state)
{
// GH#6401: VT applications should be able to receive mouse events from outside the
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
// We shouldn't throw away perfectly good events when they're offscreen, so we just
// clamp them to be within the range [(0, 0), (W, H)].
#pragma warning(suppress : 26496) // analysis can't tell we're assigning through a reference below
auto clampedPos{ viewportPos };
_GetMutableViewport().ToOrigin().Clamp(clampedPos);
return _terminalInput->HandleMouse(til::point{ clampedPos }, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state);
_GetMutableViewport().ToOrigin().Clamp(viewportPos);
return _terminalInput->HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state);
}
// Method Description:
@@ -751,6 +757,22 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro
vkey = _VirtualKeyFromCharacter(ch);
}
// GH#1527: When the user has auto mark prompts enabled, we're going to try
// and heuristically detect if this was the line the prompt was on.
// * If the key was an Enter keypress (Terminal.app also marks ^C keypresses
// as prompts. That's omitted for now.)
// * AND we're not in the alt buffer
//
// Then treat this line like it's a prompt mark.
if (_autoMarkPrompts && vkey == VK_RETURN && !_inAltBuffer())
{
DispatchTypes::ScrollMark mark;
mark.category = DispatchTypes::MarkCategory::Prompt;
// Don't set the color - we'll automatically use the DEFAULT_FOREGROUND
// color for any MarkCategory::Prompt marks without one set.
AddMark(mark);
}
// Unfortunately, the UI doesn't give us both a character down and a
// character up event, only a character received event. So fake sending both
// to the terminal input translator. Unless it's in win32-input-mode, it'll
@@ -783,8 +805,8 @@ void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree<til::point, si
{
const auto vis = _VisibleStartIndex();
auto invalidate = [=](const PointTree::interval& interval) {
COORD startCoord{ gsl::narrow<SHORT>(interval.start.x), gsl::narrow<SHORT>(interval.start.y + vis) };
COORD endCoord{ gsl::narrow<SHORT>(interval.stop.x), gsl::narrow<SHORT>(interval.stop.y + vis) };
til::point startCoord{ interval.start.x, interval.start.y + vis };
til::point endCoord{ interval.stop.x, interval.stop.y + vis };
_InvalidateFromCoords(startCoord, endCoord);
};
tree.visit_all(invalidate);
@@ -794,30 +816,30 @@ void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree<til::point, si
// - Given start and end coords, invalidates all the regions between them
// Arguments:
// - The start and end coords
void Terminal::_InvalidateFromCoords(const COORD start, const COORD end)
void Terminal::_InvalidateFromCoords(const til::point start, const til::point end)
{
if (start.Y == end.Y)
{
SMALL_RECT region{ start.X, start.Y, end.X, end.Y };
til::inclusive_rect region{ start.X, start.Y, end.X, end.Y };
_activeBuffer().TriggerRedraw(Viewport::FromInclusive(region));
}
else
{
const auto rowSize = gsl::narrow<SHORT>(_activeBuffer().GetRowByOffset(0).size());
const auto rowSize = _activeBuffer().GetRowByOffset(0).size();
// invalidate the first line
SMALL_RECT region{ start.X, start.Y, gsl::narrow<short>(rowSize - 1), gsl::narrow<short>(start.Y) };
til::inclusive_rect region{ start.X, start.Y, rowSize - 1, start.Y };
_activeBuffer().TriggerRedraw(Viewport::FromInclusive(region));
if ((end.Y - start.Y) > 1)
{
// invalidate the lines in between the first and last line
region = SMALL_RECT{ 0, start.Y + 1, gsl::narrow<short>(rowSize - 1), gsl::narrow<short>(end.Y - 1) };
region = til::inclusive_rect{ 0, start.Y + 1, rowSize - 1, end.Y - 1 };
_activeBuffer().TriggerRedraw(Viewport::FromInclusive(region));
}
// invalidate the last line
region = SMALL_RECT{ 0, end.Y, end.X, end.Y };
region = til::inclusive_rect{ 0, end.Y, end.X, end.Y };
_activeBuffer().TriggerRedraw(Viewport::FromInclusive(region));
}
}
@@ -962,16 +984,25 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
#endif
}
// Method Description:
// - Get a reference to the the terminal's read/write lock.
// Return Value:
// - a ticket_lock which can be used to manually lock or unlock the terminal.
til::ticket_lock& Terminal::GetReadWriteLock() noexcept
{
return _readWriteLock;
}
Viewport Terminal::_GetMutableViewport() const noexcept
{
// GH#3493: if we're in the alt buffer, then it's possible that the mutable
// viewport's size hasn't been updated yet. In that case, use the
// temporarily stashed _altBufferSize instead.
return _inAltBuffer() ? Viewport::FromDimensions(_altBufferSize.to_win32_coord()) :
return _inAltBuffer() ? Viewport::FromDimensions(_altBufferSize) :
_mutableViewport;
}
short Terminal::GetBufferHeight() const noexcept
til::CoordType Terminal::GetBufferHeight() const noexcept
{
return _GetMutableViewport().BottomExclusive();
}
@@ -984,7 +1015,7 @@ int Terminal::ViewStartIndex() const noexcept
int Terminal::ViewEndIndex() const noexcept
{
return _inAltBuffer() ? _altBufferSize.height : _mutableViewport.BottomInclusive();
return _inAltBuffer() ? _altBufferSize.height - 1 : _mutableViewport.BottomInclusive();
}
// _VisibleStartIndex is the first visible line of the buffer
@@ -1005,8 +1036,8 @@ Viewport Terminal::_GetVisibleViewport() const noexcept
// GH#3493: if we're in the alt buffer, then it's possible that the mutable
// viewport's size hasn't been updated yet. In that case, use the
// temporarily stashed _altBufferSize instead.
const COORD origin{ 0, gsl::narrow<short>(_VisibleStartIndex()) };
const auto size{ _inAltBuffer() ? _altBufferSize.to_win32_coord() :
const til::point origin{ 0, _VisibleStartIndex() };
const auto size{ _inAltBuffer() ? _altBufferSize :
_mutableViewport.Dimensions() };
return Viewport::FromDimensions(origin,
size);
@@ -1050,7 +1081,7 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
{
// If "wch" was a surrogate character, we just consumed 2 code units above.
// -> Increment "i" by 1 in that case and thus by 2 in total in this iteration.
proposedCursorPosition.X += gsl::narrow<SHORT>(cellDistance);
proposedCursorPosition.X += cellDistance;
i += inputDistance - 1;
}
else
@@ -1089,7 +1120,7 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
cursor.EndDeferDrawing();
}
void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
void Terminal::_AdjustCursorPosition(const til::point proposedPosition)
{
#pragma warning(suppress : 26496) // cpp core checks wants this const but it's modified below.
auto proposedCursorPosition = proposedPosition;
@@ -1098,7 +1129,7 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
// If we're about to scroll past the bottom of the buffer, instead cycle the
// buffer.
SHORT rowsPushedOffTopOfBuffer = 0;
til::CoordType rowsPushedOffTopOfBuffer = 0;
const auto newRows = std::max(0, proposedCursorPosition.Y - bufferSize.Height() + 1);
if (proposedCursorPosition.Y >= bufferSize.Height())
{
@@ -1153,7 +1184,7 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
// In the alt buffer, we never need to adjust _mutableViewport, which is the viewport of the main buffer.
if (newViewTop != _mutableViewport.Top())
{
_mutableViewport = Viewport::FromDimensions({ 0, gsl::narrow<short>(newViewTop) },
_mutableViewport = Viewport::FromDimensions({ 0, newViewTop },
_mutableViewport.Dimensions());
updatedViewport = true;
}
@@ -1190,10 +1221,21 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
if (rowsPushedOffTopOfBuffer != 0)
{
if (_scrollMarks.size() > 0)
{
for (auto& mark : _scrollMarks)
{
mark.start.y -= rowsPushedOffTopOfBuffer;
}
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
_scrollMarks.end(),
[](const VirtualTerminal::DispatchTypes::ScrollMark& m) { return m.start.y < 0; }),
_scrollMarks.end());
}
// We have to report the delta here because we might have circled the text buffer.
// That didn't change the viewport and therefore the TriggerScroll(void)
// method can't detect the delta on its own.
COORD delta{ 0, gsl::narrow_cast<short>(-rowsPushedOffTopOfBuffer) };
til::point delta{ 0, -rowsPushedOffTopOfBuffer };
_activeBuffer().TriggerScroll(delta);
}
}
@@ -1301,6 +1343,15 @@ void Terminal::SetShowWindowCallback(std::function<void(bool)> pfn) noexcept
_pfnShowWindowChanged.swap(pfn);
}
// Method Description:
// - Allows setting a callback for playing MIDI notes.
// Arguments:
// - pfn: a function callback that takes a note number, a velocity level, and a duration
void Terminal::SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept
{
_pfnPlayMidiNote.swap(pfn);
}
// Method Description:
// - Sets the cursor to be currently on. On/Off is tracked independently of
// cursor visibility (hidden/visible). On/off is controlled by the cursor
@@ -1318,7 +1369,7 @@ void Terminal::SetCursorOn(const bool isOn)
bool Terminal::IsCursorBlinkingAllowed() const noexcept
{
const auto& cursor = _activeBuffer().GetCursor();
return cursor.IsBlinkingAllowed();
return !_markMode && cursor.IsBlinkingAllowed();
}
// Method Description:
@@ -1433,6 +1484,11 @@ void Terminal::ApplyScheme(const Scheme& colorScheme)
_renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, til::color{ colorScheme.CursorColor });
_renderSettings.MakeAdjustedColorArray();
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks whose color
// may have changed.
_NotifyScrollEvent();
}
bool Terminal::_inAltBuffer() const noexcept
@@ -1459,3 +1515,101 @@ void Terminal::_updateUrlDetection()
ClearPatternTree();
}
}
void Terminal::AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark,
const til::point& start,
const til::point& end)
{
if (_inAltBuffer())
{
return;
}
DispatchTypes::ScrollMark m = mark;
m.start = start;
m.end = end;
_scrollMarks.push_back(m);
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks
_NotifyScrollEvent();
}
void Terminal::ClearMark()
{
// Look for one where the cursor is, or where the selection is if we have
// one. Any mark that intersects the cursor/selection, on either side
// (inclusive), will get cleared.
const til::point cursor{ _activeBuffer().GetCursor().GetPosition() };
til::point start{ cursor };
til::point end{ cursor };
if (IsSelectionActive())
{
start = til::point{ GetSelectionAnchor() };
end = til::point{ GetSelectionEnd() };
}
_scrollMarks.erase(std::remove_if(_scrollMarks.begin(),
_scrollMarks.end(),
[&start, &end](const auto& m) {
return (m.start >= start && m.start <= end) ||
(m.end >= start && m.end <= end);
}),
_scrollMarks.end());
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks
_NotifyScrollEvent();
}
void Terminal::ClearAllMarks()
{
_scrollMarks.clear();
// Tell the control that the scrollbar has somehow changed. Used as a
// workaround to force the control to redraw any scrollbar marks
_NotifyScrollEvent();
}
const std::vector<Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark>& Terminal::GetScrollMarks() const
{
// TODO: GH#11000 - when the marks are stored per-buffer, get rid of this.
// We want to return _no_ marks when we're in the alt buffer, to effectively
// hide them. We need to return a reference, so we can't just ctor an empty
// list here just for when we're in the alt buffer.
static std::vector<DispatchTypes::ScrollMark> _altBufferMarks{};
return _inAltBuffer() ? _altBufferMarks : _scrollMarks;
}
til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) const
{
if (mark.color.has_value())
{
return *mark.color;
}
switch (mark.category)
{
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Prompt:
{
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
}
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Error:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_RED);
}
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Warning:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_YELLOW);
}
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Success:
{
return _renderSettings.GetColorTableEntry(TextColor::BRIGHT_GREEN);
}
default:
case Microsoft::Console::VirtualTerminal::DispatchTypes::MarkCategory::Info:
{
return _renderSettings.GetColorAlias(ColorAlias::DefaultForeground);
}
}
}

View File

@@ -69,8 +69,8 @@ public:
Terminal& operator=(const Terminal&) = default;
Terminal& operator=(Terminal&&) = default;
void Create(COORD viewportSize,
SHORT scrollbackLines,
void Create(til::size viewportSize,
til::CoordType scrollbackLines,
Microsoft::Console::Render::Renderer& renderer);
void CreateFromSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings,
@@ -92,8 +92,9 @@ public:
[[nodiscard]] std::unique_lock<til::ticket_lock> LockForReading();
[[nodiscard]] std::unique_lock<til::ticket_lock> LockForWriting();
til::ticket_lock& GetReadWriteLock() noexcept;
short GetBufferHeight() const noexcept;
til::CoordType GetBufferHeight() const noexcept;
int ViewStartIndex() const noexcept;
int ViewEndIndex() const noexcept;
@@ -101,6 +102,11 @@ public:
RenderSettings& GetRenderSettings() noexcept { return _renderSettings; };
const RenderSettings& GetRenderSettings() const noexcept { return _renderSettings; };
const std::vector<Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark>& GetScrollMarks() const;
void AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark,
const til::point& start,
const til::point& end);
#pragma region ITerminalApi
// These methods are defined in TerminalApi.cpp
void PrintString(const std::wstring_view string) override;
@@ -117,28 +123,36 @@ public:
void LineFeed(const bool withReturn) override;
void SetWindowTitle(const std::wstring_view title) override;
CursorType GetUserDefaultCursorStyle() const override;
bool ResizeWindow(const size_t width, const size_t height) override;
bool ResizeWindow(const til::CoordType width, const til::CoordType height) override;
void SetConsoleOutputCP(const unsigned int codepage) override;
unsigned int GetConsoleOutputCP() const override;
void EnableXtermBracketedPasteMode(const bool enabled) override;
void CopyToClipboard(std::wstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
void SetWorkingDirectory(std::wstring_view uri) override;
void PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) override;
void ShowWindow(bool showOrHide) override;
void UseAlternateScreenBuffer() override;
void UseMainScreenBuffer() override;
void AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) override;
bool IsConsolePty() const override;
bool IsVtInputEnabled() const override;
void NotifyAccessibilityChange(const til::rect& changedRect) override;
#pragma endregion
void ClearMark();
void ClearAllMarks();
til::color GetColorForMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark) const;
#pragma region ITerminalInput
// These methods are defined in Terminal.cpp
bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override;
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
bool SendMouseEvent(const til::point viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
[[nodiscard]] HRESULT UserResize(const til::size viewportSize) noexcept override;
void UserScrollViewport(const int viewTop) override;
int GetScrollOffset() noexcept override;
@@ -148,14 +162,14 @@ public:
void FocusChanged(const bool focused) noexcept override;
std::wstring GetHyperlinkAtPosition(const COORD position);
uint16_t GetHyperlinkIdAtPosition(const COORD position);
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromPosition(const COORD position);
std::wstring GetHyperlinkAtPosition(const til::point position);
uint16_t GetHyperlinkIdAtPosition(const til::point position);
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromPosition(const til::point position);
#pragma endregion
#pragma region IBaseData(base to IRenderData and IUiaData)
Microsoft::Console::Types::Viewport GetViewport() noexcept override;
COORD GetTextBufferEndPosition() const noexcept override;
til::point GetTextBufferEndPosition() const noexcept override;
const TextBuffer& GetTextBuffer() const noexcept override;
const FontInfo& GetFontInfo() const noexcept override;
@@ -165,7 +179,7 @@ public:
#pragma region IRenderData
// These methods are defined in TerminalRenderData.cpp
COORD GetCursorPosition() const noexcept override;
til::point GetCursorPosition() const noexcept override;
bool IsCursorVisible() const noexcept override;
bool IsCursorOn() const noexcept override;
ULONG GetCursorHeight() const noexcept override;
@@ -176,7 +190,7 @@ public:
const bool IsGridLineDrawingAllowed() noexcept override;
const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override;
const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override;
const std::vector<size_t> GetPatternId(const COORD location) const noexcept override;
const std::vector<size_t> GetPatternId(const til::point location) const noexcept override;
#pragma endregion
#pragma region IUiaData
@@ -185,11 +199,11 @@ public:
const bool IsSelectionActive() const noexcept override;
const bool IsBlockSelection() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
const COORD GetSelectionAnchor() const noexcept override;
const COORD GetSelectionEnd() const noexcept override;
void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
const til::point GetSelectionAnchor() const noexcept override;
const til::point GetSelectionEnd() const noexcept override;
const std::wstring_view GetConsoleTitle() const noexcept override;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
void ColorSelection(const til::point coordSelectionStart, const til::point coordSelectionEnd, const TextAttribute) override;
const bool IsUiaDataInitialized() const noexcept override;
#pragma endregion
@@ -201,6 +215,7 @@ public:
void SetCursorPositionChangedCallback(std::function<void()> pfn) noexcept;
void TaskbarProgressChangedCallback(std::function<void()> pfn) noexcept;
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void SetCursorOn(const bool isOn);
bool IsCursorBlinkingAllowed() const noexcept;
@@ -234,15 +249,17 @@ public:
Viewport,
Buffer
};
void MultiClickSelection(const COORD viewportPos, SelectionExpansion expansionMode);
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
void MultiClickSelection(const til::point viewportPos, SelectionExpansion expansionMode);
void SetSelectionAnchor(const til::point position);
void SetSelectionEnd(const til::point position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode);
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
void SelectAll();
bool IsInMarkMode() const;
void ToggleMarkMode();
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
static UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey);
UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const;
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
#pragma endregion
@@ -268,6 +285,7 @@ private:
std::function<void()> _pfnCursorPositionChanged;
std::function<void()> _pfnTaskbarProgressChanged;
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
@@ -284,6 +302,7 @@ private:
bool _suppressApplicationTitle;
bool _bracketedPasteMode;
bool _trimBlockSelection;
bool _autoMarkPrompts;
size_t _taskbarState;
size_t _taskbarProgress;
@@ -297,25 +316,26 @@ private:
FontInfo _fontInfo{ DEFAULT_FONT_FACE, TMPF_TRUETYPE, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false };
#pragma region Text Selection
// a selection is represented as a range between two COORDs (start and end)
// the pivot is the COORD that remains selected when you extend a selection in any direction
// the pivot is the til::point that remains selected when you extend a selection in any direction
// this is particularly useful when a word selection is extended over its starting point
// see TerminalSelection.cpp for more information
struct SelectionAnchors
{
COORD start;
COORD end;
COORD pivot;
til::point start;
til::point end;
til::point pivot;
};
std::optional<SelectionAnchors> _selection;
bool _blockSelection;
std::wstring _wordDelimiters;
SelectionExpansion _multiClickSelectionMode;
bool _markMode;
#pragma endregion
std::unique_ptr<TextBuffer> _mainBuffer;
std::unique_ptr<TextBuffer> _altBuffer;
Microsoft::Console::Types::Viewport _mutableViewport;
SHORT _scrollbackLines;
til::CoordType _scrollbackLines;
bool _detectURLs{ false };
til::size _altBufferSize;
@@ -339,7 +359,7 @@ private:
interval_tree::IntervalTree<til::point, size_t> _patternIntervalTree;
void _InvalidatePatternTree(interval_tree::IntervalTree<til::point, size_t>& tree);
void _InvalidateFromCoords(const COORD start, const COORD end);
void _InvalidateFromCoords(const til::point start, const til::point end);
// Since virtual keys are non-zero, you assume that this field is empty/invalid if it is.
struct KeyEventCodes
@@ -349,6 +369,8 @@ private:
};
std::optional<KeyEventCodes> _lastKeyEventCodes;
std::vector<Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark> _scrollMarks;
static WORD _ScanCodeFromVirtualKey(const WORD vkey) noexcept;
static WORD _VirtualKeyFromScanCode(const WORD scanCode) noexcept;
static WORD _VirtualKeyFromCharacter(const wchar_t ch) noexcept;
@@ -365,7 +387,7 @@ private:
void _WriteBuffer(const std::wstring_view& stringView);
void _AdjustCursorPosition(const COORD proposedPosition);
void _AdjustCursorPosition(const til::point proposedPosition);
void _NotifyScrollEvent() noexcept;
@@ -377,14 +399,14 @@ private:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
std::vector<SMALL_RECT> _GetSelectionRects() const noexcept;
std::pair<COORD, COORD> _PivotSelection(const COORD targetPos, bool& targetStart) const;
std::pair<COORD, COORD> _ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const;
COORD _ConvertToBufferCell(const COORD viewportPos) const;
void _MoveByChar(SelectionDirection direction, COORD& pos);
void _MoveByWord(SelectionDirection direction, COORD& pos);
void _MoveByViewport(SelectionDirection direction, COORD& pos);
void _MoveByBuffer(SelectionDirection direction, COORD& pos);
std::vector<til::inclusive_rect> _GetSelectionRects() const noexcept;
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const;
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
til::point _ConvertToBufferCell(const til::point viewportPos) const;
void _MoveByChar(SelectionDirection direction, til::point& pos);
void _MoveByWord(SelectionDirection direction, til::point& pos);
void _MoveByViewport(SelectionDirection direction, til::point& pos);
void _MoveByBuffer(SelectionDirection direction, til::point& pos);
#pragma endregion
#ifdef UNIT_TESTING

View File

@@ -45,7 +45,7 @@ void Terminal::SetViewportPosition(const til::point position)
if (!_inAltBuffer())
{
const auto dimensions = _GetMutableViewport().Dimensions();
_mutableViewport = Viewport::FromDimensions(position.to_win32_coord(), dimensions);
_mutableViewport = Viewport::FromDimensions(position, dimensions);
Terminal::_NotifyScrollEvent();
}
}
@@ -106,7 +106,7 @@ CursorType Terminal::GetUserDefaultCursorStyle() const
return _defaultCursorShape;
}
bool Terminal::ResizeWindow(const size_t /*width*/, const size_t /*height*/)
bool Terminal::ResizeWindow(const til::CoordType /*width*/, const til::CoordType /*height*/)
{
// TODO: This will be needed to support various resizing sequences. See also GH#1860.
return false;
@@ -188,10 +188,15 @@ void Terminal::SetWorkingDirectory(std::wstring_view uri)
_workingDirectory = uri;
}
void Terminal::PlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration)
{
_pfnPlayMidiNote(noteNumber, velocity, duration);
}
void Terminal::UseAlternateScreenBuffer()
{
// the new alt buffer is exactly the size of the viewport.
_altBufferSize = til::size{ _mutableViewport.Dimensions() };
_altBufferSize = _mutableViewport.Dimensions();
const auto cursorSize = _mainBuffer->GetCursor().GetSize();
@@ -199,7 +204,7 @@ void Terminal::UseAlternateScreenBuffer()
_mainBuffer->ClearPatternRecognizers();
// Create a new alt buffer
_altBuffer = std::make_unique<TextBuffer>(_altBufferSize.to_win32_coord(),
_altBuffer = std::make_unique<TextBuffer>(_altBufferSize,
TextAttribute{},
cursorSize,
true,
@@ -270,7 +275,7 @@ void Terminal::UseMainScreenBuffer()
if (_deferredResize.has_value())
{
LOG_IF_FAILED(UserResize(_deferredResize.value().to_win32_coord()));
LOG_IF_FAILED(UserResize(_deferredResize.value()));
_deferredResize = std::nullopt;
}
@@ -293,6 +298,12 @@ void Terminal::UseMainScreenBuffer()
CATCH_LOG();
}
void Terminal::AddMark(const Microsoft::Console::VirtualTerminal::DispatchTypes::ScrollMark& mark)
{
const til::point cursorPos{ _activeBuffer().GetCursor().GetPosition() };
AddMark(mark, cursorPos, cursorPos);
}
// Method Description:
// - Reacts to a client asking us to show or hide the window.
// Arguments:

View File

@@ -43,9 +43,9 @@ using namespace Microsoft::Terminal::Core;
// - Helper to determine the selected region of the buffer. Used for rendering.
// Return Value:
// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin.
std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
std::vector<til::inclusive_rect> Terminal::_GetSelectionRects() const noexcept
{
std::vector<SMALL_RECT> result;
std::vector<til::inclusive_rect> result;
if (!IsSelectionActive())
{
@@ -66,7 +66,7 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
// - None
// Return Value:
// - None
const COORD Terminal::GetSelectionAnchor() const noexcept
const til::point Terminal::GetSelectionAnchor() const noexcept
{
return _selection->start;
}
@@ -77,7 +77,7 @@ const COORD Terminal::GetSelectionAnchor() const noexcept
// - None
// Return Value:
// - None
const COORD Terminal::GetSelectionEnd() const noexcept
const til::point Terminal::GetSelectionEnd() const noexcept
{
return _selection->end;
}
@@ -101,7 +101,7 @@ const bool Terminal::IsBlockSelection() const noexcept
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - expansionMode: the SelectionExpansion to dictate the boundaries of the selection anchors
void Terminal::MultiClickSelection(const COORD viewportPos, SelectionExpansion expansionMode)
void Terminal::MultiClickSelection(const til::point viewportPos, SelectionExpansion expansionMode)
{
// set the selection pivot to expand the selection using SetSelectionEnd()
_selection = SelectionAnchors{};
@@ -119,7 +119,7 @@ void Terminal::MultiClickSelection(const COORD viewportPos, SelectionExpansion e
// - Record the position of the beginning of a selection
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetSelectionAnchor(const COORD viewportPos)
void Terminal::SetSelectionAnchor(const til::point viewportPos)
{
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
@@ -136,7 +136,7 @@ void Terminal::SetSelectionAnchor(const COORD viewportPos)
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - newExpansionMode: overwrites the _multiClickSelectionMode for this function call. Used for ShiftClick
void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionExpansion> newExpansionMode)
void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<SelectionExpansion> newExpansionMode)
{
if (!_selection.has_value())
{
@@ -180,7 +180,7 @@ void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionE
// - targetStart: if true, target will be the new start. Otherwise, target will be the new end.
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_PivotSelection(const COORD targetPos, bool& targetStart) const
std::pair<til::point, til::point> Terminal::_PivotSelection(const til::point targetPos, bool& targetStart) const
{
if (targetStart = _activeBuffer().GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
{
@@ -202,7 +202,7 @@ std::pair<COORD, COORD> Terminal::_PivotSelection(const COORD targetPos, bool& t
// - anchors: a pair of selection anchors representing a desired selection
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const
std::pair<til::point, til::point> Terminal::_ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const
{
auto start = anchors.first;
auto end = anchors.second;
@@ -235,13 +235,41 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
_blockSelection = isEnabled;
}
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey)
bool Terminal::IsInMarkMode() const
{
if (mods.IsShiftPressed() && !mods.IsAltPressed())
return _markMode;
}
void Terminal::ToggleMarkMode()
{
if (_markMode)
{
// Exit Mark Mode
ClearSelection();
}
else
{
// Enter Mark Mode
// NOTE: directly set cursor state. We already should have locked before calling this function.
_activeBuffer().GetCursor().SetIsOn(false);
const auto cursorPos{ _activeBuffer().GetCursor().GetPosition() };
_selection = SelectionAnchors{};
_selection->start = cursorPos;
_selection->end = cursorPos;
_selection->pivot = cursorPos;
_markMode = true;
_blockSelection = false;
}
}
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
{
if ((_markMode || mods.IsShiftPressed()) && !mods.IsAltPressed())
{
if (mods.IsCtrlPressed())
{
// Ctrl + Shift + _
// (Mark Mode) Ctrl + _
switch (vkey)
{
case VK_LEFT:
@@ -257,6 +285,7 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
else
{
// Shift + _
// (Mark Mode) Just the vkeys
switch (vkey)
{
case VK_HOME:
@@ -281,11 +310,19 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
return std::nullopt;
}
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode)
// Method Description:
// - updates the selection endpoints based on a direction and expansion mode. Primarily used for keyboard selection.
// Arguments:
// - direction: the direction to move the selection endpoint in
// - mode: the type of movement to be performed (i.e. move by word)
// - mods: the key modifiers pressed when performing this update
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
{
// 1. Figure out which endpoint to update
// One of the endpoints is the pivot, signifying that the other endpoint is the one we want to move.
const auto movingEnd{ _selection->start == _selection->pivot };
// If we're in mark mode, shift dictates whether you are moving the end or not.
// Otherwise, we're updating an existing selection, so one of the endpoints is the pivot,
// signifying that the other endpoint is the one we want to move.
const auto movingEnd{ _markMode ? mods.IsShiftPressed() : _selection->start == _selection->pivot };
auto targetPos{ movingEnd ? _selection->end : _selection->start };
// 2. Perform the movement
@@ -307,8 +344,23 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
// 3. Actually modify the selection
// NOTE: targetStart doesn't matter here
auto targetStart = false;
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
if (_markMode)
{
// [Mark Mode]
// - moveSelectionEnd --> just move end (i.e. shift + arrow keys)
// - !moveSelectionEnd --> move all three (i.e. just use arrow keys)
_selection->end = targetPos;
if (!movingEnd)
{
_selection->start = targetPos;
_selection->pivot = targetPos;
}
}
else
{
auto targetStart = false;
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
}
// 4. Scroll (if necessary)
if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
@@ -338,38 +390,39 @@ void Terminal::SelectAll()
_selection->pivot = _selection->end;
}
void Terminal::_MoveByChar(SelectionDirection direction, COORD& pos)
void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
{
switch (direction)
{
case SelectionDirection::Left:
_activeBuffer().GetSize().DecrementInBounds(pos);
pos = _activeBuffer().GetGlyphStart(til::point{ pos }).to_win32_coord();
pos = _activeBuffer().GetGlyphStart(pos);
break;
case SelectionDirection::Right:
_activeBuffer().GetSize().IncrementInBounds(pos);
pos = _activeBuffer().GetGlyphEnd(til::point{ pos }).to_win32_coord();
pos = _activeBuffer().GetGlyphEnd(pos);
break;
case SelectionDirection::Up:
{
const auto bufferSize{ _activeBuffer().GetSize() };
pos = { pos.X, std::clamp(base::ClampSub<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
pos = { pos.X, std::clamp(pos.Y - 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
break;
}
case SelectionDirection::Down:
{
const auto bufferSize{ _activeBuffer().GetSize() };
pos = { pos.X, std::clamp(base::ClampAdd<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
pos = { pos.X, std::clamp(pos.Y + 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
break;
}
}
}
void Terminal::_MoveByWord(SelectionDirection direction, COORD& pos)
void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
{
switch (direction)
{
case SelectionDirection::Left:
{
const auto wordStartPos{ _activeBuffer().GetWordStart(pos, _wordDelimiters) };
if (_activeBuffer().GetSize().CompareInBounds(_selection->pivot, pos) < 0)
{
@@ -390,7 +443,9 @@ void Terminal::_MoveByWord(SelectionDirection direction, COORD& pos)
pos = wordStartPos;
}
break;
}
case SelectionDirection::Right:
{
const auto wordEndPos{ _activeBuffer().GetWordEnd(pos, _wordDelimiters) };
if (_activeBuffer().GetSize().CompareInBounds(pos, _selection->pivot) < 0)
{
@@ -411,6 +466,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, COORD& pos)
pos = wordEndPos;
}
break;
}
case SelectionDirection::Up:
_MoveByChar(direction, pos);
pos = _activeBuffer().GetWordStart(pos, _wordDelimiters);
@@ -422,7 +478,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, COORD& pos)
}
}
void Terminal::_MoveByViewport(SelectionDirection direction, COORD& pos)
void Terminal::_MoveByViewport(SelectionDirection direction, til::point& pos)
{
const auto bufferSize{ _activeBuffer().GetSize() };
switch (direction)
@@ -436,22 +492,22 @@ void Terminal::_MoveByViewport(SelectionDirection direction, COORD& pos)
case SelectionDirection::Up:
{
const auto viewportHeight{ _GetMutableViewport().Height() };
const auto newY{ base::ClampSub<short, short>(pos.Y, viewportHeight) };
pos = newY < bufferSize.Top() ? bufferSize.Origin() : COORD{ pos.X, newY };
const auto newY{ pos.Y - viewportHeight };
pos = newY < bufferSize.Top() ? bufferSize.Origin() : til::point{ pos.X, newY };
break;
}
case SelectionDirection::Down:
{
const auto viewportHeight{ _GetMutableViewport().Height() };
const auto mutableBottom{ _GetMutableViewport().BottomInclusive() };
const auto newY{ base::ClampAdd<short, short>(pos.Y, viewportHeight) };
pos = newY > mutableBottom ? COORD{ bufferSize.RightInclusive(), mutableBottom } : COORD{ pos.X, newY };
const auto newY{ pos.Y + viewportHeight };
pos = newY > mutableBottom ? til::point{ bufferSize.RightInclusive(), mutableBottom } : til::point{ pos.X, newY };
break;
}
}
}
void Terminal::_MoveByBuffer(SelectionDirection direction, COORD& pos)
void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos)
{
const auto bufferSize{ _activeBuffer().GetSize() };
switch (direction)
@@ -473,6 +529,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, COORD& pos)
void Terminal::ClearSelection()
{
_selection = std::nullopt;
_markMode = false;
}
// Method Description:
@@ -507,10 +564,10 @@ const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool sin
// - viewportPos: a coordinate on the viewport
// Return Value:
// - the corresponding location on the buffer
COORD Terminal::_ConvertToBufferCell(const COORD viewportPos) const
til::point Terminal::_ConvertToBufferCell(const til::point viewportPos) const
{
const auto yPos = base::ClampedNumeric<short>(_VisibleStartIndex()) + viewportPos.Y;
COORD bufferPos = { viewportPos.X, yPos };
const auto yPos = _VisibleStartIndex() + viewportPos.Y;
til::point bufferPos = { viewportPos.X, yPos };
_activeBuffer().GetSize().Clamp(bufferPos);
return bufferPos;
}
@@ -522,7 +579,7 @@ COORD Terminal::_ConvertToBufferCell(const COORD viewportPos) const
// - coordSelectionStart - Not used
// - coordSelectionEnd - Not used
// - attr - Not used.
void Terminal::ColorSelection(const COORD, const COORD, const TextAttribute)
void Terminal::ColorSelection(const til::point, const til::point, const TextAttribute)
{
THROW_HR(E_NOTIMPL);
}

View File

@@ -14,13 +14,12 @@ Viewport Terminal::GetViewport() noexcept
return _GetVisibleViewport();
}
COORD Terminal::GetTextBufferEndPosition() const noexcept
til::point Terminal::GetTextBufferEndPosition() const noexcept
{
// We use the end line of mutableViewport as the end
// of the text buffer, it always moves with the written
// text
COORD endPosition{ _GetMutableViewport().Width() - 1, gsl::narrow<short>(ViewEndIndex()) };
return endPosition;
return { _GetMutableViewport().Width() - 1, ViewEndIndex() };
}
const TextBuffer& Terminal::GetTextBuffer() const noexcept
@@ -38,7 +37,7 @@ void Terminal::SetFontInfo(const FontInfo& fontInfo)
_fontInfo = fontInfo;
}
COORD Terminal::GetCursorPosition() const noexcept
til::point Terminal::GetCursorPosition() const noexcept
{
const auto& cursor = _activeBuffer().GetCursor();
return cursor.GetPosition();
@@ -104,10 +103,10 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin
// - The location
// Return value:
// - The pattern IDs of the location
const std::vector<size_t> Terminal::GetPatternId(const COORD location) const noexcept
const std::vector<size_t> Terminal::GetPatternId(const til::point location) const noexcept
{
// Look through our interval tree for this location
const auto intervals = _patternIntervalTree.findOverlapping(til::point{ location.X + 1, location.Y }, til::point{ location });
const auto intervals = _patternIntervalTree.findOverlapping({ location.X + 1, location.Y }, location);
if (intervals.size() == 0)
{
return {};
@@ -147,7 +146,7 @@ catch (...)
return {};
}
void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd)
{
#pragma warning(push)
#pragma warning(disable : 26496) // cpp core checks wants these const, but they're decremented below.
@@ -178,8 +177,8 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
_NotifyScrollEvent();
}
realCoordStart.Y -= gsl::narrow<short>(_VisibleStartIndex());
realCoordEnd.Y -= gsl::narrow<short>(_VisibleStartIndex());
realCoordStart.Y -= _VisibleStartIndex();
realCoordEnd.Y -= _VisibleStartIndex();
SetSelectionAnchor(realCoordStart);
SetSelectionEnd(realCoordEnd, SelectionExpansion::Char);

View File

@@ -37,8 +37,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
L"qps-PLOCA",
L"qps-PLOCM",
L"ru",
L"zh-Hans-CN",
L"zh-Hant-TW",
L"zh-Hans",
L"zh-Hant",
};
GlobalAppearance::GlobalAppearance()

View File

@@ -93,7 +93,7 @@
<Grid.RowDefinitions>
<!-- profile name -->
<RowDefinition Height="*" />
<RowDefinition Height="20" />
<!-- author and version -->
<RowDefinition Height="*" />
</Grid.RowDefinitions>
@@ -109,20 +109,25 @@
<TextBlock Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Height="20"
AutomationProperties.AccessibilityView="Raw"
Text="{x:Bind Name}" />
<TextBlock Grid.Row="1"
Grid.Column="1"
Height="20"
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind Author}" />
Text="{x:Bind Author}"
Visibility="{x:Bind local:Converters.StringNotEmptyToVisibility(Author)}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Height="20"
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind Version}" />
Text="{x:Bind Version}"
Visibility="{x:Bind local:Converters.StringNotEmptyToVisibility(Version)}" />
</Grid>
</DataTemplate>

View File

@@ -594,7 +594,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// navigate to the profile next to this one
const auto newSelectedItem{ menuItems.GetAt(index < menuItems.Size() - 1 ? index : index - 1) };
SettingsNav().SelectedItem(newSelectedItem);
_Navigate(newSelectedItem.try_as<MUX::Controls::NavigationViewItem>().Tag().try_as<Editor::ProfileViewModel>(), BreadcrumbSubPage::None, true);
const auto newTag = newSelectedItem.as<MUX::Controls::NavigationViewItem>().Tag();
if (const auto profileViewModel = newTag.try_as<ProfileViewModel>())
{
_Navigate(*profileViewModel, BreadcrumbSubPage::None);
}
else
{
_Navigate(newTag.as<hstring>(), BreadcrumbSubPage::None);
}
}
IObservableVector<IInspectable> MainPage::Breadcrumbs() noexcept

View File

@@ -9,13 +9,43 @@
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Background="{ThemeResource SettingsPageBackground}"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml" />
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<!--
These two values are "SolidBackgroundFillColorTertiary".
We can't just put those here though as
StaticResources, because XAML will evaluate their
values when the App is first loaded, and we'll
always use the value from the OS theme, regardless
of the requested theme. Kinda the same thing we've
had to do with TabViewBackground in the past.
-->
<ResourceDictionary x:Key="Dark">
<Color x:Key="SettingsPageBackground">#282828</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="SettingsPageBackground">#F9F9F9</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- Define resources for HighContrast mode here -->
<StaticResource x:Key="SettingsPageBackground"
ResourceKey="SystemColorWindowColorBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground"
@@ -30,7 +60,7 @@
</Page.Resources>
<muxc:NavigationView x:Name="SettingsNav"
Background="Transparent"
Background="{ThemeResource SettingsPageBackground}"
IsBackButtonVisible="Collapsed"
IsSettingsVisible="False"
ItemInvoked="SettingsNav_ItemInvoked"
@@ -149,7 +179,7 @@
</Frame>
<!-- Explicitly set the background color on grid to prevent the navigation animation from overflowing it -->
<Grid Grid.Row="1"
Height="80"
Height="55"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,1,0,0">
<Grid.ColumnDefinitions>
@@ -166,14 +196,14 @@
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button x:Name="ResetButton"
x:Uid="Settings_ResetSettingsButton"
Click="ResetButton_Click" />
<Button x:Name="SaveButton"
x:Uid="Settings_SaveSettingsButton"
Margin="10,0,0,0"
Click="SaveButton_Click"
Style="{StaticResource AccentButtonStyle}" />
<Button x:Name="ResetButton"
x:Uid="Settings_ResetSettingsButton"
Margin="10,0,0,0"
Click="ResetButton_Click" />
</StackPanel>
</Grid>
</Grid>

View File

@@ -8,11 +8,10 @@
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::winrt::Windows::Foundation;
static constexpr std::wstring_view PreviewText{ L"Windows Terminal\r\nCopyright (c) Microsoft Corporation\r\n\nC:\\Windows\\Terminal> " };
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
PreviewConnection::PreviewConnection() noexcept
PreviewConnection::PreviewConnection(const winrt::hstring previewString) noexcept :
_previewString{ previewString }
{
}
@@ -21,7 +20,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// First send a sequence to disable cursor blinking
_TerminalOutputHandlers(L"\x1b[?12l");
// Send the preview text
_TerminalOutputHandlers(PreviewText);
_TerminalOutputHandlers(_previewString);
}
void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept

View File

@@ -19,7 +19,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
class PreviewConnection : public winrt::implements<PreviewConnection, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection>
{
public:
PreviewConnection() noexcept;
PreviewConnection(const winrt::hstring previewString) noexcept;
void Initialize(const Windows::Foundation::Collections::ValueSet& settings) noexcept;
void Start() noexcept;
@@ -31,5 +31,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler);
TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, IInspectable);
private:
const winrt::hstring _previewString;
};
}

View File

@@ -14,10 +14,12 @@
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Navigation;
static const winrt::hstring PreviewText{ L"Windows Terminal\r\nCopyright (c) Microsoft Corporation\r\n\nC:\\Windows\\Terminal> " };
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Profiles_Appearance::Profiles_Appearance() :
_previewControl{ Control::TermControl(Model::TerminalSettings{}, nullptr, make<PreviewConnection>()) }
_previewControl{ Control::TermControl(Model::TerminalSettings{}, nullptr, make<PreviewConnection>(PreviewText)) }
{
InitializeComponent();

View File

@@ -39,6 +39,10 @@ static constexpr std::string_view ScrollUpKey{ "scrollUp" };
static constexpr std::string_view ScrollUpPageKey{ "scrollUpPage" };
static constexpr std::string_view ScrollToTopKey{ "scrollToTop" };
static constexpr std::string_view ScrollToBottomKey{ "scrollToBottom" };
static constexpr std::string_view ScrollToMarkKey{ "scrollToMark" };
static constexpr std::string_view AddMarkKey{ "addMark" };
static constexpr std::string_view ClearMarkKey{ "clearMark" };
static constexpr std::string_view ClearAllMarksKey{ "clearAllMarks" };
static constexpr std::string_view SendInputKey{ "sendInput" };
static constexpr std::string_view SetColorSchemeKey{ "setColorScheme" };
static constexpr std::string_view SetTabColorKey{ "setTabColor" };
@@ -76,6 +80,8 @@ static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
static constexpr std::string_view SelectAllKey{ "selectAll" };
static constexpr std::string_view MarkModeKey{ "markMode" };
static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" };
static constexpr std::string_view ActionKey{ "action" };
@@ -352,6 +358,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::ScrollUpPage, RS_(L"ScrollUpPageCommandKey") },
{ ShortcutAction::ScrollToTop, RS_(L"ScrollToTopCommandKey") },
{ ShortcutAction::ScrollToBottom, RS_(L"ScrollToBottomCommandKey") },
{ ShortcutAction::ScrollToMark, RS_(L"ScrollToPreviousMarkCommandKey") },
{ ShortcutAction::AddMark, RS_(L"AddMarkCommandKey") },
{ ShortcutAction::ClearMark, RS_(L"ClearMarkCommandKey") },
{ ShortcutAction::ClearAllMarks, RS_(L"ClearAllMarksCommandKey") },
{ ShortcutAction::SendInput, L"" },
{ ShortcutAction::SetColorScheme, L"" },
{ ShortcutAction::SetTabColor, RS_(L"ResetTabColorCommandKey") },
@@ -388,6 +398,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::AdjustOpacity, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
{ ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
{ ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
{ ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") },
};
}();

View File

@@ -29,6 +29,8 @@
#include "CloseTabsAfterArgs.g.cpp"
#include "CloseTabArgs.g.cpp"
#include "MoveTabArgs.g.cpp"
#include "ScrollToMarkArgs.g.cpp"
#include "AddMarkArgs.g.cpp"
#include "FindMatchArgs.g.cpp"
#include "ToggleCommandPaletteArgs.g.cpp"
#include "NewWindowArgs.g.cpp"
@@ -611,6 +613,38 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return RS_(L"ScrollDownCommandKey");
}
winrt::hstring ScrollToMarkArgs::GenerateName() const
{
switch (Direction())
{
case Microsoft::Terminal::Control::ScrollToMarkDirection::Last:
return winrt::hstring{ RS_(L"ScrollToLastMarkCommandKey") };
case Microsoft::Terminal::Control::ScrollToMarkDirection::First:
return winrt::hstring{ RS_(L"ScrollToFirstMarkCommandKey") };
case Microsoft::Terminal::Control::ScrollToMarkDirection::Next:
return winrt::hstring{ RS_(L"ScrollToNextMarkCommandKey") };
case Microsoft::Terminal::Control::ScrollToMarkDirection::Previous:
default:
return winrt::hstring{ RS_(L"ScrollToPreviousMarkCommandKey") };
}
return winrt::hstring{ RS_(L"ScrollToPreviousMarkCommandKey") };
}
winrt::hstring AddMarkArgs::GenerateName() const
{
if (Color())
{
return winrt::hstring{
fmt::format(std::wstring_view(RS_(L"AddMarkWithColorCommandKey")),
til::color{ Color().Value() }.ToHexString(true))
};
}
else
{
return RS_(L"AddMarkCommandKey");
}
}
winrt::hstring MoveTabArgs::GenerateName() const
{
winrt::hstring directionString;

View File

@@ -30,6 +30,8 @@
#include "CloseTabArgs.g.h"
#include "ScrollUpArgs.g.h"
#include "ScrollDownArgs.g.h"
#include "ScrollToMarkArgs.g.h"
#include "AddMarkArgs.g.h"
#include "MoveTabArgs.g.h"
#include "ToggleCommandPaletteArgs.g.h"
#include "FindMatchArgs.g.h"
@@ -180,6 +182,14 @@ private:
#define SCROLL_DOWN_ARGS(X) \
X(Windows::Foundation::IReference<uint32_t>, RowsToScroll, "rowsToScroll", false, nullptr)
////////////////////////////////////////////////////////////////////////////////
#define SCROLL_TO_MARK_ARGS(X) \
X(Microsoft::Terminal::Control::ScrollToMarkDirection, Direction, "direction", false, Microsoft::Terminal::Control::ScrollToMarkDirection::Previous)
////////////////////////////////////////////////////////////////////////////////
#define ADD_MARK_ARGS(X) \
X(Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>, Color, "color", false, nullptr)
////////////////////////////////////////////////////////////////////////////////
#define TOGGLE_COMMAND_PALETTE_ARGS(X) \
X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action)
@@ -612,6 +622,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(ScrollDownArgs, SCROLL_DOWN_ARGS);
ACTION_ARGS_STRUCT(ScrollToMarkArgs, SCROLL_TO_MARK_ARGS);
ACTION_ARGS_STRUCT(AddMarkArgs, ADD_MARK_ARGS);
ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS);
ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS);

View File

@@ -290,6 +290,17 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.IReference<UInt32> RowsToScroll { get; };
};
[default_interface] runtimeclass ScrollToMarkArgs : IActionArgs
{
Microsoft.Terminal.Control.ScrollToMarkDirection Direction { get; };
};
[default_interface] runtimeclass AddMarkArgs : IActionArgs
{
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> Color { get; };
};
[default_interface] runtimeclass ToggleCommandPaletteArgs : IActionArgs
{
CommandPaletteLaunchMode LaunchMode { get; };

View File

@@ -47,6 +47,10 @@
ON_ALL_ACTIONS(ScrollDownPage) \
ON_ALL_ACTIONS(ScrollToTop) \
ON_ALL_ACTIONS(ScrollToBottom) \
ON_ALL_ACTIONS(ScrollToMark) \
ON_ALL_ACTIONS(AddMark) \
ON_ALL_ACTIONS(ClearMark) \
ON_ALL_ACTIONS(ClearAllMarks) \
ON_ALL_ACTIONS(ResizePane) \
ON_ALL_ACTIONS(MoveFocus) \
ON_ALL_ACTIONS(MovePane) \
@@ -89,7 +93,9 @@
ON_ALL_ACTIONS(Quit) \
ON_ALL_ACTIONS(AdjustOpacity) \
ON_ALL_ACTIONS(RestoreLastClosed) \
ON_ALL_ACTIONS(SelectAll)
ON_ALL_ACTIONS(SelectAll) \
ON_ALL_ACTIONS(MarkMode) \
ON_ALL_ACTIONS(ToggleBlockSelection)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
@@ -117,6 +123,8 @@
ON_ALL_ACTIONS_WITH_ARGS(ResizePane) \
ON_ALL_ACTIONS_WITH_ARGS(ScrollDown) \
ON_ALL_ACTIONS_WITH_ARGS(ScrollUp) \
ON_ALL_ACTIONS_WITH_ARGS(ScrollToMark) \
ON_ALL_ACTIONS_WITH_ARGS(AddMark) \
ON_ALL_ACTIONS_WITH_ARGS(SendInput) \
ON_ALL_ACTIONS_WITH_ARGS(SetColorScheme) \
ON_ALL_ACTIONS_WITH_ARGS(SetTabColor) \

View File

@@ -14,6 +14,7 @@
#include <shellapi.h>
#include <shlwapi.h>
#include <til/latch.h>
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings;
@@ -1120,12 +1121,27 @@ void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& term
// but in the future it might be worthwhile to change the code to use list indices instead.
void CascadiaSettings::_refreshDefaultTerminals()
{
if (!_defaultTerminals)
if (_defaultTerminals)
{
auto [defaultTerminals, defaultTerminal] = DefaultTerminal::Available();
_defaultTerminals = winrt::single_threaded_observable_vector(std::move(defaultTerminals));
_currentDefaultTerminal = std::move(defaultTerminal);
return;
}
// This is an extract of extractValueFromTaskWithoutMainThreadAwait
// as DefaultTerminal::Available creates the exact same issue.
std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> result{ {}, nullptr };
til::latch latch{ 1 };
std::ignore = [&]() -> winrt::fire_and_forget {
const auto cleanup = wil::scope_exit([&]() {
latch.count_down();
});
co_await winrt::resume_background();
result = DefaultTerminal::Available();
}();
latch.wait();
_defaultTerminals = winrt::single_threaded_observable_vector(std::move(result.first));
_currentDefaultTerminal = std::move(result.second);
}
void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)

View File

@@ -144,6 +144,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
private:
static const std::filesystem::path& _settingsPath();
static const std::filesystem::path& _releaseSettingsPath();
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const;

View File

@@ -61,9 +61,11 @@ static auto extractValueFromTaskWithoutMainThreadAwait(TTask&& task) -> decltype
til::latch latch{ 1 };
const auto _ = [&]() -> winrt::fire_and_forget {
const auto cleanup = wil::scope_exit([&]() {
latch.count_down();
});
co_await winrt::resume_background();
finalVal.emplace(co_await task);
latch.count_down();
}();
latch.wait();
@@ -766,8 +768,28 @@ void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator
Model::CascadiaSettings CascadiaSettings::LoadAll()
try
{
const auto settingsString = ReadUTF8FileIfExists(_settingsPath()).value_or(std::string{});
const auto firstTimeSetup = settingsString.empty();
auto settingsString = ReadUTF8FileIfExists(_settingsPath()).value_or(std::string{});
auto firstTimeSetup = settingsString.empty();
// If it's the firstTimeSetup and a preview build, then try to
// read settings.json from the Release stable file path if it exists.
// Otherwise use default settings file provided from original settings file
bool releaseSettingExists = false;
if (firstTimeSetup)
{
#if defined(WT_BRANDING_PREVIEW)
{
try
{
settingsString = ReadUTF8FileIfExists(_releaseSettingsPath()).value_or(std::string{});
releaseSettingExists = settingsString.empty() ? false : true;
}
catch (...)
{
}
}
#endif
}
// GH#11119: If we find that the settings file doesn't exist, or is empty,
// then let's quick delete the state file as well. If the user does have a
@@ -780,7 +802,9 @@ try
ApplicationState::SharedInstance().Reset();
}
const auto settingsStringView = firstTimeSetup ? UserSettingsJson : settingsString;
// Only uses default settings when firstTimeSetup is true and releaseSettingExists is false
// Otherwise use existing settingsString
const auto settingsStringView = (firstTimeSetup && !releaseSettingExists) ? UserSettingsJson : settingsString;
auto mustWriteToDisk = firstTimeSetup;
SettingsLoader loader{ settingsStringView, DefaultJson };
@@ -791,7 +815,8 @@ try
// ApplyRuntimeInitialSettings depends on generated profiles.
// --> ApplyRuntimeInitialSettings must be called after GenerateProfiles.
if (firstTimeSetup)
// Doesn't run when there is a Release settings.json that exists
if (firstTimeSetup && !releaseSettingExists)
{
loader.ApplyRuntimeInitialSettings();
}
@@ -953,6 +978,18 @@ const std::filesystem::path& CascadiaSettings::_settingsPath()
return path;
}
// Method Description:
// - Returns the path of the settings.json file from stable file path
// Arguments:
// - <none>
// Return Value:
// - Path to stable settings
const std::filesystem::path& CascadiaSettings::_releaseSettingsPath()
{
static const auto path = GetReleaseSettingsPath() / SettingsFilename;
return path;
}
// function Description:
// - Returns the full path to the settings file, either within the application
// package, or in its unpackaged location. This path is under the "Local

View File

@@ -21,24 +21,30 @@ static constexpr std::string_view BackgroundKey{ "background" };
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
static constexpr std::string_view CursorColorKey{ "cursorColor" };
static constexpr std::array<std::string_view, 16> TableColors = {
"black",
"red",
"green",
"yellow",
"blue",
"purple",
"cyan",
"white",
"brightBlack",
"brightRed",
"brightGreen",
"brightYellow",
"brightBlue",
"brightPurple",
"brightCyan",
"brightWhite"
};
static constexpr size_t ColorSchemeExpectedSize = 16;
static constexpr std::array<std::pair<std::string_view, size_t>, 18> TableColorsMapping{ {
// Primary color mappings
{ "black", 0 },
{ "red", 1 },
{ "green", 2 },
{ "yellow", 3 },
{ "blue", 4 },
{ "purple", 5 },
{ "cyan", 6 },
{ "white", 7 },
{ "brightBlack", 8 },
{ "brightRed", 9 },
{ "brightGreen", 10 },
{ "brightYellow", 11 },
{ "brightBlue", 12 },
{ "brightPurple", 13 },
{ "brightCyan", 14 },
{ "brightWhite", 15 },
// Alternate color mappings (GH#11456)
{ "magenta", 5 },
{ "brightMagenta", 13 },
} };
ColorScheme::ColorScheme() noexcept :
ColorScheme{ winrt::hstring{} }
@@ -98,11 +104,18 @@ bool ColorScheme::_layerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor);
// Required fields
for (unsigned int i = 0; i < TableColors.size(); ++i)
size_t colorCount = 0;
for (const auto& [key, index] : TableColorsMapping)
{
isValid &= JsonUtils::GetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
colorCount += JsonUtils::GetValueForKey(json, key, til::at(_table, index));
if (colorCount == ColorSchemeExpectedSize)
{
break;
}
}
isValid &= (colorCount == 16); // Valid schemes should have exactly 16 colors
return isValid;
}
@@ -122,9 +135,10 @@ Json::Value ColorScheme::ToJson() const
JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor);
for (unsigned int i = 0; i < TableColors.size(); ++i)
for (size_t i = 0; i < ColorSchemeExpectedSize; ++i)
{
JsonUtils::SetValueForKey(json, til::at(TableColors, i), til::at(_table, i));
const auto& key = til::at(TableColorsMapping, i).first;
JsonUtils::SetValueForKey(json, key, til::at(_table, i));
}
return json;

View File

@@ -17,13 +17,21 @@ DefaultTerminal::DefaultTerminal(DelegationConfig::DelegationPackage&& pkg) :
winrt::hstring DefaultTerminal::Name() const
{
return _pkg.terminal.name.empty() ? winrt::hstring{ RS_(L"InboxWindowsConsoleName") } : winrt::hstring{ _pkg.terminal.name };
switch (_pkg.pair.kind)
{
case DelegationConfig::DelegationPairKind::Default:
return RS_(L"DefaultWindowsConsoleName");
case DelegationConfig::DelegationPairKind::Conhost:
return RS_(L"InboxWindowsConsoleName");
default:
return winrt::hstring{ _pkg.info.name };
}
}
winrt::hstring DefaultTerminal::Version() const
{
// If there's no version information... return empty string instead.
const auto& version = _pkg.terminal.version;
const auto& version = _pkg.info.version;
if (DelegationConfig::PkgVersion{} == version)
{
return winrt::hstring{};
@@ -36,12 +44,20 @@ winrt::hstring DefaultTerminal::Version() const
winrt::hstring DefaultTerminal::Author() const
{
return _pkg.terminal.author.empty() ? winrt::hstring{ RS_(L"InboxWindowsConsoleAuthor") } : winrt::hstring{ _pkg.terminal.author };
switch (_pkg.pair.kind)
{
case DelegationConfig::DelegationPairKind::Default:
return {}; // The "Let Windows decide" option has no author.
case DelegationConfig::DelegationPairKind::Conhost:
return RS_(L"InboxWindowsConsoleAuthor");
default:
return winrt::hstring{ _pkg.info.author };
}
}
winrt::hstring DefaultTerminal::Icon() const
{
return _pkg.terminal.logo.empty() ? winrt::hstring{ L"\uE756" } : winrt::hstring{ _pkg.terminal.logo };
return _pkg.info.logo.empty() ? winrt::hstring{ L"\uE756" } : winrt::hstring{ _pkg.info.logo };
}
std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> DefaultTerminal::Available()
@@ -76,11 +92,9 @@ std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> DefaultTe
bool DefaultTerminal::HasCurrent()
{
std::vector<DelegationConfig::DelegationPackage> allPackages;
DelegationConfig::DelegationPackage currentPackage;
DelegationConfig::DelegationPackage currentPackage{ DelegationConfig::DefaultDelegationPair };
LOG_IF_FAILED(DelegationConfig::s_GetAvailablePackages(allPackages, currentPackage));
// Good old conhost has a hardcoded GUID of {00000000-0000-0000-0000-000000000000}.
return currentPackage.terminal.clsid != CLSID{};
return !currentPackage.pair.IsDefault();
}
void DefaultTerminal::Current(const Model::DefaultTerminal& term)

View File

@@ -14,6 +14,7 @@
static constexpr std::string_view Utf8Bom{ "\xEF\xBB\xBF", 3 };
static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
static constexpr std::wstring_view ReleaseSettingsFolder{ L"Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\" };
namespace winrt::Microsoft::Terminal::Settings::Model
{
@@ -43,6 +44,32 @@ namespace winrt::Microsoft::Terminal::Settings::Model
return baseSettingsPath;
}
// Returns a path like C:\Users\<username>\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState
// to the path of the stable release settings
std::filesystem::path GetReleaseSettingsPath()
{
static std::filesystem::path baseSettingsPath = []() {
wil::unique_cotaskmem_string localAppDataFolder;
// We're using KF_FLAG_NO_PACKAGE_REDIRECTION to ensure that we always get the
// user's actual local AppData directory.
THROW_IF_FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_PACKAGE_REDIRECTION, nullptr, &localAppDataFolder));
// Returns a path like C:\Users\<username>\AppData\Local
std::filesystem::path parentDirectoryForSettingsFile{ localAppDataFolder.get() };
//Appending \Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState to the settings path
parentDirectoryForSettingsFile /= ReleaseSettingsFolder;
if (!IsPackaged())
{
parentDirectoryForSettingsFile /= UnpackagedSettingsFolderName;
}
return parentDirectoryForSettingsFile;
}();
return baseSettingsPath;
}
// Function Description:
// - Checks the permissions on this file, to make sure it can only be opened
// for writing by admins. We will be checking to see if the file is owned

View File

@@ -4,6 +4,7 @@
namespace winrt::Microsoft::Terminal::Settings::Model
{
std::filesystem::path GetBaseSettingsPath();
std::filesystem::path GetReleaseSettingsPath();
std::string ReadUTF8File(const std::filesystem::path& path, const bool elevatedOnly = false);
std::optional<std::string> ReadUTF8FileIfExists(const std::filesystem::path& path, const bool elevatedOnly = false);
void WriteUTF8File(const std::filesystem::path& path, const std::string_view& content, const bool elevatedOnly = false);

Some files were not shown because too many files have changed in this diff Show More