Compare commits

..

104 Commits

Author SHA1 Message Date
Leonard Hecker
d6707d2444 Update vsconfig 2022-11-30 23:05:43 +01:00
Mike Griese
f2eed92345 Fix a couple issues with experimental.useBackgroundImageForWindow (#14456)
This fixes two issues with `experimental.useBackgroundImageForWindow` I discovered while looking at #14260 

* It looks like the opacity of the whole-window BG image wouldn't hot reload if the path didn't. 
* > set useBGForWindow:true, focus a pane with an image, then set it to useBGForWindow:false, and observe a pane with <100 opacity. You'll be able to see the BG image left behind!

These are pretty easy to miss, so I can see how it happened. 

I don't think this _technically_ closes that thread, though. Ultimately, I think OP's settings were just wrong (and possible didn't hot-reload). There's another, trickier bit I'm discussing in that thread, that might deserve its own separate follow-up for discussion.
2022-11-29 23:09:03 +00:00
Leonard Hecker
b6b1ff8b2c Wait for clients to exit on ConPTY shutdown (#14282)
#14160 didn't fix #14132 entirely. There seems to be a race condition left
where (on my system) 9 out of 10 times everything works correctly,
but sometimes OpenConsole exits, while pwsh and bash keep running.

My leading theory is that the new code is exiting OpenConsole faster than the
old code. This prevents clients from calling console APIs, etc. causing them
to get stuck. The old code (and new code) calls `ExitProcess` when the ConPTY
pipes break and I think this is wrong: In conhost when you close the window we
only call `CloseConsoleProcessState` via the `WM_CLOSE` event and that's it.
Solution: Remove the call to `RundownAndExit` for ConPTY.

During testing I found that continuously printing text inside msys2 will cause
child processes to only exit slowly one by one every 5 seconds.
This happens because `CloseConsoleProcessState` calls `HandleCtrlEvent` without
holding the console lock. This creates a race condition where most of the time
the console IO thread is the one picking up the `CTRL_CLOSE_EVENT`. But that's
problematic because the `CTRL_CLOSE_EVENT` leads to a `ConsoleControl` call of
type `ConsoleEndTask` which calls back into conhost's IO thread and
so you got the IO thread waiting on itself to respond.
Solution: Don't race conditions.

## Validation Steps Performed
* `Enter-VsDevShell` and close the tab
  Everything exits after 5s 
* Run msys2 bash from within pwsh and close the tab
  Everything exits instantly 
* Run `cat bigfile.txt` and close the tab
  Everything exits instantly 
* Patch `conhost.exe` with `sfpcopy`, as well as `KernelBase.dll`
  with the recent changes to `winconpty`, then launch and exit
  shells and applications via VS Code's terminal 
* On the main branch without this modification remove the call to
  `TriggerTeardown` in `RundownAndExit` (this speeds up the shutdown).
  Run (msys2's) `bash.exe --login` and hold enter and then press Ctrl+Shift+W
  simultaneously. The tab should close and randomly OpenConsole should exit
  early while pwsh/bash keep running. Then retry this with this branch and
  observe how the child processes don't stick around forever anymore. 
2022-11-29 21:15:45 +01:00
Josh Soref
a7ab17571b Update to check-spelling v0.0.21 (#14455)
Upgrades check-spelling to v0.0.21

The command to apply changes should now work on Windows (it requires
Perl, but I believe that's more or less present most of the time, and it
should walk you through the rest of the required tools).

There are a bunch of new features, the most important here are probably
being able to update the metadata from Windows. (If it doesn't work,
please @ me).

Also, candidate.patterns will automatically suggest patterns. You can
see them in patterns.txt, e.g.:

```
# Automatically suggested patterns
# hit-count: 3831 file-count: 582
# IServiceProvider
\bI(?=(?:[A-Z][a-z]{2,})+\b)
```

The metadata bits (the hit count/file count) don't have to be retained
(I hope they'll be useful in deciding whether/or not to add a pattern,
i.e. "how applicable is it?"), the comment hinting at what the pattern
does is probably worth retaining.

We've been using more or less this version for a while internally
(including talk-to-bot, and, I do have a pattern that could be used to
let people use that in forks, but, I'm going to skip that for now).

This weekend, I did some cleanup for `act` (to run check-spelling
locally), and some minor polish.

You can see the runs I made in
https://github.com/check-spelling/terminal/actions
2022-11-28 13:35:07 -06:00
Leonard Hecker
8f346a7158 Rewrite Utf16Parser (#14417)
This commit replaces `Utf16Parser` with `<til/unicode.h>` which includes:
* `til::utf16_iterator` as a replacement for `Utf16Parser::Parse`
* `til::utf16_next` as a replacement for `Utf16Parser::ParseNext`

This fixes 2 bugs with `Utf16Parser`:
* Swallowing invalid surrogate pairs instead of turning them into U+FFFD.
* `std::vector<std::vector<wchar_t>>`. It's now >12000% faster.

## Validation Steps Performed
* New unit tests pass 
* Searching for narrow/wide characters in conhost works 
2022-11-23 21:13:36 +00:00
Junyoung Lee
437b5ac595 Add the setting "confirmCloseAllTabs" to SUI (#14419)
This commit adds the setting "confirmCloseAllTabs" to SUI.
The setting was added to the Interactions pane of Global Settings.

Closes #14413
Closes #14033
2022-11-21 22:03:56 +00:00
Ben Constable
feed768b3f [schema] Update allowed types for startingDirectory (#14408)
Update the schema to support null.

## PR Checklist
* [x] Closes #14299
* [x] Schema updated.
2022-11-21 16:02:24 -05:00
Mike Griese
937cadcad0 Spec for "matching" profiles in "New Tab Customization" (#12584)
Doc updated in response to some discussion in [#11326] and
[#7774]. In those PRs, it became clear that there needs to be a simple way of
collecting up a whole group of profiles automatically for sorting in these
menus. Although discussion centered on how hard it would be for extensions to
provide that customization themselves, the `match` statement was added as a way
to allow the user to easily filter those profiles themselves.

This was something we had originally considered as a "future consideration", but
ultimately deemed it to be out of scope for the initial spec review.

References:

* #1571
* #11326
* #7774
2022-11-18 14:13:15 -06:00
Dustin L. Howett
9aee510ce0 Add .git-blame-ignore-revs to make GitHub's blame view nicer (#14394)
Commits mentioned in this file will be acknowledged by GitHub, but
skipped in the blame view. Other tools use this as well.

You can make git use it by passing
`--ignore-revs-file .git-blame-ignore-revs` to `git blame`.

The only commits we're ignoring right now are codebase-wide reformatting
or line endings changes.
2022-11-15 18:49:57 -06:00
Dan Moseley
c9aeea1fdc Update bug template to help find the version number accurately (#14375)
Due to https://github.com/microsoft/terminal/issues/14335 using `wt -v` is not recommended. BTW, it might be nice if Ctrl-Shift-P and typing "about" or "version" gave the version, too.
2022-11-14 22:00:11 +00:00
Ian O'Neill
6b4b63b18a Ensure reading the buffer content actually returns the content (#14379)
## Summary of the Pull Request
Ensures that reading the buffer content actually returns the content.

## References
Regressed in #13626.

## PR Checklist
* [x] Closes #14378
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed

## Validation Steps Performed
Added a test.
2022-11-12 23:31:10 +00:00
Leonard Hecker
a01500f051 Rewrite ROW to be Unicode capable (#13626)
This commit is a from-scratch rewrite of `ROW` with the primary goal to get
rid of the rather bodgy `UnicodeStorage` class and improve Unicode support.

Previously a 120x9001 terminal buffer would store a vector of 9001 `ROW`s
where each `ROW` stored exactly 120 `wchar_t`. Glyphs exceeding their
allocated space would be stored in the `UnicodeStorage` which was basically
a `hashmap<Coordinate, String>`. Iterating over the text in a `ROW` would
require us to check each glyph and fetch it from the map conditionally.
On newlines we'd have to invalidate all map entries that are now gone,
so for every invalidated `ROW` we'd iterate through all glyphs again and if
a single one was stored in `UnicodeStorage`, we'd then iterate through the
entire hashmap to remove all coordinates that were residing on that `ROW`.
All in all, this wasn't the most robust nor performant code.

The new implementation is simple (from a design perspective):
Store all text in a `ROW` in a regular string. Grow the string if needed.
The association between columns and text works by storing character offsets
in a column-wide array. This algorithm is <100 LOC and removes ~1000.

As an aside this PR does a few more things that go hand in hand:
* Remove most of `ROW` helper classes, which aren't needed anymore.
* Allocate backing memory in a single `VirtualAlloc` call.
* Rewrite `IsCursorDoubleWidth` to use `DbcsAttrAt` directly.
  Improves overall performance by 10-20% and makes this implementation
  faster than the previous NxM storage, despite the added complexity.

Part of #8000

## Validation Steps Performed
* Existing and new unit and feature tests complete 
* Printing Unicode completes without crashing 
* Resizing works without crashing 
2022-11-11 20:34:58 +01:00
Leonard Hecker
c12dc2aa4d Fix VtIoTests for debug builds (#14358)
1774cfd added a debug assertion in `CreateIoHandlers` which broke this test.
2022-11-10 15:58:10 +00:00
James Holderness
88c3ef68a5 Add support for the rectangular area operations (#14285)
## Summary of the Pull Request

This PR adds support for the rectangular area escape sequences:
`DECCRA`, `DECFRA`, `DECERA`, `DECSERA`, `DECCARA`, `DECRARA`, and
`DECSACE`. They provide VT applications with an efficient way to copy,
fill, erase, or change the attributes in a rectangular area of the
screen.

## PR Checklist
* [x] Closes #14112
* [x] CLA signed.
* [x] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. Issue number
where discussion took place: #14112

## Detailed Description of the Pull Request / Additional comments

All of these operations take a rectangle, defined by four coordinates.
These need to have defaults applied, potentially need to be clipped
and/or clamped within the active margins, and finally converted to
absolute buffer coordinates. To avoid having to repeat that boilerplate
code everywhere, I've pulled that functionality out into a shared method
which they all use.

With that out of the way, operations like `DECFRA` (fill), `DECERA`
(erase), and `DECSERA` (selective erase) are fairly simple. They're just
filling the given rectangle using the existing methods `_FillRect` and
`_SelectiveEraseRect`. `DECCRA` (copy) is a little more work, because we
didn't have existing code for that in `AdaptDispatch`, but it's mostly
just cloned from the conhost `_CopyRectangle` function.

The `DECCARA` (change attributes) and `DECRARA` (reverse attributes)
operations are different though. Their coordinates can be interpreted as
either a rectangle, or a stream of character positions (determined by
the `DECSACE` escape sequence), and they both deal with attribute
manipulation of the target area. So again I've pulled out that common
functionality into some shared methods.

They both also take a list of `SGR` options which define the attribute
changes that they need to apply to the target area. To parse that data,
I've had to refactor the `SGR` decoder from the `SetGraphicsRendition`
method so it could be used with a given `TextAttribute` instance instead
of just modifying the active attributes.

The way that works in `DECCARA`, we apply the `SGR` options to two
`TextAttribute` instances - one with all rendition bits on, and one with
all off - producing a pair of bit masks. Then by `AND`ing the target
attributes with the first bit mask, and `OR`ing them with the second, we
can efficiently achieve the same effect as if we'd applied each `SGR`
option to our target cells one by one.

In the case of `DECRARA`, we only need to create a single bit mask to
achieve the "reverse attribute" effect. That bit mask is applied to the
target cells with an `XOR` operation.

## Validation Steps Performed

Thanks to @KalleOlaviNiemitalo, we've been able to run a series of tests
on a real VT420, so we have a good idea of how these ops are intended to
work. Our implementation does a reasonably good job of matching that
behavior, but we don't yet support paging, so we don't have the `DECCRA`
ability to copy between pages, and we also don't have the concept of
"unoccupied" cells, so we can't support that aspect of the streaming
operations.

It's also worth mentioning that the VT420 doesn't have colors, so we
can't be sure exactly how they are meant to interpreted. However, based
on the way the other attribute are handled, and what we know from the
DEC STD 070 documentation, I think it's fair to assume that our handling
of colors is also reasonable.
2022-11-10 15:18:13 +00:00
James Holderness
1b09ae3b95 Prevent conhost scrollbar overlapping content (#14329)
Prior to this PR, the conhost vertical scrollbar would be forced to be
visible whenever the "Disable Scroll-Forward" option was set. It was
assumed that it would be needed as soon as the current viewport was
filled, so it was better to start off visible and disabled.

When the viewport height and buffer height are the same, though, the
scrollbar is never needed, and conhost compensates for that by making
the window narrower. But since we were still forcing the scrollbar to be
visible, that would result in it overlapping content in the rightmost
columns.

This PR attempts to fix that issue by simply leaving the scrollbar to
decide the visibility itself. This is perhaps not as aesthetically
pleasing when it starts off hidden and then later becomes visible, but
that seems better than having it overlap the content.

I've manually confirmed this fixes the problem reported in issue #2449.

Closes #2449
2022-11-10 15:17:26 +00:00
PankajBhojwani
7aa7ce2bce Remove constexpr from _altBufferMarks (#14360)
## Summary of the Pull Request
Compiler was raising the error: `expression did not evaluate to a constant.`, causing the solution to fail when building. Removing this `constexpr` fixes it.

## PR Checklist
* [ ] Closes #xxx
* [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've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Validation Steps Performed
Solution builds now
2022-11-09 21:21:19 +00:00
PankajBhojwani
0f81deb7c0 Clean up velocity for Feature_AdjustIndistinguishableText in Terminal (#13972)
Changes the velocity flag for AdjustIndistinguishableText to only be disabled for conhost
Closes #13508
2022-11-09 00:36:40 +00:00
Leonard Hecker
48325f9aca Re-enable AuditMode for TerminalCore (#14344)
AuditMode was accidentally disabled in 1c6aa4d, around 2 years ago.
This should fix this issue and address all the warnings it now generates.

Related to #14129.
2022-11-08 19:06:58 +00:00
Leonard Hecker
8695d9ee4b Fix Helix failures by updating the SDK (#14353)
This mirrors microsoft/WinUI !8045797 and appears to resolve our recent Helix
pipeline failures, due to a python script issue (see Helix logs).
2022-11-08 18:26:43 +00:00
Leonard Hecker
a798a603e1 Initialize all members of Terminal (#14345)
The following members were not initialized during construction:
* `CursorType _defaultCursorShape`
* `bool _suppressApplicationTitle`
* `bool _bracketedPasteMode`
* `size_t _hyperlinkPatternId`
* `SelectionExpansion _multiClickSelectionMode`
* `til::CoordType _scrollbackLines`

Unlike gcc and clang, MSVC is fairly tame when it comes to removing code
tainted by undefined behavior, so the most likely affect this had is that
we were reading uninitialized memory.

Related to #14129.
2022-11-07 23:16:03 +00:00
Carlos Zamora
62b34cf6f7 Apply AutoProps to TextBox settings in SUI (#14178)
We already were setting the automation properties on the expander, however, we were not setting it on the content when an expander was present. This change applies the automation properties to both the expander and the child content (i.e. TextBox).

Closes #13827
2022-11-04 19:13:59 +00:00
Mike Griese
f25d258a43 Clarify which wt should be run
As noted in #14335. Closes #14335
2022-11-04 13:58:37 -05:00
Dustin Howett
cfdea71dc0 Merge remote-tracking branch 'openconsole/inbox' 2022-11-02 17:56:48 -05:00
Hamza Nauman
86928bb48d Merged PR 8072712: [Git2Git] Fix conhost crash due to cmd.exe launch race
There is a condition which causes the console host process (conhost.exe)
to crash with a `FAIL_FAST` in `WriteCharsLegacy`.

**_Repro Scenario:_** Two conditions need to be met for crash to happen:

1. The `ENABLE_PROCESSED_OUTPUT` console mode needs to be disabled. This
   condition is met through the race condition, explained in the section
   below.
2. We are printing a string where there is a full width character
   (character that requires two spaces on the screen) being printed at
   the edge of the console window. That is, we have one character space
   available, and the character requires 2 spaces.

Running following script (attached to bug) causes a crash:
`for /l %%A in (0, 1, 10000) do start /B C:\test.bat`

The script `test.bat` repeatedly prints a console-width line
with a DBCS character that doesn't fit.

_**Race:**_ Normally, we get into `WriteCharsLegacy` with
`PROCESSED_OUTPUT` enabled. However, during the initialization of a new
CMD session, `cmd!ResetConsoleMode()` is called, which first sets the
output console mode to the value of `curOutputMode` (which is a static
variable initialized to 0) by calling `SetConsoleMode()`, before then
setting it to the desired output mode with processed output enabled:

```c++
void ResetConsoleMode( void )
{
    static DWORD desOutMode = ENABLE_PROCESSED_OUTPUT | /* ... */;

    SetConsoleMode(conOut, curOutputMode); // <------ sets console mode to 0, disabling processed output
    if (GetConsoleMode(conOut, &curOutputMode)) {
        if ((curOutputMode & desOutMode) != desOutMode) {
            curOutputMode |= desOutMode;

            if (!SetConsoleMode(conOut, curOutputMode) && /* ... */) // <----- enables processed output
```

If there is another instance of CMD that is producing output in between
these two `SetConsoleMode()` calls, then we may end up in
`WriteCharsLegacy` with processed output mode disabled.

This fix removes a `FAIL_FAST_IF` that checks for `PROCESSED_OUTPUT`
mode after the initial character processing loop.

Before RS5, this was an `ASSERT()`. This FAIL_FAST was added in RS5 in
PR !1794053 (which changed all `ASSERT`-likes to `FAIL_FAST_IF`.)

We believe this assertion guarded only the "processing" of Backspace,
Tab, CR and LF, and did not expect that we would get out of the
character processing loop with unprocessed glyph characters.  The
`FAIL_FAST` is redundant in this case, as the handlers for the
Backspace, Tab, CR and LF characters we are already checking for
`PROCESSED_OUTPUT`. Therefore, it is safe to remove this `FAIL_FAST`.

It turns out that we *can* exit the loop with unprocessed glyph
characters. In these cases, we don't want to `FAIL_FAST`.

# Validation
* Basic sanity testing to confirm strings are correctly being printed.
* Repro scenario script no longer crashes conhost.exe

Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 eb5d8064dc0f09d8be92efb5e6efa69983f30a3f

Related work items: MSFT-42055103
2022-11-02 17:50:00 -05:00
Leonard Hecker
bb4711de54 AtlasEngine: Fix a heap overflow bug (#14275)
`TextBuffer` is buggy and allows a `Trailing` `DbcsAttribute` to be written
into the first column. Since other code then blindly assumes that there's a
preceding `Leading` character, we'll get called with a X coordinate of -1.
This issue will be fixed by #13626 and this commit fixes it in the meantime.

Additionally fixes an unimportant crash when the window height is 0px,
because it was annoying during testing and doesn't hurt to be fixed.

## Validation Steps Performed
* Run a stress test that prints random Unicode at random positions
* Resize the window furiously at the same time
* Doesn't crash / fail-fast 
2022-11-02 03:11:59 +01:00
PankajBhojwani
23a02c5218 Reorder the color chips grid in the color schemes page (#14223)
XAML has an issue in windows 10 where `Width="*"` does not work properly
inside the `DataTemplate` for a `ListView`. Because of this, the color
scheme list view items looked very strange in windows 10 (see #14187).

Thanks to that, we thought up a few new designs for the color schemes
page and selected a new one that blends the color chips with a region
that shows the foreground and background color with the text preview.

Closes #14187
2022-11-01 17:22:46 +00:00
Rose
2119164d43 Fix C# warnings across the project, modernize slightly (#13938)
No behavioral changes, just C# modernizations.
2022-11-01 17:07:51 +00:00
Leonard Hecker
b4d37d8c70 Add recursive_ticket_lock and use it for Terminal (#13746)
My hope with this commit is to make our code more robust against accidental
recursive locking, as well as making it easier to write code with confidence,
with only a slight performance trade-off.

## Validation Steps Performed
* Playing Pac Man music through MIDI 
* Windows Terminal runs happily ever after 
2022-10-31 23:07:59 +00:00
Leonard Hecker
b4fce27203 Skip DECPS/MIDI output on Ctrl+C/Break (#14214)
Silent MIDI notes can be used to seemingly deny a user's input for long
durations (multiple minutes). This commit improves the situation by ignoring
all DECPS sequences for a second when Ctrl+C/Ctrl+Break is pressed.

Additionally it fixes a regression introduced in 666c446:
When we close a tab we need to unblock/shutdown `MidiAudio` early,
so that `ConptyConnection::Close()` can run down as fast as possible.

## Validation Steps Performed
* In pwsh in Windows Terminal 1.16 run ``while ($True) { echo "`e[3;8;3,~" }``
  * Ctrl+C doesn't do anything 
  * Closing the tab doesn't do anything 
* With these modifications in Windows Terminal:
  * Ctrl+C stops the output 
  * Closing the tab completes instantly 
* With these modifications in OpenConsole:
  * Ctrl+C stops the output 
  * Closing the window completes instantly 
2022-10-31 17:18:16 -05:00
Jeroen B
8ea3cb9972 Disable acrylic material (temporarily) when opacity is set to 100% (#14193)
If the opacity is set to 100%, the background becomes solid instead of 'fully opaque acrylic'. If the opacity is below 100% the acrylic material is re-enabled (depending on the user's settings).

## Validation Steps Performed

I updated two unit tests to reflect the change in behavior and manually tested the transition from <100% opacity to 100% opacity (and vice versa) on win11.

Steps:
1. Start with 100% opacity and acrylic material enabled.
2. Decrease opacity and observe acrylic effect.
3. Increase opacity back to 100% and disable the acrylic effect.
4. Decrease opacity and notice that acrylic effect is no longer there.

Closes #12880
2022-10-26 23:19:36 +00:00
Leonard Hecker
2b851caeed Disable /fp:fast by default (#14267)
Lately I've been a bit concerned about issues resulting from b036cab enabling
`/fp:fast` throughout the entire project. This commit reverts that change and:
* Enables `/fp:contract` which defaults to off since VS 17.0
  This re-enables FMA for floats on ARM64. Since this doesn't affect NANs, etc.
  I don't expect any issues apart from a slight change in float accuracy.
* Introduces `TIL_FAST_MATH_BEGIN` with which `/fp:fast` can be selectively
  enabled for code that benefits from it like `ColorFix.cpp`.

Without `TIL_FAST_MATH_BEGIN` `ColorFix` is about twice as slow
(which is actually very noticeable in real life).
This PR doesn't produce any noticeable performance regressions.

## Validation Steps Performed
* Patch `RenderSettings.hpp` to include `Mode::AlwaysDistinguishableColors`
* Run a color intense application in AtlasEngine and observe CPU usage
2022-10-26 18:49:02 +00:00
PankajBhojwani
aa625098ed Allow for exe/dll paths for the Icon setting (#14107)
## Summary of the Pull Request
Allow exe/dll paths for the `Icon` setting

The exe/dll icon needs to work in all the following areas:
* [x] The tab
* [x] The navigation view item in the SUI
* [x] The new tab flyout
* [x] The command palette

## PR Checklist
* [x] Closes #1504 
* [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'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
For the command palette, we had to switch to using `ContentPresenter` because `IconSourceElement` cannot take in every type of icon we need to provide

## Validation Steps Performed
Setting "%SystemRoot%\System32\shell32.dll,214" as the icon for a profile works in all the cases listed above.
2022-10-26 18:12:32 +00:00
PankajBhojwani
85ca8f556c Respect the "/max" parameter when starting console apps with defterm enabled (#14222)
## Summary of the Pull Request

- Pipe the `ShowWindow` value through to `ConptyConnection`
- When `TerminalPage` receives the new connection, it checks the `ShowWindow` value and maximizes *IF* there were no other pre-existing tabs (in glomming mode, we don't want to maximize sessions that did not ask for it)

## References
#12154 

## PR Checklist
* [ ] Closes #xxx
* [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'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
This is just a temporary solution until we change our defterm handoff process. Because of the way the process currently works, we have no way of knowing that the connection has requested the window to be maximized until after we have already started a terminal session. This means that we have to manually maximize the window upon receiving the connection, instead of having the session _start_ maximized, as it probably should. 

## Validation Steps Performed
`start /max python` with defterm enabled opens up python in a maximized WT window
2022-10-26 18:04:07 +00:00
James Holderness
afefe693df Add support for private options in DSR queries (#14290)
The original implementation of the _Device Status Report_ sequence was
only capable of handling ANSI status queries. This PR adds the ability
to respond to private DEC queries as well.

To prove it's working as intended, I've also included support for the
DEC extended cursor position report (`DECXCPR`), which is essentially
the same as the ANSI cursor position report, but with an additional
parameter indicating the page number. Until we support paging, though,
that value is just hardcoded to 1.

## References

The method for distinguishing between ANSI options and the private DEC
options is based on the updates made to the `SM`/`RM` mode sequences in
PR #8469.

## Validation Steps Performed

I've added a couple of unit tests covering the `DECXCPR` report, and
also manually confirmed we now pass the _Extended Cursor-Position_ test
in vttest.

Closes #14206
2022-10-25 18:41:01 +00:00
Dustin Howett
7c8f74259d Migrate OSS up to 3eaa78149 2022-10-25 10:55:49 -05:00
Dustin Howett
0c130fa65b Merged PR 8030958: [Git2Git] Merged PR 8017580: Emit traces to determine user opt-in status for Default-by-default
[Git2Git] Merged PR 8017580: Emit traces to determine user opt-in status for Default-by-default

We already have tracing in the console host that tells us when a
console session was successfully handed off to a Terminal. However, that
doesn't provide us enough information about Windows' intent in doing
so--namely, (1) whether the user _wanted_ that handoff to happen, OR (2)
whether the user has opted out because they didn't want it to happen.

(1) looks like any other hand-off, which will pollute our statistics
(2) doesn't generate any messages, because we fail out of handoff before
logging a single thing.

This pull request adds new, better events.

The events look like this (in TVPP):

```
Microsoft.Windows.Console.Host	ConsoleHandoffSessionStarted
handoffCLSID: 	{2eaca947-7f5f-4cfa-ba87-8f7fbeefbe69}
handoffTargetChosenByWindows: 	true
```

```
Microsoft.Windows.Console.Host	ConsoleHandoffSessionStarted
handoffCLSID: 	{2eaca947-7f5f-4cfa-ba87-8f7fbeefbe69}
handoffTargetChosenByWindows: 	false
```

```
Microsoft.Windows.Console.Host	ConsoleHandoffSessionStarted
handoffCLSID: 	{b23d10c0-e52e-411e-9d5b-c09fdf709c7d}
handoffTargetChosenByWindows: 	false
```

Cherry picked from !7583836
Cherry-picked from commit `26f311e2`.

Fixes MSFT-41943733

Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 2c876875f24263409175e986102862eda4f09f32
2022-10-25 15:45:00 +00:00
Carlos Zamora
3eaa781499 Remove redundant tooltips from settings UI (#14244)
## Summary of the Pull Request
This removes all of the redundant tooltips from the settings UI. Since all of the settings are added through the SettingsContainer, it's a pretty simple change.

Closes #14184

## Validation Steps Performed
- [X] hover over all settings in the settings UI
- [X] hover over all entries in the SUI nav view
2022-10-24 19:38:27 +00:00
Leonard Hecker
bbc14a0baf Use float throughout ColorFix (#14266)
This is just a quick drive-by improvement. Switching from double to float
roughly doubles performance on a contemporary x86 CPU with `/fp:fast`.

## Validation Steps Performed
* Patch `RenderSettings.hpp` to include `Mode::AlwaysDistinguishableColors`
* Run a color intense application in AtlasEngine and observe CPU usage
2022-10-24 18:49:39 +00:00
Leonard Hecker
b674ac5c19 Fix a test failure in HashTests (#14277)
744ca24 broke `HashTests` as it renamed a preprocessor definition.
2022-10-21 12:19:34 -05:00
Dustin Howett
744ca2450a Merge remote-tracking branch 'openconsole/inbox' 2022-10-20 15:08:04 -05:00
Dustin Howett
f88ef02c98 Merged PR 8014375: [Git2Git] Build fixes on top of bfd480b88
This pull request introduces a number of source files to ut_til/sources.

Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 35865f47ced9103b02b06a1eb8d43d26b420af99
2022-10-20 20:07:00 +00:00
Dan Albrecht
bcf2422c4d Remove vestigial submodule entry (#14248)
Remove vestigial submodule entry

## References
#12778 removed the actual WIL submodule, but there was still an entry left in the `.gitmodules` file that was causing me confusion about an orphaned submodule directory I had in an old copy of the repo.

## Validation Steps Performed
 - Official validation checks passed
 - Build still works locally after a `git clean -fdx`
2022-10-20 18:49:27 +00:00
Dustin Howett
7e7a69ff7c Migrate OSS up to bfd480b88 2022-10-20 13:29:14 -05:00
Leonard Hecker
bfd480b885 Fix circular TermControl reference (#14228)
This regression was introduced in b3c9f01. Since `TermControl` is the XAML
object that owns its scrollbar and the scrollbar's `VisualStateManager`
a strong reference back to the `TermControl` results in a circular reference.

## Validation Steps Performed
* Set a breakpoint on `TermControl::~TermControl()`
* Breakpoint hits on tab close 
2022-10-17 21:14:00 +00:00
Ian O'Neill
6d94fbc89f Update XamlStyler version (#14230)
Updates the version of XamlStyler to one with support for .NET 6.0. The version used before this depended on .NET 3.1, [which goes out of support on 2022-12-13.](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core)

This shouldn't be controversial as .NET 6.0 is included with VS 2022, unlike .NET 3.1.
2022-10-17 17:48:10 +00:00
Ian O'Neill
42befa7b58 Override center on launch setting when position specified on cmdline (#14229)
Override the center on launch setting when a position is specified on the commandline.

## Validation Steps Performed
1. Set center on launch in the SUI.
2. Run `wtd` - the new window is centered.
3. Run `wtd --pos 100,200` - the new window is positioned at (100,200).

Closes #14176
2022-10-17 17:44:45 +00:00
Leonard Hecker
97abc3df1d AtlasEngine: Block chars done right-er (#14099)
This commit makes the following improvements:
* Only adjust block characters that come from fallback fonts. This ensures
  that the glyphs of the chosen font all look exactly as they were designed.
* When adjusting the size, use the fallback font's full block glyph U+2588
  to determine the size that the given glyph should have.

Closes #14098

## Validation Steps Performed
* Print `UTF-8-demo.txt` in Consolas.
* All block glyphs look uniform. 
2022-10-17 17:25:08 +00:00
d-caldasCaridad
8ef18d3c88 README: Fix the winget install instructions (#14236) 2022-10-17 12:13:16 -05:00
Dustin L. Howett
1f19ed0cd2 Fix the Release build ProxyStubClsids from #13570 (#14225)
These were wrong.
2022-10-15 07:58:03 +00:00
Dustin L. Howett
2a5ce4900f Stop freeing BSTRs that do not belong to us (#14224)
The `BSTR` arguments originate from COM calls who own them.
Found via AppVerifier.
2022-10-15 07:00:04 +00:00
Dustin L. Howett
ab04067e49 Disallow a window waiting on system() to block DefTerm startup (#14195)
We'll just ping the window and give it a chance to respond before we
bunk with it.

Fixes #14131
2022-10-14 21:50:22 +00:00
Dustin L. Howett
18e4e22394 OpenHere: stop failing if there's no site... (#14211)
It cannot be known why there is no site.
We should at least not crash.

Fixes MSFT-41571451
2022-10-14 14:32:06 -05:00
Bjorn Neergaard
33cb0eb05f Fix a missing entry for intenseTextStyle in the Profile schema (#14210)
Fix a missing entry in the JSON schema for `intenseTextStyle`.

The JSON schema was missing an entry in the Profile section for
`intenseTextStyle`. I have added it as it appears in AppearanceConfig.
Note that this is currently duplicated in the schema -- however, this is
the pattern used already in Profile as AppearanceConfig entries have
alternate descriptions (and I have updated the description in that
section to make it clear it applies to unfocused terminals).
Longer-term, it likely makes sense to consolidate all entires into
ApperanceConfig and rely on the description for the `unfocusedApperance`
object/the name of the object to make the limited scope of those keys
clear, so that Profile can simply extend ApperanceConfig and the
duplication in the schema can be reduced.

## Validation Steps Performed
Validation with schema verifying tools including VS Code.

Partially addresses #13387
2022-10-14 14:30:44 -05:00
Dustin L. Howett
d319d479c7 Pipe DirectKey events into TerminalPage for Alt+Space (#14221)
This pull request operates on the same theory as #14217, but at a lower
level. Carlos and I discovered that TerminalPage *already* has an
action-dispatching key preview handler, and that my implementation of
`IDirectKeyListener` handles focus-tree bubbling mostly correctly.

Because of that discovery, we learned we could move the
`IDirectKeyListener` into TerminalPage itself and not have to complicate
the SUI or the Command Palette with the DirectKey interface.

Validation:
When bound to Alt+Space, the system menu works in the command palette,
the settings UI, and in read-only panes.

Fixes #11970
Closes #14217
Fixes MSFT-41390832
2022-10-14 19:19:44 +00:00
James Holderness
0022898fe8 Make sure VT reports still work when DECARM is disabled (#14216)
The way `DECARM` was initially implemented, we checked for repeated key
presses by matching the last recorded virtual key code, and used a 0 key
code to indicate that no key was pressed. This caused the VT query
responses to fail, because they generated key events with a 0 key code,
and that would end up being detected as a repeated key that should be
suppressed.

This PR fixes that issue by using a `std::optional` to track the last
key code, so if no key has been pressed we can represent that with
`std::nullopt`, and there's no way that can be confused with a genuine
key press.

The `DECARM` mode was introduced in PR #13981.

## Validation Steps Performed
I've manually tested in Vttest to confirm that the query reports are now
working again, even when `DECARM` is disabled. I've also checked that
`DECARM` itself it still working as expected.

Closes #14208
2022-10-14 17:39:36 +00:00
Leonard Hecker
1774cfd843 Fix a deadlock during ConPTY shutdown (#14160)
Problem:
* Calling `RundownAndExit` tries to flush out the last frame from `VtEngine`
* `VtEngine` indirectly calls `RundownAndExit` if the pipe is gone via `VtIo`
* `RundownAndExit` is called by other parts of OpenConsole
* `RundownAndExit` must be `[[noreturn]]` for most parts of OpenConsole
* `VtIo` itself has a mutex ensuring orderly shutdown
* In short, doing a thread safe orderly shutdown requires us to hold both,
  a lock in `RundownAndExit` and `VtIo` at the same time, but while other parts
  need a blocking `RundownAndExit`, `VtIo` needs a non-blocking one
* Refactoring the code to use optionally non-blocking `RundownAndExit`
  requires refactoring and might prove to be just as buggy

Solution:
* Simply don't call `RundownAndExit` in `VtEngine` at all
* In case the write pipe breaks:
  * `VtEngine` will close the handle
  * The client should notice that their read pipe is broken and
    close their write pipe sooner or later
  * Once we notice that our read pipe is broken, we call `RundownAndExit`
  * `RundownAndExit` might call back into `VtEngine` but
    without a pipe it won't do anything
* In case the read pipe breaks or any other part calls `RundownAndExit`:
  * We call `RundownAndExit`
  * `RundownAndExit` might call back into `VtEngine` and depending on whether
    the write pipe is broken or not it will simply write into it or ignore it

Closes #14132
Pretty sure this also applies to #1810

## Validation Steps Performed
* Open 5 tabs and run MSYS2's `bash --login` in each of them
* `Enter-VsDevShell` in another tab
* Close window
* 5 tab processes are killed instantly, 1 after ~3s 
* Replace conhost with OpenConsole via sfpcopy
* Launch Dozens of Git Bash tabs in VS Code
* Close them randomly
* Remaining ones still work, processes are gone 
2022-10-13 23:17:32 +00:00
Dustin L. Howett
07201d6cd1 Add a maximum OSC 8 URI length of 2MB following iTerm2 (#14198)
c0b2f488c1

Unlike iTerm2, we're not planning on making it configurable.

This commit also adds a max length of 1024 characters on the "display URI" that we pass up to XAML, so as to not inundate the UI.

Fixes #14200
2022-10-12 22:16:38 +00:00
Rose
eebea5129c Fix a few C++ Warnings, default a bunch of ctors/dtors (#13926)
No behavioral changes, just some modernizations like replacing empty methods with default and using _v instead of ::value for some types.
2022-10-12 12:58:10 -07:00
PankajBhojwani
6033ae66c5 Update LaunchPosition to use int32_t instead of int64_t (#14190)
Since #13730 merged, when we parse LaunchPosition we treat the
coordinates as `int32_t`. This PR updates the actual `LaunchPosition`
struct to also use `int32_t` for consistency.

## Validation Steps Performed
Terminal still builds and runs
2022-10-12 17:01:00 +00:00
Rose
e4b80e2ef3 Generally tidy the Commandline class (#14092)
This pull request adds a couple `const` keywords, simplifies a bit of boolean logic,
adds the `static` keyword to `Commandline::IsEditLineEmpty`, and a couple more things.
2022-10-12 14:22:34 +00:00
PankajBhojwani
6803cfb96f Fix some issues with the launch parameters in the SUI (#14186)
Addressing post-hoc comments on the launch parameters expander in the SUI (added in #13605)

- Use more contextually appropriate strings (`Centered` instead of `On` / `Off`)
- Don't emit an extra `NotifyChanges`

References #13605
2022-10-11 23:28:14 +00:00
Jeroen B
8f08bb04d0 Fix duplication issue for unfocused tabs (#13964)
Instead of using the currently focused tab when an unfocused tab is duplicated, the `_MakePane(...)` function now uses an optional source tab argument that points to the correct tab being duplicated.

## Validation Steps Performed

Manually tested on multiple tabs with different profiles. Performed steps:

* Construct at least two tabs with different profiles.
* Select `Duplicate Tab` option from the dropdown menu of the unfocused tab.
* Verify that the new tab has the same profile as the tab it was duplicated from.

Closes #13942
2022-10-11 18:25:33 -05:00
Carlos Zamora
30046dd4a7 Make SUI breadcrumb readable by screen readers (#14180)
The breadcrumbs in the SUI were not readable by screen readers because they are represented as a button with a text block inside of it. Turns out, if you make the DataTemplate's item `IStringable` (meaning it has a `ToString()`), it all magically works! Allowing the screen reader to read the button as text.

Closes #13826
2022-10-11 23:17:54 +00:00
James Holderness
cacf66860f Add support for DECARM (Auto Repeat Mode) (#13981)
This PR adds support for the `DECARM` (Auto Repeat Mode) sequence, which
controls whether a keypress automatically repeats if you keep it held
down for long enough.

Note that this won't fully work in Windows Terminal until issue #8440 is
resolved.

Every time we receive a `KeyDown` event, we record the virtual key code
to track that as the last key pressed. If we receive a `KeyUp` event the
matches that last key code, we reset that field. Then if the Auto Repeat
Mode is reset, and we receive a `KeyDown` event that matches the last
key code, we simply ignore it.

## Validation Steps Performed

I've manually tested the `DECARM` functionality in Vttest and confirmed
that it's working as expected. Although note that in Windows Terminal
this only applies to non-alphanumeric keys for now (e.g. Tab, BackSpace,
arrow keys, etc.)

I've also added a basic unit test that verifies that repeated key
presses are suppressed when the `DECARM` mode is disabled.

Closes #13919
2022-10-10 16:33:06 -07:00
Jonathan Meier
21a9c55752 Fix clipped progress ring in tab when tab title is too long (#14167)
It turns out that the negative margin for the progress ring is causing
the clipping in case the tab title gets too long:

43dbbd590f/src/cascadia/TerminalApp/TabHeaderControl.xaml (L18-L27)

The negative margin was introduced in #8113 because the progress ring is
supposed to replace the tab icon but the `TabView` still reserves space
even if no icon is set (see
https://github.com/microsoft/terminal/pull/8133#issuecomment-739098014).
However, it is not actually the `TabView` reserving space even when
there is no icon, but a workaround for a crash in the
`IconPathConverter` that returns a `BitmapIconSource` with a `nullptr`
source instead of a `nullptr` `IconSource`:

43dbbd590f/src/cascadia/TerminalSettingsModel/IconPathConverter.cpp (L143-L154)

The workaround in `IconPathConverter` could probably be removed as I did
not find any instance where it is still used in a way that could trigger
the mentioned crash, but I did not dare to just remove it as I do not
know enough about the code by far. Hence, I opted to just locally
instantiate the `IconSource` with a `nullptr` directly in `TerminalTab`.

Fixes #8910
2022-10-10 17:41:55 +00:00
Ian O'Neill
43dbbd590f Add --pos and --size cmdline args (#13730)
Adds `--pos`, and `--size` commandline arguments to `wt`.

Closes #4620
2022-10-07 16:41:09 -07:00
Rose
3517a6a3f1 Update scratch projects to XAML 2.7.3 (#14159)
This will let those projects build. I forgot to include them last time for some reason.

See PR #14123
2022-10-07 22:21:15 +00:00
Rose
d6ac717a48 Update target .NET version to .NET 6 (#14137)
.NET 6 is the latest LTS, and we should use that instead of .NET Core 3.1
2022-10-07 20:08:40 +00:00
James Holderness
657dd5f43e Add support for selective erase operations (#14046)
This PR adds support for the selective erase escape sequences: `DECSED`,
`DECSEL`, and `DECSCA`. They provide a way of marking certain areas of
the screen as "protected", so you can erase the content everywhere else
without affecting those protected areas.

This adds another bit in the `CharacterAttributes` enum to track the
protected status of each cell, and an operation triggered by the
`DECSCA` sequence which can toggle that bit in the active attributes.

There there are two new erase operations triggered by the `DECSED` and
`DECSEL` sequences, which work similar to the existing `ED` (erase in
display) and `EL` (erase in line) operations, but which only apply to
unprotected cells.

I've also updated the `DECRQSS` settings request, so you can query the
active protected attribute status.

## Validation Steps Performed

I've manually confirmed that we pass the selective erase tests in Vttest
now, and I've also manually tested some more complicated edge cases and
confirmed that we match the behavior of the hardware VT240 emulator in
MAME.

For unit testing I've extended the existing erase tests to cover
selective erase as an additional option, I've added a test covering the
`DECSCA` sequence, and I've extended the `DECRQSS` adapter test to
confirm the attribute reporting is working.

Closes #14029
2022-10-07 09:49:23 -07:00
Rose
11ad04754d Update to XAML 2.7.3 (#14123)
And the prerelease version of it
2022-10-07 00:13:57 +00:00
Leonard Hecker
51c0b423fb Upgrade to Windows SDK 22621 (#14135)
The diff between the 22000 and 22621 SDKs is fairly small, but it does include
a number of C++ correctness fixes, updates to libraries like DirectXMath and
the latest updates to DirectWrite and DXGI which I make heavy use off.

## Validation Steps Performed
* It builds 
2022-10-07 00:09:27 +00:00
Rose
d7e24ad6d0 Update libpopcnt to 2.5 (#14140) 2022-10-06 23:43:42 +00:00
Rose
a63f060f72 Update IntervalTree.h dependency (#14148)
This was the last update since 2021
2022-10-06 23:42:20 +00:00
Carlos Zamora
5608cf15a3 [wpf] Add UIA events (#14097)
Adds UIA events to the WPF control for the following items:
- selection changed
- text changed (and output)
- cursor changed

### Automation Peer
Similar to the architecture of the UWP TermControl, we added a
`HwndTerminalAutomationPeer` which acts as the
`TermControlAutomationPeer` in UWP. However, we don't need a XAML
wrapper here, so really we just need it to inherit from
`TermControlUiaProvider` (the `ITextProvider` implementation shared
across conhost and WT) and `IUiaEventDispatcher` (the event dispatching
interface that is responsible for signaling the screen reader that
something has changed).

### Removing the local echo
As with WT, we need to record key events to remove the local echo. These
recorded events are matched up with the output text. Each sequential
match is removed in the output text so that it's not read by the screen
reader.

### Detecting what to send events for
As with WT, a `UiaEngine` was added to the renderer and it is set up
when a UIA client is detected. WT would normally stop sending events
when focus was lost from the control. We do the same here.

### Automation properties
`TermControlUiaProvider` was upgraded to support property values. Such
properties include class name and control type. These align with those
set in `TermControlAutomationPeer`. Realistically, those should point to
these, but that requires a lot more work and a localization burden
(because we need to move the localized word "terminal").

`HwndTerminalAutomationPeer` takes this a step further and overrides the
class name to be `WPFTermControl`. This allows screen readers to provide
special handling for the `WPFTermControl` vs the UWP term control since
they will be updating at different speeds.

### Build fixes
To build the WPF test app, I had to mess with the dependencies a little
bit. Really just add the atlas engine and uia renderer to the build
steps.

### HwndTerminal initialization
The initialization order with `WM_NCCREATE` was changed to match that of
Windows Terminal (BaseWindow/IslandWindow). This is safer now. I also
removed the `static` window because it was unnecessary.

### Handling `WM_GETOBJECT`
WPF's HwndHost likes to mark the `WM_GETOBJECT` message as handled to
force the usage of the WPF automation peer. We now explicitly mark it as
not handled and don't return an automation peer. This forces the message
to go down to the HwndTerminal where we return terminal's UiaProvider.

### Remove TermControl layer from UIA tree
TermContol (the top-most layer in the UIA tree) would pop up and not do
anything. This PR also overrides the automation peer at that layer and
marks IsContentElement/IsControlElement=false (the equivalent to
AccessibilityView=Raw). This makes the layer only appear in the UIA tree
if you are using the raw view (i.e. you know what you're doing and you
want to see each individual layer even if you can't directly interact
with it).

## Validation Steps Performed
Tested with Narrator/NVDA using WpfTerminalTestNetCore project in our
repo.
- [X] New output is read out (not just key events, but also other output
  text)
- [X] Local echo does not occur (i.e. pressing 'A' should only read 'A'
  once, not twice [key event and rendered letter]).
- [X] selection events are read out properly
- [X] cursor change events are read out properly (tested with text
  cursor indicator preview in Settings App > Accessibility > Text
  Cursor)

NOTE: test this with Release builds. Debug builds may be too slow and
not read out properly

Closes #12642
2022-10-06 23:11:47 +00:00
PankajBhojwani
40bc3d7fbc Implement InitialPosition and CenterOnLaunch in the SUI (#13605)
## Summary of the Pull Request
`InitialPosition` and `CenterOnLaunch` can now be edited in the SUI

## PR Checklist
* [x] Closes #9075 
* [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'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
`InitialPosition` follows the same style as `LaunchSize`, with a number box for the x coordinate and a number box for the y coordinate. When there is no value for either of these coordinates, the respective number box is empty (and displays the text `Undefined`). 

## Validation Steps Performed
They work
2022-10-05 19:37:16 +00:00
Rose
00cdacc96b Update jsoncpp from 1.9.3 to 1.9.5 (#14122) 2022-10-05 16:35:27 +00:00
Kayla Cinnamon
54dc2c4432 Add tooltips to context menus (#14058)
Add tooltips to the tab context menu and the tab dropdown menu.

Closes #13243
2022-09-30 18:04:27 +00:00
Dustin L. Howett
fc0ef37977 Reject illegal paths in OSC 9;9 (#14093)
Paths that contain illegal path components will be dropped
in the dispatcher.
2022-09-27 20:45:58 -05:00
Rose
1da3bc7f1f Use Nuget 6.x (#13937)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

Update Nuget used
2022-09-27 19:40:32 +00:00
Leonard Hecker
2de475b36a AtlasEngine: Partially revert glyph scaling changes (#14085)
This change fixes issues with certain fonts that draw _way_ outside the advance
width/height black box and expect to remain centered on the baseline.

## Validation Steps Performed
* Use MesloLGM Nerd Font
* Print U+E0B0
* Ensure it's centered, even if it's cut off 
2022-09-27 18:59:13 +00:00
Rose
c392ade6dd Simplify types (#14071)
Use _v suffix instead of the ::value suffix
2022-09-27 18:58:17 +00:00
Rose
975d767d11 Use using over typedef (#14072) 2022-09-27 18:57:42 +00:00
Rose
4f3a639c19 Use std::ranges to simplify code (#14070)
## Validation Steps Performed
Manual and Automated testing
2022-09-27 18:56:51 +00:00
Mike Griese
e1e6e662f9 Add the Needs-Triage label automatically
if the bot adds it, then the issue will get added to the project before the bot gets a chance to add the triage label. Just start with the triage label.
2022-09-23 08:20:21 -05:00
Leonard Hecker
97dc5c8d75 AtlasEngine: Fix uneven baselines when scaling glyphs (#14039)
This commit changes the glyph scale algorithm to prefer aligning glyphs to
their baseline. This improves the visual appearance of simulated italic glyphs.
However wide Emojis in narrow cells now look slightly worse without centering.

Closes #13987

## Validation Steps Performed
* Use FiraCode which has no italic variant and instead uses simulated italics
* Write italic text
* Baseline is consistent 
2022-09-21 22:30:15 +00:00
Leonard Hecker
274bdb31da Fix potential lags/deadlocks during tab close (#14041)
`ConptyClosePseudoConsole` blocks until OpenConsole exits.
This is problematic for the changes in 666c446, which stopped calling that
function on a background thread to solve a race condition. This commit fixes
the potential lags/deadlocks from waiting on OpenConsole's exit, by adding
`ConptyClosePseudoConsoleNoWait` which only closes the IO handles and allows
OpenConsole to exit naturally. This uncovered another potential deadlock
in `ServiceLocator::RundownAndExit` which might call itself recursively.

Closes #14032

## Validation Steps Performed
* Print tons of text and concurrently close the tab.
  Tab closes, OpenConsole/pwsh exits instantly 
* Use `Enter-VsDevShell` and close the tab.
  Tab closes instantly, OpenConsole/pwsh exits after ~5 seconds 
2022-09-21 22:26:30 +00:00
Dustin L. Howett
5027c8031d OpenHere: Replace explorer window lookup code w/ site lookup (#14048)
When we first introduced the shell extension, it didn't work properly
for some folders (such as the Desktop, or perhaps any "background"
click) due to a bug in Windows. We worked around that bug with the help
of an awesome community member, who contributed code that would pull up
the topmost Explorer window and query its location.

That Windows bug was eventually fixed, but we still had trouble with
items appearing correctly. On Windows 11, the Open in Terminal menu item
appears and disappears at random when you right-click the desktop, but
it always appears when you right-click a folder. It sometimes appears
for Quick Access, even though it shouldn't.

We tried to fix that in #13206, but the fix caused more issues than it
solved. We reverted it for 1.15 and 1.16.

At the end of the day, it turns out that getting the path from the
toplevel explorer window is fragile. Fortunately, the shell does offer
us a way to get that information: the site chain.

This pull request replaces GetPathFromExplorer() with an implementation
of `IObjectWithSite`, which allows us to use the site chain to look up
from whence a context menu request was initiated. It also makes item
lookup generally more robust.

*  Tested on Windows 11
  *  Desktop
  *  Folder Background
  *  Folder Selected
  *  Quick Access (does not appear)
  *  This PC (does not appear)
*  Tested on Windows 10
  *  Desktop
  *  Folder Background
  *  Folder Selected
  *  Quick Access (does not appear)
  *  This PC (does not appear)

References #13206
References #13523
Closes #12578

Co-authored-by: John Lueders <johnlue@microsoft.com>
2022-09-21 12:41:37 -05:00
Steve Otteson
b3c9f01432 Add support for scroll bar 'always' setting (#14047)
This fixes #3454 by adding support for an "always" mode for the scroll bar.

This change uses a custom VisualStateManager to keep the scroll bar from collapsing if the profile is using the 'always' setting.

## Validation Steps Performed
Tried updating settings.json directly and using the UI and making sure the scroll bar behaves as expected.

Closes #3454
2022-09-21 12:24:11 +00:00
Dustin Howett
c0c284fed3 Merged PR 7872414: [Git2Git] Merged PR 7872347: BUILD BREAK FIX: Use the X86 wyhash32 code for ARM32
We're using this in UnicodeStorage!

Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 828282c76d559ae2dbbee4288796a939e7f869f8

Related work items: MSFT-41396187
2022-09-20 21:42:17 +00:00
Leonard Hecker
08096b2343 AtlasEngine: Fix cursor invalidation (#14038)
There's a different behavior regarding cursors between conhost and Windows
Terminal. In case of the latter we don't necessarily call `PaintCursor`
during cursor movement, because the cursor blinker never stops "blinking".

Closes #14028

## Validation Steps Performed
* Enter text until after the line wraps
* Hold backspace until the line unwraps
* No leftover cursor on the second line 
2022-09-20 12:37:03 -05:00
Leonard Hecker
f79276b21b Fix font size rounding in the settings UI (#14040)
This fixes an issue with c51bb3a, where some fractional font
sizes are displayed as something like 13.600000000001.

Closes #14024

## Validation Steps Performed
* Enter a font size of 13.6 and save
* NumberBox displays "13.6" 
2022-09-20 12:36:48 -05:00
James Holderness
1ce22a87e6 Merge the legacy and extended attributes (#14036)
This PR attempts to simplify the `TextAttribute` class by merging the
two fields that were previously storing the "legacy" attributes and the
"extended" attributes separately.

When the `TextAttribute` class is initialized with a legacy value, we
were masking off the `META_ATTRS` bits to store in the `_wAttrLegacy`
field, and then additionally clearing the `COMMON_LVB_SBCSDBCS` bits,
so there were only 5 bits that were actually used in the end. We also
had an additional `_extendedAttrs` field holding other VT attributes,
which only used 8 of its available 16 bits.

In this PR I've now merged the the two sets of attributes into one enum,
so they all fit in a single 16-bit value. The legacy attributes retain
the same bit positions they originally had, so we can mask them off from
an incoming legacy value as we did before. I've just simplified the
process somewhat by creating a `USED_META_ATTRS` mask that covers the
exact subset of meta attributes that we care about.

The new enum that holds the combined attributes has now been named
`CharacterAttributes` rather than `ExtendedAttributes`, since that seems
to be the term typically used in VT documentation. This covers both
rendition/visual attributes and logical attributes (not yet used, but we
will need them at some point to support selective erase operations).

While making these changes I also noticed the `IsLeadingByte` and
`IsTrailingByte` methods weren't actually used anywhere, and weren't
correctly implemented anyway, so I've removed those now.

## Validation Steps Performed

I've manually run a number of attribute test scripts which cover both
legacy and VT operations, and everything still appears to be working
correctly.

Closes #14031
2022-09-20 13:15:20 +00:00
Leonard Hecker
c51bb3a7a6 Add support for fractional font sizes (#14013)
After this commit a user may specify fractional font sizes.
Support was only implemented for AtlasEngine however.
DxEngine continues to use rounded (integer) font sizes.

Closes #6678

## Validation Steps Performed
* Install a bitmap font that requires fractional font sizes
  (e.g. Terminus TTF, https://files.ax86.net/terminus-ttf/)
* Set font size to something integer (e.g. 14pt)
  Glyphs are blurry 
* Set font size to something fractional (e.g. 13.5pt)
  Glyphs are crisp 
2022-09-16 22:37:56 +00:00
Dustin Howett
6567201e0e Merge remote-tracking branch 'openconsole/inbox' into main 2022-09-16 15:34:50 -05:00
Leonard Hecker
bea13bddf1 AtlasEngine: Fix bugs around bitmap font rendering (#14014)
This commit fixes several issues:
* Some fonts set a line-gap even though they behave as if they
  don't want any line-gaps. Since Terminals don't really have
  any gaps anyways, it'll now not be taken into account anymore.
* Center alignment breaks bitmap glyphs which expect left-alignment.
* Automatic "opsz" axis makes Terminus TTF's italic glyphs look quite
  weird. I disabled this feature as we might not need it anyways.

A complete fix depends on #14013
Closes #14006

## Validation Steps Performed
* Use Terminus TTF at 13.5pt
* Print UTF-8-demo.txt
* No gaps between block characters 
2022-09-16 18:54:11 +00:00
Leonard Hecker
81e2bc98d1 Stop DoSing users with renderer errors (#13995)
If a rendering engine constantly throws error we'll effectively
denial-of-service our users by drowning them in warning popups.
This commit fixes the issue by limiting the retries in all cases.

Issue found in: #13985

## Validation Steps Performed
* Add a `THROW_HR(E_INVALIDARG);` in `AtlasEngine::StartPaint()`
* Launch Windows Terminal
* Only one warning popup shows up 
* Rendering is disabled until one clicks "resume" 
2022-09-16 14:17:10 +00:00
Mike Griese
446ef22044 apparently I don't know yaml 2022-09-15 16:17:27 -05:00
Leonard Hecker
16aa79d78d AtlasEngine: Fix a crash when drawing double width rows (#13966)
The `TileHashMap` refresh via `makeNewest()` in `StartPaint()` depends
on us filling the entire `cellGlyphMapping` row with valid data.
This commit makes sure to initialize the `cellGlyphMapping` buffer.
Additionally it clears the rest of the row with whitespace
until proper `LineRendition` support is added.

Closes #13962

## Validation Steps Performed
* vttest's "Test of double-sized characters" stops crashing 
* No weird leftover characters 
2022-09-15 17:01:33 +00:00
Leonard Hecker
e2b2d9b92c AtlasEngine: Properly detect shader model 4 support (#13994)
Direct3D 10.0 and 10.1 only have optional support for shader model 4.
This commit fixes our assumption that it's always present by checking
`ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x` first.

Closes #13985

## Validation Steps Performed
* Set feature level to 10.1 via `dxcpl`
* `CheckFeatureSupport` is called and doesn't throw 
2022-09-14 23:27:46 +00:00
Dustin Howett
fba4e227f0 Merged PR 7854069: [Git2Git] Build fixes on top of 704458ee0
Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 4024b6933d446a359a35136053da8b4a8f598d9d

Related work items: MSFT-41327033
2022-09-14 21:53:13 +00:00
Dustin Howett
d21036d313 Merged PR 7847415: [Git2Git] Migrate all GSL Golden Path references to use VCPkg
Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_we_adept_e4d2 7ea9457533db712ee8e8a5a11e1fbbdfc9430027

Related work items: MSFT:40841395
2022-09-14 18:03:49 +00:00
Dustin Howett
79ef7477f2 Migrate OSS up to 704458ee0 2022-09-13 16:37:28 -05:00
Dustin Howett
0973aeab15 Merged PR 7705347: Add stubs to Terminal for OneCoreSafe*, fix the ConIoSrv header
This is required for us to build out of the Terminal repo.

Related work items: MSFT-40435912
2022-08-10 23:18:27 +00:00
Dustin Howett
a94e508010 Merged PR 7705187: [Git2Git] Merged PR 7693103: Reintroduce OneCore redirects for MapVKeyW/VkKeyScanW/GetKeyState
This pull request reintroduces aliases for `VkKeyScanW`,
`MapVirtualKeyW` and `GetKeyState` that redirect through ConIoSrv on
OneCore devices.

We made an assertion in PR !7096375 that those APIs were hosted in an
extension APIset that was present on all OneCore devices. It turned out
that this was _incorrect_: that APIset extension is only hosted on
OneCoreUAP and above.

This would not be a problem save for NanoServer. NanoServer is built on
top of OneCore.

As Nano is a container host OS, it is primarily interfaced vith via
ConPTY... which exercises the VkKeyScanW/MapVirtualKeyW codepaths quite
a bit. Those APIs started returning invalid data, which caused us to
convert all incoming keyboard events into numpad events. This didn't
prove to be an issue for CMD or PowerShell (weirdly,) but it did prove
to be an issue for Redis. Unfortunately, Redis is exactly the sort of
thing you might want to run in a container.

Reintroducing these aliases was complicated because we took the
opportunity to remove all of IInputServices (!7105348), which was a
wrapper around some code that would choose Win32 or OneCore depending on
the runtime environment.

I made the choice (with the help of Leonard Hecker) to reimplement these
functions in a different way: always call the delay-loaded version, and
then on OneCore editions check the return value and error code to ssee
if we hit a delay load failure. It incurs a minor cost, but all of the
delay loads are in-proc and do not require us to make a syscall, so that
cost is negligible.

Part of this new implementation requires us to change _all conhost
internal callers_ to use "OneCoreSafe" versions of those APIs. We can't
redirect the user32 versions out of the way and usurp their import
symbols, so this commit also introduces some warning defines. If you use
VkKeyScanW (and friends), you _should_ get a linker error. Assuming
HostAndPropsheetIncludes has been included. It very well may not have
been included.

Fixes MSFT-40435912
Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 949e8dfc07f122520c6a74412329a6f7e77d19c5
2022-08-10 21:14:52 +00:00
424 changed files with 7547 additions and 7105 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"XamlStyler.Console": {
"version": "3.2008.4",
"version": "3.2206.4",
"commands": [
"xstyler"
]

15
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,15 @@
# Commits mentioned in this file will automatically
# be skipped by GitHub's blame view.
# To use it with "git", run
# > git blame --ignore-revs-file ./.git-blame-ignore-revs
# Reformatted the entire codebase
9b92986b49bed8cc41fde4d6ef080921c41e6d9e
# Line Endings changes
cb7a76d96c92aa9fc7b03f69148fb0c75dff191d
5bbf61af8c8f12e6c05d07a696bf7d411b330a67
d07546a6fef73fa4e1fb1c2f01535843d1fcc212
# UTF-8 BOM changes
ddae2a1d49d604487d3c963e5eacbeb73861d986

View File

@@ -1,6 +1,6 @@
name: "Bug report 🐛"
description: Report errors or unexpected behavior
labels: [Issue-Bug]
labels: [Issue-Bug, Needs-Triage]
body:
- type: markdown
attributes:
@@ -14,7 +14,7 @@ body:
label: Windows Terminal version
placeholder: "1.7.3651.0"
description: |
You can find the version in the about dialog, or by running `wt -v` at the commandline.
You can copy the version number from the About dialog. Open the About dialog by opening the menu with the "V" button (to the right of the "+" button that opens a new tab) and choosing About from the end of the list.
validations:
required: false

View File

@@ -5,6 +5,7 @@ on:
issues:
types:
- labeled
- unlabeled
jobs:
add-to-project:

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "dep/gsl"]
path = dep/gsl
url = https://github.com/microsoft/gsl
[submodule "dep/wil"]
path = dep/wil
url = https://github.com/microsoft/wil

View File

@@ -1,35 +1,36 @@
{
"version": "1.0",
"components": [
"Microsoft.Component.MSBuild",
"Microsoft.Net.Component.4.5.TargetingPack",
"Microsoft.Net.Component.4.TargetingPack",
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
"Microsoft.NetCore.Component.DevelopmentTools",
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.Universal",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
"Microsoft.VisualStudio.Component.DiagnosticTools",
"Microsoft.VisualStudio.Component.Graphics",
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
"Microsoft.VisualStudio.Component.NuGet",
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
"Microsoft.Net.Component.4.TargetingPack",
"Microsoft.Net.Component.4.5.TargetingPack",
"Microsoft.VisualStudio.Component.DiagnosticTools",
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
"Microsoft.VisualStudio.Component.Windows11SDK.22000",
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
"Microsoft.VisualStudio.Component.VC.CoreIde",
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
"Microsoft.VisualStudio.Component.Graphics",
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64",
"Microsoft.VisualStudio.Component.VC.ASAN",
"Microsoft.VisualStudio.Component.VC.v143.x86.x64",
"Microsoft.VisualStudio.Component.VC.CoreIde",
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.VC.v143.ARM64",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
"Microsoft.VisualStudio.Component.VC.v143.x86.x64",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v143",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.Universal"
]
}

View File

@@ -1,26 +0,0 @@
{
"actions":
[
{
"command": { "action": "sendInput", "input": "bx\r" },
"name": "Build project",
"description": "Build the project in the CWD"
},
{
"command": { "action": "sendInput", "input": "bz\r" },
"name": "Build solution, incremental",
"description": "Just build changes to the solution"
},
{
"command": { "action": "sendInput", "input": "bcz\r" },
"name": "Clean & build solution",
"description": "Start over. Go get your coffee. "
},
{
"command": { "action": "sendInput", "input": "nuget push -apikey az -source TerminalDependencies %userprofile%\\Downloads" },
"name": "Upload package to nuget feed",
"description": "Go download a .nupkg, put it in ~/Downloads, and use this to push to our private feed."
},
]
}

View File

@@ -183,7 +183,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control", "src\cascadia\TerminalControl\dll\TerminalControl.vcxproj", "{CA5CAD1A-F542-4635-A069-7CAEFB930070}"
ProjectSection(ProjectDependencies) = postProject
{3C67784E-1453-49C2-9660-483E2CC7F7AD} = {3C67784E-1453-49C2-9660-483E2CC7F7AD}
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
EndProjectSection
EndProject
@@ -644,7 +643,8 @@ Global
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.Build.0 = Debug|x64
@@ -662,7 +662,8 @@ Global
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.ActiveCfg = Release|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.Build.0 = Release|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.Build.0 = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.ActiveCfg = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.Build.0 = Release|x64
@@ -2309,7 +2310,8 @@ Global
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.ActiveCfg = Debug|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.Build.0 = Debug|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.ActiveCfg = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.Build.0 = Debug|x64
@@ -2326,7 +2328,8 @@ Global
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.ActiveCfg = Release|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.Build.0 = Release|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.Build.0 = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.ActiveCfg = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.Build.0 = Release|x64
@@ -3314,7 +3317,8 @@ Global
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.Build.0 = Debug|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.ActiveCfg = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.Build.0 = Debug|x64
@@ -3334,7 +3338,8 @@ Global
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.ActiveCfg = Release|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.Build.0 = Release|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.Build.0 = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.ActiveCfg = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.Build.0 = Release|x64

View File

@@ -67,7 +67,7 @@ the latest Terminal release by installing the `Microsoft.WindowsTerminal`
package:
```powershell
winget install --id=Microsoft.WindowsTerminal -e
winget install --id Microsoft.WindowsTerminal -e
```
#### Via Chocolatey (unofficial)
@@ -289,7 +289,7 @@ If you would like to ask a question that you feel doesn't warrant an issue
app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)
to locally install and run Windows Terminal
* You must have [PowerShell 7 or later](https://github.com/PowerShell/PowerShell/releases/latest) installed
* You must have the [Windows 11 (10.0.22000.0)
* You must have the [Windows 11 (10.0.22621.0)
SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)
installed
* You must have at least [VS

View File

@@ -1,5 +1,5 @@
{
"msbuild-sdks": {
"Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20277.5"
"Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22525.5"
}
}

View File

@@ -14,16 +14,16 @@
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>
<!-- Mandatory. Major version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
<!-- Mandatory. Major version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
<PGOPackageVersionMajor>$(VersionMajor)</PGOPackageVersionMajor>
<!-- Mandatory. Minor version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
<!-- Mandatory. Minor version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
<PGOPackageVersionMinor>$(VersionMinor)</PGOPackageVersionMinor>
<!-- Mandatory, defaults to 0. Patch version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
<!-- Mandatory, defaults to 0. Patch version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
<PGOPackageVersionPatch>0</PGOPackageVersionPatch>
<!-- Optional, defaults to empty. Prerelease version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
<!-- Optional, defaults to empty. Prerelease version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
<PGOPackageVersionPrerelease></PGOPackageVersionPrerelease>
<!-- Mandatory. Path to nuget.config file for the project. Path is relative to where the props file will be. -->

View File

@@ -2,7 +2,7 @@
jobs:
- job: CodeFormatCheck
displayName: Proper Code Formatting Check
pool: { vmImage: windows-2019 }
pool: { vmImage: windows-2022 }
steps:
- checkout: self

View File

@@ -43,9 +43,9 @@ jobs:
filename: 'set'
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.2.0'
displayName: 'Use NuGet 6.3.0'
inputs:
versionSpec: 5.2.0
versionSpec: 6.3.0
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
displayName: 'NuGet restore build/Helix/packages.config'

View File

@@ -27,9 +27,9 @@ jobs:
displayName: 'Retrieve VC tools directory'
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.2.0'
displayName: 'Use NuGet 6.3.0'
inputs:
versionSpec: 5.2.0
versionSpec: 6.3.0
- task: NuGetAuthenticate@0

View File

@@ -1,8 +1,8 @@
steps:
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.2.0'
displayName: 'Use NuGet 6.3.0'
inputs:
versionSpec: 5.2.0
versionSpec: 6.3.0
- task: NuGetAuthenticate@0

View File

@@ -22,7 +22,7 @@ Param(
[Parameter(HelpMessage="Path to makeappx.exe")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x86\MakeAppx.exe"
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe"
)
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {

View File

@@ -8,7 +8,7 @@ Param(
[Parameter(HelpMessage="Path to Windows Kit")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0"
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0"
)
$ErrorActionPreference = "Stop"

View File

@@ -13,6 +13,10 @@ Licensed under the MIT license.
#define CIS_EVENT_TYPE_FOCUS (1)
#define CIS_EVENT_TYPE_FOCUS_ACK (2)
#define CIS_MSG_TYPE_MAPVIRTUALKEY (0)
#define CIS_MSG_TYPE_VKKEYSCAN (1)
#define CIS_MSG_TYPE_GETKEYSTATE (2)
#define CIS_MSG_TYPE_GETDISPLAYSIZE (3)
#define CIS_MSG_TYPE_GETFONTSIZE (4)
#define CIS_MSG_TYPE_SETCURSOR (5)
@@ -31,6 +35,22 @@ typedef struct {
UCHAR Type;
union {
struct {
UINT Code;
UINT MapType;
UINT ReturnValue;
} MapVirtualKeyParams;
struct {
WCHAR Character;
SHORT ReturnValue;
} VkKeyScanParams;
struct {
int VirtualKey;
SHORT ReturnValue;
} GetKeyStateParams;
struct {
CD_IO_DISPLAY_SIZE DisplaySize;

View File

@@ -2,8 +2,8 @@
[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated)
from source commit
[6aba23f](https://github.com/open-source-parsers/jsoncpp/commit/6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2),
release 1.9.3.
[5defb4e](https://github.com/open-source-parsers/jsoncpp/commit/5defb4ed1a4293b8e2bf641e16b156fb9de498cc),
release 1.9.5.
> Generating amalgamated source and header JsonCpp is provided with a script to
> generate a single header and a single source file to ease inclusion into an

View File

@@ -6,7 +6,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/open-source-parsers/jsoncpp",
"commitHash": "6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2"
"commitHash": "5defb4ed1a4293b8e2bf641e16b156fb9de498cc"
}
}
}

View File

@@ -7,28 +7,28 @@
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
@@ -94,10 +94,10 @@ license you like.
// 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.3"
#define JSONCPP_VERSION_STRING "1.9.5"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 3
#define JSONCPP_VERSION_PATCH 5
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
@@ -162,11 +162,10 @@ public:
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
* The pointer argument is tagged as "volatile" to prevent the
* compiler optimizing out this critical step.
*/
void deallocate(volatile pointer p, size_type n) {
std::memset(p, 0, n * sizeof(T));
void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}

View File

@@ -6,28 +6,28 @@
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
@@ -93,10 +93,10 @@ license you like.
// 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.3"
#define JSONCPP_VERSION_STRING "1.9.5"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 3
#define JSONCPP_VERSION_PATCH 5
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
@@ -161,11 +161,10 @@ public:
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
* The pointer argument is tagged as "volatile" to prevent the
* compiler optimizing out this critical step.
*/
void deallocate(volatile pointer p, size_type n) {
std::memset(p, 0, n * sizeof(T));
void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}
@@ -575,7 +574,7 @@ public:
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#pragma warning(disable : 4251 4275)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
@@ -788,10 +787,10 @@ private:
CZString(ArrayIndex index);
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
CZString(CZString const& other);
CZString(CZString&& other);
CZString(CZString&& other) noexcept;
~CZString();
CZString& operator=(const CZString& other);
CZString& operator=(CZString&& other);
CZString& operator=(CZString&& other) noexcept;
bool operator<(CZString const& other) const;
bool operator==(CZString const& other) const;
@@ -867,14 +866,15 @@ public:
Value(const StaticString& value);
Value(const String& value);
Value(bool value);
Value(std::nullptr_t ptr) = delete;
Value(const Value& other);
Value(Value&& other);
Value(Value&& other) noexcept;
~Value();
/// \note Overwrite existing comments. To preserve comments, use
/// #swapPayload().
Value& operator=(const Value& other);
Value& operator=(Value&& other);
Value& operator=(Value&& other) noexcept;
/// Swap everything.
void swap(Value& other);
@@ -1159,9 +1159,9 @@ private:
public:
Comments() = default;
Comments(const Comments& that);
Comments(Comments&& that);
Comments(Comments&& that) noexcept;
Comments& operator=(const Comments& that);
Comments& operator=(Comments&& that);
Comments& operator=(Comments&& that) noexcept;
bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const;
void set(CommentPlacement slot, String comment);
@@ -1442,8 +1442,8 @@ public:
* because the returned references/pointers can be used
* to change state of the base class.
*/
reference operator*() { return deref(); }
pointer operator->() { return &deref(); }
reference operator*() const { return const_cast<reference>(deref()); }
pointer operator->() const { return const_cast<pointer>(&deref()); }
};
inline void swap(Value& a, Value& b) { a.swap(b); }
@@ -1506,8 +1506,7 @@ namespace Json {
* \deprecated Use CharReader and CharReaderBuilder.
*/
class JSONCPP_DEPRECATED(
"Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
class JSON_API Reader {
public:
using Char = char;
using Location = const Char*;
@@ -1524,13 +1523,13 @@ public:
};
/** \brief Constructs a Reader allowing all features for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader();
/** \brief Constructs a Reader allowing the specified feature set for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
@@ -1797,6 +1796,9 @@ public:
* - `"allowSpecialFloats": false or true`
* - If true, special float values (NaNs and infinities) are allowed and
* their values are lossfree restorable.
* - `"skipBom": false or true`
* - If true, if the input starts with the Unicode byte order mark (BOM),
* it is skipped.
*
* You can examine 'settings_` yourself to see the defaults. You can also
* write and read them just like any JSON Value.
@@ -2000,6 +2002,8 @@ public:
* - Number of precision digits for formatting of real values.
* - "precisionType": "significant"(default) or "decimal"
* - Type of precision for formatting of real values.
* - "emitUTF8": false or true
* - If true, outputs raw UTF8 strings instead of escaping them.
* You can examine 'settings_` yourself
* to see the defaults. You can also write and read them just like any
@@ -2035,7 +2039,7 @@ public:
/** \brief Abstract class for writers.
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
*/
class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
class JSON_API Writer {
public:
virtual ~Writer();
@@ -2055,7 +2059,7 @@ public:
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
class JSON_API FastWriter
: public Writer {
public:
FastWriter();
@@ -2115,7 +2119,7 @@ private:
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
class JSON_API
StyledWriter : public Writer {
public:
StyledWriter();
@@ -2184,7 +2188,7 @@ private:
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
class JSON_API
StyledStreamWriter {
public:
/**

View File

@@ -6,28 +6,28 @@
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
@@ -202,14 +202,18 @@ template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
* Return iterator that would be the new end of the range [begin,end), if we
* were to delete zeros in the end of string, but not the last zero before '.'.
*/
template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
template <typename Iter>
Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
for (; begin != end; --end) {
if (*(end - 1) != '0') {
return end;
}
// Don't delete the last zero before the decimal point.
if (begin != (end - 1) && *(end - 2) == '.') {
return end;
if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
if (precision) {
return end;
}
return end - 2;
}
}
return end;
@@ -338,8 +342,7 @@ bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
// Since String is reference-counted, this at least does not
// create an extra copy.
String doc;
std::getline(is, doc, static_cast<char> EOF);
String doc(std::istreambuf_iterator<char>(is), {});
return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
}
@@ -1409,8 +1412,11 @@ bool OurReader::readToken(Token& token) {
if (features_.allowSingleQuotes_) {
token.type_ = tokenString;
ok = readStringSingleQuote();
break;
} // else fall through
} else {
// If we don't allow single quotes, this is a failure case.
ok = false;
}
break;
case '/':
token.type_ = tokenComment;
ok = readComment();
@@ -2152,7 +2158,7 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const {
if (valid_keys.count(key))
continue;
if (invalid)
(*invalid)[std::move(key)] = *si;
(*invalid)[key] = *si;
else
return false;
}
@@ -2667,7 +2673,7 @@ Value::CZString::CZString(const CZString& other) {
storage_.length_ = other.storage_.length_;
}
Value::CZString::CZString(CZString&& other)
Value::CZString::CZString(CZString&& other) noexcept
: cstr_(other.cstr_), index_(other.index_) {
other.cstr_ = nullptr;
}
@@ -2693,7 +2699,7 @@ Value::CZString& Value::CZString::operator=(const CZString& other) {
return *this;
}
Value::CZString& Value::CZString::operator=(CZString&& other) {
Value::CZString& Value::CZString::operator=(CZString&& other) noexcept {
cstr_ = other.cstr_;
index_ = other.index_;
other.cstr_ = nullptr;
@@ -2841,7 +2847,7 @@ Value::Value(const Value& other) {
dupMeta(other);
}
Value::Value(Value&& other) {
Value::Value(Value&& other) noexcept {
initBasic(nullValue);
swap(other);
}
@@ -2856,7 +2862,7 @@ Value& Value::operator=(const Value& other) {
return *this;
}
Value& Value::operator=(Value&& other) {
Value& Value::operator=(Value&& other) noexcept {
other.swap(*this);
return *this;
}
@@ -3320,7 +3326,8 @@ void Value::resize(ArrayIndex newSize) {
if (newSize == 0)
clear();
else if (newSize > oldSize)
this->operator[](newSize - 1);
for (ArrayIndex i = oldSize; i < newSize; ++i)
(*this)[i];
else {
for (ArrayIndex index = newSize; index < oldSize; ++index) {
value_.map_->erase(index);
@@ -3781,14 +3788,15 @@ bool Value::isObject() const { return type() == objectValue; }
Value::Comments::Comments(const Comments& that)
: ptr_{cloneUnique(that.ptr_)} {}
Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {}
Value::Comments::Comments(Comments&& that) noexcept
: ptr_{std::move(that.ptr_)} {}
Value::Comments& Value::Comments::operator=(const Comments& that) {
ptr_ = cloneUnique(that.ptr_);
return *this;
}
Value::Comments& Value::Comments::operator=(Comments&& that) {
Value::Comments& Value::Comments::operator=(Comments&& that) noexcept {
ptr_ = std::move(that.ptr_);
return *this;
}
@@ -3804,13 +3812,11 @@ String Value::Comments::get(CommentPlacement slot) const {
}
void Value::Comments::set(CommentPlacement slot, String comment) {
if (!ptr_) {
if (slot >= CommentPlacement::numberOfCommentPlacement)
return;
if (!ptr_)
ptr_ = std::unique_ptr<Array>(new Array());
}
// check comments array boundry.
if (slot < CommentPlacement::numberOfCommentPlacement) {
(*ptr_)[slot] = std::move(comment);
}
(*ptr_)[slot] = std::move(comment);
}
void Value::setComment(String comment, CommentPlacement placement) {
@@ -4124,7 +4130,7 @@ Value& Path::make(Value& root) const {
#if !defined(isnan)
// IEEE standard states that NaN values will not compare to themselves
#define isnan(x) (x != x)
#define isnan(x) ((x) != (x))
#endif
#if !defined(__APPLE__)
@@ -4210,16 +4216,18 @@ String valueToString(double value, bool useSpecialFloats,
buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
// strip the zero padding from the right
if (precisionType == PrecisionType::decimalPlaces) {
buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
}
// try to ensure we preserve the fact that this was given to us as a double on
// input
if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
buffer += ".0";
}
// strip the zero padding from the right
if (precisionType == PrecisionType::decimalPlaces) {
buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
buffer.end());
}
return buffer;
}
} // namespace
@@ -4231,11 +4239,11 @@ String valueToString(double value, unsigned int precision,
String valueToString(bool value) { return value ? "true" : "false"; }
static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
assert(s || !n);
return std::any_of(s, s + n, [](unsigned char c) {
return c == '\\' || c == '"' || !std::isprint(c);
return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
});
}
@@ -4326,12 +4334,12 @@ static void appendHex(String& result, unsigned ch) {
result.append("\\u").append(toHex16Bit(ch));
}
static String valueToQuotedStringN(const char* value, unsigned length,
static String valueToQuotedStringN(const char* value, size_t length,
bool emitUTF8 = false) {
if (value == nullptr)
return "";
if (!isAnyCharRequiredQuoting(value, length))
if (!doesAnyCharRequireEscaping(value, length))
return String("\"") + value + "\"";
// We have to walk value and escape any special characters.
// Appending to String is not efficient, but this should be rare.
@@ -4404,7 +4412,7 @@ static String valueToQuotedStringN(const char* value, unsigned length,
}
String valueToQuotedString(const char* value) {
return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
return valueToQuotedStringN(value, strlen(value));
}
// Class Writer
@@ -4453,7 +4461,7 @@ void FastWriter::writeValue(const Value& value) {
char const* end;
bool ok = value.getString(&str, &end);
if (ok)
document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
break;
}
case booleanValue:
@@ -4476,8 +4484,7 @@ void FastWriter::writeValue(const Value& value) {
const String& name = *it;
if (it != members.begin())
document_ += ',';
document_ += valueToQuotedStringN(name.data(),
static_cast<unsigned>(name.length()));
document_ += valueToQuotedStringN(name.data(), name.length());
document_ += yamlCompatibilityEnabled_ ? ": " : ":";
writeValue(value[name]);
}
@@ -4522,7 +4529,7 @@ void StyledWriter::writeValue(const Value& value) {
char const* end;
bool ok = value.getString(&str, &end);
if (ok)
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
else
pushValue("");
break;
@@ -4563,7 +4570,7 @@ void StyledWriter::writeValue(const Value& value) {
}
void StyledWriter::writeArrayValue(const Value& value) {
unsigned size = value.size();
size_t size = value.size();
if (size == 0)
pushValue("[]");
else {
@@ -4572,7 +4579,7 @@ void StyledWriter::writeArrayValue(const Value& value) {
writeWithIndent("[");
indent();
bool hasChildValue = !childValues_.empty();
unsigned index = 0;
ArrayIndex index = 0;
for (;;) {
const Value& childValue = value[index];
writeCommentBeforeValue(childValue);
@@ -4595,7 +4602,7 @@ void StyledWriter::writeArrayValue(const Value& value) {
{
assert(childValues_.size() == size);
document_ += "[ ";
for (unsigned index = 0; index < size; ++index) {
for (size_t index = 0; index < size; ++index) {
if (index > 0)
document_ += ", ";
document_ += childValues_[index];
@@ -4740,7 +4747,7 @@ void StyledStreamWriter::writeValue(const Value& value) {
char const* end;
bool ok = value.getString(&str, &end);
if (ok)
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
else
pushValue("");
break;
@@ -5014,8 +5021,8 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
char const* end;
bool ok = value.getString(&str, &end);
if (ok)
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
emitUTF8_));
pushValue(
valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
else
pushValue("");
break;
@@ -5038,8 +5045,8 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
String const& name = *it;
Value const& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedStringN(
name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
writeWithIndent(
valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
*sout_ << colonSymbol_;
writeValue(childValue);
if (++it == members.end()) {
@@ -5273,7 +5280,7 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const {
if (valid_keys.count(key))
continue;
if (invalid)
(*invalid)[std::move(key)] = *si;
(*invalid)[key] = *si;
else
return false;
}

View File

@@ -59,7 +59,7 @@ To modify settings specific to the current application, invoke the `Properties`
When console applications are launched, the Windows Console Host determines which settings to use by overlaying settings from the above locations.
1. Initialize settings based on hardcoded defaults
1. Initialize settings based on hard-coded defaults
2. Overlay settings specified by the user's configured defaults
3. Overlay application-specific settings from either the registry or the shortcut file, depending on how the application was launched

View File

@@ -380,7 +380,7 @@ Here's the AppxManifest we're using:
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22621.0" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
</Dependencies>
@@ -517,7 +517,7 @@ This is because of a few key lines we already put in the appxmanifest:
```xml
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22621.0" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
</Dependencies>

View File

@@ -203,7 +203,7 @@
},
"intenseTextStyle": {
"default": "bright",
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"description": "Controls how 'intense' text is rendered when unfocused. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"enum": [
"none",
"bold",
@@ -244,7 +244,7 @@
"default": 12,
"description": "Size of the font in points.",
"minimum": 1,
"type": "integer"
"type": "number"
},
"weight": {
"default": "normal",
@@ -2252,7 +2252,7 @@
"default": 12,
"description": "[deprecated] Define 'size' within the 'font' object instead.",
"minimum": 1,
"type": "integer",
"type": "number",
"deprecated": true
},
"fontWeight": {
@@ -2283,6 +2283,17 @@
],
"deprecated": true
},
"intenseTextStyle": {
"default": "bright",
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"enum": [
"none",
"bold",
"bright",
"all"
],
"type": "string"
},
"foreground": {
"$ref": "#/$defs/Color",
"default": "#cccccc",
@@ -2350,7 +2361,8 @@
"description": "Defines the visibility of the scrollbar.",
"enum": [
"visible",
"hidden"
"hidden",
"always"
],
"type": "string"
},
@@ -2384,7 +2396,10 @@
},
"startingDirectory": {
"description": "The directory the shell starts in when it is loaded.",
"type": "string"
"type": [
"string",
"null"
]
},
"suppressApplicationTitle": {
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal.",

View File

@@ -1,7 +1,7 @@
---
author: Mike Griese @zadjii-msft
created on: 2020-5-13
last updated: 2020-08-04
last updated: 2022-11-18
issue id: 1571
---
@@ -76,6 +76,34 @@ There are five `type`s of objects in this menu:
- The `"entries"` property specifies a list of menu entries that will appear
nested under this entry. This can contain other `"type":"folder"` groups as
well!
- The `"inline"` property accepts two values
- `auto`: When the folder only has one entry in it, don't actually create a
nested layer to then menu. Just place the single entry in the layer that
folder would occupy. (Useful for dynamic profile sources with only a
single entry).
- `never`: (**default**) Always create a nested entry, even for a single
sub-item.
- The `allowEmpty` property will force this entry to show up in the menu, even
if it doesn't have any profiles in it. This defaults to `false`, meaning
that folders without any entries in them will just be ignored when
generating the menu. This will be more useful with the `matchProfile` entry,
below.
When this is true, and the folder is empty, we should add a
placeholder `<empty>` entry to the menu, to indicate that no profiles were
in that folder.
- _This setting is probably pretty niche, and not a requirement_. More of a
theoretical suggestion than anything.
- In the case of no entries for this folder, we should make sure to also
reflect the `inline` property:
- `allowEmpty:true`, `inline:auto`: just ignore the entry at all. Don't
add a placeholder to the parent list.
- `allowEmpty:true`, `inline:never`: Add a nested entry, with an
`<empty>` placeholder.
- `allowEmpty:false`, `inline:auto`: just ignore the entry at all. Don't
add a placeholder to the parent list.
- `allowEmpty:false`, `inline:never`: just ignore the entry at all. Don't
add a placeholder to the parent list.
* `"type":"action"`: This represents a menu entry that should execute a specific
`ShortcutAction`.
- the `id` property will specify the global action ID (see [#6899], [#7175])
@@ -97,6 +125,16 @@ There are five `type`s of objects in this menu:
enabling all other profiles to also be accessible.
- The "name" of these entries will simply be the name of the profile
- The "icon" of these entries will simply be the profile's icon
- This won't include any profiles that have been included via `matchProfile`
entries (below)
* `"type": "matchProfile"`: Expands to all the profiles that match a given
string. This lets the user easily specify a whole collection of profiles for a
folder, without needing to add them all manually.
- `"name"`, `"commandline"` or `"source"`: These three properties are used to
filter the list of profiles, based on the matching property in the profile
itself. The value is a string to compare with the corresponding property in
the profile. A full string comparison is done - not a regex or partial
string match.
The "default" new tab menu could be imagined as the following blob of json:
@@ -108,6 +146,42 @@ The "default" new tab menu could be imagined as the following blob of json:
}
```
Alternatively, we could consider something like the following. This would place
CMD, PowerShell, and all PowerShell cores in the root at the top, followed by
nested entries for each subsequent dynamic profile generator.
```jsonc
{
"newTabMenu": [
{ "type":"profile", "profile": "cmd" },
{ "type":"profile", "profile": "Windows PowerShell" },
{ "type": "matchProfile", "source": "Microsoft.Terminal.PowerShellCore" }
{
"type": "folder",
"name": "WSL",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
},
{
"type": "folder",
"name": "Visual Studio",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.VisualStudio" } ]
},
// ... etc for other profile generators
{ "type": "remainingProfiles" }
]
}
```
I might only recommend that for `userDefaults.json`, which is the json files
used as a template for a user's new settings file. This would prevent us from
moving the user's cheese too much, if they're already using the Terminal and
happy with their list as is. Especially consider someone who's default profile
is a WSL distro, which would now need two clicks to get to.
> _note_: We will also want to support the same `{ "key": "SomeResourceString"}`
> syntax used by the Command Palette commands
> for specifying localizable names, if we chose to pursue this route.
### Other considerations
Also considered during the investigation for this feature was re-using the list
@@ -154,6 +228,42 @@ The design chosen in this spec more cleanly separates the responsibilities of
the list of profiles and the contents of the new tab menu. This way, each object
can be defined independent of the structure of the other.
Regarding implementation of `matchProfile` entries: In order to build the menu,
we'll evaluate the entries in the following order:
* all explicit `profile` entries
* then all `matchProfile` entries, using profiles not already specified
* then expand out `remainingProfiles` with anything not found above.
As an example:
```jsonc
{
"newTabMenu": [
{ "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" }
{
"type": "folder",
"name": "WSLs",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
},
{ "type": "remainingProfiles" }
]
}
```
For profiles { "Profile A", "Profile B (WSL)", "Profile C (WSL)" }, This would
expand to:
```
New Tab Button ▽
├─ Profile A
├─ Profile B (WSL)
├─ Profile C (WSL)
└─ WSLs
└─ Profile B (WSL)
└─ Profile C (WSL)
```
## UI/UX Design
See the above _figure 1_.
@@ -289,7 +399,39 @@ And assuming the user has bound:
- Close Tab: `{ "action": "closeTab", "index": "${selectedTab.index}" }`
- Close Other Tabs: `{ "action": "closeTabs", "otherThan": "${selectedTab.index}" }`
- Close Tabs to the Right: `{ "action": "closeTabs", "after": "${selectedTab.index}" }`
* We may want to consider regex, tag-based, or some other type of matching for
`matchProfile` entries in the future. We originally considered using regex for
`matchProfile` by default, but decided instead on full string matches to leave
room for regex matching in the future. Should we chose to pursue something
like that, we should use a settings structure like:
```json
"type": "profileMatch",
"source": { "type": "regex", "value": ".*wsl.*" }
```
* We may want to expand `matchProfile` to match on other properties too. (`title`?)
* We may want to consider adding support for capture groups, e.g.
```json
{
"type": "profileMatch",
"name": { "type": "regex", "value": "^ssh: (.*)" }
}
```
for matching to all your `ssh: ` profiles, but populate the name in the entry
with that first capture group. So, ["ssh: foo", "ssh: bar"] would just expand
to a "foo" and "bar" entry.
## Updates
_February 2022_: Doc updated in response to some discussion in [#11326] and
[#7774]. In those PRs, it became clear that there needs to be a simple way of
collecting up a whole group of profiles automatically for sorting in these
menus. Although discussion centered on how hard it would be for extensions to
provide that customization themselves, the `match` statement was added as a way
to allow the user to easily filter those profiles themselves.
This was something we had originally considered as a "future consideration", but
ultimately deemed it to be out of scope for the initial spec review.
<!-- Footnotes -->
[#2046]: https://github.com/microsoft/terminal/issues/2046
@@ -298,3 +440,5 @@ And assuming the user has bound:
[#3337]: https://github.com/microsoft/terminal/issues/3337
[#6899]: https://github.com/microsoft/terminal/issues/6899
[#7175]: https://github.com/microsoft/terminal/issues/7175
[#11326]: https://github.com/microsoft/terminal/issues/11326
[#7774]: https://github.com/microsoft/terminal/issues/7774

View File

@@ -201,7 +201,7 @@ Concerns:
### Accessibility
Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hardcoded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment.
Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hard-coded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment.
The major players here that I am considering are NVDA, JAWS, and Narrator. As far as I am aware, all of these applications attempt to drive their interactivity through UI Automation where possible. And we have worked with all of these applications in the past in improving their support for both `conhost.exe` and the Windows Terminal product. I have relatively high confidence that we will be able to work with them again to help update these assistive products to understand the new UI delegation, if necessary.

View File

@@ -76,7 +76,7 @@ These are in accordance with ConHost's keyboard selection model.
This idea was abandoned due to several reasons:
1. Keyboard selection should be a standard way to interact with a terminal across all consumers (i.e. WPF control, etc.)
2. There isn't really another set of key bindings that makes sense for this. We already hardcoded <kbd>ESC</kbd> as a way to clear the selection. This is just an extension of that.
2. There isn't really another set of key bindings that makes sense for this. We already hard-coded <kbd>ESC</kbd> as a way to clear the selection. This is just an extension of that.
3. Adding 12 conditionally effective key bindings takes the spot of 12 potential non-conditional key bindings. It would be nice if a different key binding could be set when the selection is not active, but that makes the settings design much more complicated.
4. 12 new items in the command palette is also pretty excessive.
5. If proven wrong when this is in WT Preview, we can revisit this and make them customizable then. It's better to add the ability to customize it later than take it away.

View File

@@ -115,7 +115,7 @@ greater detail below:
### Default Settings
We'll have a static version of the "Default" file **hardcoded within the
We'll have a static version of the "Default" file **hard-coded within the
application package**. This `defaults.json` file will live within the
application's package, which will prevent users from being able to edit it.
@@ -128,19 +128,19 @@ won't actually be generated, but because it's shipped with our app, it'll be
overridden each time the app is updated. "Auto-generated" should be good enough
to indicate to users that it should not be modified.
Because the `defaults.json` file is hardcoded within our application, we can use
Because the `defaults.json` file is hard-coded within our application, we can use
its text directly, without loading the file from disk. This should help save
some startup time, as we'll only need to load the user settings from disk.
When we make changes to the default settings, or we make changes to the settings
schema, we should make sure that we update the hardcoded `defaults.json` with
schema, we should make sure that we update the hard-coded `defaults.json` with
the new values. That way, the `defaults.json` file will always have the complete
set of settings in it.
### Layering settings
When we load the settings, we'll do it in three stages. First, we'll deserialize
the default settings that we've hardcoded. We'll then generate any profiles that
the default settings that we've hard-coded. We'll then generate any profiles that
might come from dynamic profile sources. Then, we'll intelligently layer the
user's setting upon those we've already loaded. If a user wants to make changes
to some objects, like the default profiles, we'll need to make sure to load from

View File

@@ -158,7 +158,7 @@ For `settings.json`, `_globals` will only hold the values set in `settings.json`
This process becomes a bit more complex for `Profile` because it can fallback in the following order:
1. `settings.json` profile
2. `settings.json` `profiles.defaults`
3. (if a dynamic profile) the hardcoded value in the dynamic profile generator
3. (if a dynamic profile) the hard-coded value in the dynamic profile generator
4. `defaults.json` profile
`CascadiaSettings` must do the following...
@@ -280,7 +280,7 @@ TerminalApp will construct and reference a `CascadiaSettings settings` as follow
and layers the settings.json data on top of it.
- check for errors/warnings, and handle them appropriately
This will be different from the current model which has the settings.json path hardcoded, and is simplified
This will be different from the current model which has the settings.json path hard-coded, and is simplified
to a `LoadAll()` call wrapped in error handlers.
**NOTE:** This model allows us to layer even more settings files on top of the existing Terminal Settings

View File

@@ -1,2 +0,0 @@
@echo off
git branch | D:\dev\private\OpenConsole\bin\x64\Debug\Scratch.exe --prefix "git checkout "

View File

@@ -6,6 +6,7 @@
#include <iostream>
#include <memory>
#include <cassert>
#include <limits>
#ifdef USE_INTERVAL_TREE_NAMESPACE
namespace interval_tree

View File

@@ -8,7 +8,7 @@ That provenance file is automatically read and inventoried by Microsoft systems
## What should be done to update this in the future?
1. Go to ekg/intervaltreerepository on GitHub.
1. Go to the ekg/intervaltree repository on GitHub.
2. Take the file IntervalTree.h wholesale and drop it into the directory here.
3. Don't change anything about it.
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.

View File

@@ -6,7 +6,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/ekg/intervaltree",
"commitHash": "b90527f9e6d51cd36ecbb50429e4524d3a418ea5"
"commitHash": "aa5937755000f1cd007402d03b6f7ce4427c5d21"
}
}
}

View File

@@ -6,7 +6,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/kimwalisch/libpopcnt",
"commitHash": "043a99fba31121a70bcb2f589faa17f534ae6085"
"commitHash": "c49987e90e56191c399cab881ab87b5daecc9b8e"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>EchoCon</RootNamespace>
<WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@@ -27,7 +27,7 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>

View File

@@ -136,12 +136,12 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>

View File

@@ -147,18 +147,18 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
Well, App.xbf is hardcoded by the framework to be found at the resource ROOT.
Well, App.xbf is hard-coded by the framework to be found at the resource ROOT.
To make that happen, we have to disable the prepending of the project name
to the App xaml files.
-->

View File

@@ -91,12 +91,12 @@
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<ItemDefinitionGroup>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.1" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View File

@@ -144,12 +144,12 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!-- Override GetPackagingOutputs to roll up all our dependencies.

View File

@@ -2,6 +2,6 @@
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.1" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
</packages>

View File

@@ -5,11 +5,6 @@
#include "MidiAudio.hpp"
#include "../terminal/parser/stateMachine.hpp"
#include <dsound.h>
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dsound.lib")
using Microsoft::WRL::ComPtr;
using namespace std::chrono_literals;
@@ -18,66 +13,48 @@ using namespace std::chrono_literals;
constexpr auto WAVE_SIZE = 16u;
constexpr auto WAVE_DATA = std::array<byte, WAVE_SIZE>{ 128, 159, 191, 223, 255, 223, 191, 159, 128, 96, 64, 32, 0, 32, 64, 96 };
MidiAudio::MidiAudio(HWND windowHandle)
void MidiAudio::_initialize(HWND windowHandle) noexcept
{
if (SUCCEEDED(DirectSoundCreate8(nullptr, &_directSound, nullptr)))
_hwnd = windowHandle;
_directSoundModule.reset(LoadLibraryExW(L"dsound.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (_directSoundModule)
{
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
if (const auto createFunction = GetProcAddressByFunctionDeclaration(_directSoundModule.get(), DirectSoundCreate8))
{
_createBuffers();
if (SUCCEEDED(createFunction(nullptr, &_directSound, nullptr)))
{
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
{
_createBuffers();
}
}
}
}
}
MidiAudio::~MidiAudio() noexcept
void MidiAudio::BeginSkip() 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.
const auto lock = std::unique_lock{ _inUseMutex };
}
catch (...)
{
// If the lock fails, we'll just have to live with the consequences.
}
_skip.SetEvent();
}
void MidiAudio::Initialize()
void MidiAudio::EndSkip() noexcept
{
_shutdownFuture = _shutdownPromise.get_future();
_skip.ResetEvent();
}
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
void MidiAudio::PlayNote(HWND windowHandle, const int noteNumber, const int velocity, const std::chrono::milliseconds duration) noexcept
try
{
if (_skip.is_signaled())
{
return;
}
if (_hwnd != windowHandle)
{
_initialize(windowHandle);
}
const auto& buffer = _buffers.at(_activeBufferIndex);
if (velocity && buffer)
{
@@ -100,10 +77,10 @@ try
buffer->SetCurrentPosition((_lastBufferPosition + 12) % WAVE_SIZE);
}
// 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);
// By waiting on the skip event with a maximum duration of the note, we'll
// either be paused for the appropriate amount of time, or we'll break out early
// because BeginSkip() was called. This happens for Ctrl+C or during shutdown.
_skip.wait(::base::saturated_cast<DWORD>(duration.count()));
if (velocity && buffer)
{

View File

@@ -12,8 +12,6 @@ Abstract:
#pragma once
#include <array>
#include <future>
#include <mutex>
struct IDirectSound8;
struct IDirectSoundBuffer;
@@ -21,26 +19,20 @@ struct IDirectSoundBuffer;
class MidiAudio
{
public:
MidiAudio(HWND windowHandle);
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;
void BeginSkip() noexcept;
void EndSkip() noexcept;
void PlayNote(HWND windowHandle, const int noteNumber, const int velocity, const std::chrono::milliseconds duration) noexcept;
private:
void _initialize(HWND windowHandle) noexcept;
void _createBuffers() noexcept;
Microsoft::WRL::ComPtr<IDirectSound8> _directSound;
std::array<Microsoft::WRL::ComPtr<IDirectSoundBuffer>, 2> _buffers;
wil::slim_event_manual_reset _skip;
HWND _hwnd = nullptr;
wil::unique_hmodule _directSoundModule;
wil::com_ptr<IDirectSound8> _directSound;
std::array<wil::com_ptr<IDirectSoundBuffer>, 2> _buffers;
size_t _activeBufferIndex = 0;
DWORD _lastBufferPosition = 0;
std::promise<void> _shutdownPromise;
std::future<void> _shutdownFuture;
std::mutex _inUseMutex;
};

View File

@@ -0,0 +1,3 @@
BUILD_PASS1_CONSUMES= \
onecore\windows\vcpkg|PASS1 \

View File

@@ -25,7 +25,9 @@ Abstract:
#endif
// Windows Header Files:
#include <windows.h>
#include <Windows.h>
#include <mmeapi.h>
#include <dsound.h>
// clang-format on

View File

@@ -1,134 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "AttrRow.hpp"
// Routine Description:
// - constructor
// Arguments:
// - cchRowWidth - the length of the default text attribute
// - attr - the default text attribute
// Return Value:
// - constructed object
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
// Arguments:
// - attr - The default text attributes to use on text in this row.
void ATTR_ROW::Reset(const TextAttribute attr)
{
_data.replace(0, _data.size(), attr);
}
// Routine Description:
// - Takes an existing row of attributes, and changes the length so that it fills the NewWidth.
// If the new size is bigger, then the last attr is extended to fill the NewWidth.
// If the new size is smaller, the runs are cut off to fit.
// Arguments:
// - oldWidth - The original width of the row.
// - newWidth - The new width of the row.
// Return Value:
// - <none>, throws exceptions on failures.
void ATTR_ROW::Resize(const til::CoordType newWidth)
{
_data.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
}
// Routine Description:
// - returns a copy of the TextAttribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the text attribute at column
// Note:
// - will throw on error
TextAttribute ATTR_ROW::GetAttrByColumn(const til::CoordType column) const
{
return _data.at(gsl::narrow<uint16_t>(column));
}
// Routine Description:
// - Finds the hyperlink IDs present in this row and returns them
// Return value:
// - The hyperlink IDs present in this row
std::vector<uint16_t> ATTR_ROW::GetHyperlinks() const
{
std::vector<uint16_t> ids;
for (const auto& run : _data.runs())
{
if (run.value.IsHyperlink())
{
ids.emplace_back(run.value.GetHyperlinkId());
}
}
return ids;
}
// Routine Description:
// - Sets the attributes (colors) of all character positions from the given position through the end of the row.
// Arguments:
// - iStart - Starting index position within the row
// - attr - Attribute (color) to fill remaining characters with
// Return Value:
// - <none>
bool ATTR_ROW::SetAttrToEnd(const til::CoordType beginIndex, const TextAttribute attr)
{
_data.replace(gsl::narrow<uint16_t>(beginIndex), _data.size(), attr);
return true;
}
// Method Description:
// - Replaces all runs in the row with the given toBeReplacedAttr with the new
// attribute replaceWith.
// Arguments:
// - toBeReplacedAttr - the attribute to replace in this row.
// - replaceWith - the new value for the matching runs' attributes.
// Return Value:
// - <none>
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith)
{
_data.replace_values(toBeReplacedAttr, replaceWith);
}
// Routine Description:
// - Takes an attribute, and merges it into this row from beginIndex (inclusive) to endIndex (exclusive).
// - For example, if the current row was [{4, BLUE}], the merge arguments were
// { beginIndex = 1, endIndex = 3, newAttr = RED }, then the row would modified to be
// [{ 1, BLUE}, {2, RED}, {1, BLUE}].
// Arguments:
// - beginIndex, endIndex: The [beginIndex, endIndex) range that's to be replaced with newAttr.
// - newAttr: The attribute to merge into this row.
// Return Value:
// - <none>
void ATTR_ROW::Replace(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
{
_data.replace(gsl::narrow<uint16_t>(beginIndex), gsl::narrow<uint16_t>(endIndex), newAttr);
}
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
{
return _data.begin();
}
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
{
return _data.end();
}
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
{
return _data.cbegin();
}
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
{
return _data.cend();
}
bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept
{
return a._data == b._data;
}

View File

@@ -1,68 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- AttrRow.hpp
Abstract:
- contains data structure for the attributes of one row of screen buffer
Author(s):
- Michael Niksa (miniksa) 10-Apr-2014
- Paul Campbell (paulcam) 10-Apr-2014
Revision History:
- From components of output.h/.c
by Therese Stowell (ThereseS) 1990-1991
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
--*/
#pragma once
#include "til/rle.h"
#include "TextAttribute.hpp"
class ATTR_ROW final
{
using rle_vector = til::small_rle<TextAttribute, uint16_t, 1>;
public:
using const_iterator = rle_vector::const_iterator;
ATTR_ROW(til::CoordType width, TextAttribute attr);
~ATTR_ROW() = default;
ATTR_ROW(const ATTR_ROW&) = default;
ATTR_ROW& operator=(const ATTR_ROW&) = default;
ATTR_ROW(ATTR_ROW&&)
noexcept = default;
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
TextAttribute GetAttrByColumn(til::CoordType column) const;
std::vector<uint16_t> GetHyperlinks() const;
bool SetAttrToEnd(til::CoordType beginIndex, TextAttribute attr);
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith);
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;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
friend class ROW;
private:
void Reset(const TextAttribute attr);
rle_vector _data;
#ifdef UNIT_TESTING
friend class CommonState;
#endif
};

View File

@@ -1,281 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRow.hpp"
#include "unicode.hpp"
#include "Row.hpp"
// Routine Description:
// - constructor
// Arguments:
// - rowWidth - the size (in wchar_t) of the char and attribute rows
// - pParent - the parent ROW
// Return Value:
// - instantiated object
// 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(til::CoordType rowWidth, ROW* const pParent) noexcept :
_data(rowWidth, value_type()),
_pParent{ FAIL_FAST_IF_NULL(pParent) }
{
}
#pragma warning(pop)
// Routine Description:
// - gets the size of the row, in glyph cells
// Arguments:
// - <none>
// Return Value:
// - the size of the row
til::CoordType CharRow::size() const noexcept
{
return gsl::narrow_cast<til::CoordType>(_data.size());
}
// Routine Description:
// - Sets all properties of the CharRowBase to default values
// Arguments:
// - sRowWidth - The width of the row.
// Return Value:
// - <none>
void CharRow::Reset() noexcept
{
for (auto& cell : _data)
{
cell.Reset();
}
}
// Routine Description:
// - resizes the width of the CharRowBase
// Arguments:
// - 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 til::CoordType newSize) noexcept
{
try
{
const value_type insertVals;
_data.resize(newSize, insertVals);
}
CATCH_RETURN();
return S_OK;
}
typename CharRow::iterator CharRow::begin() noexcept
{
return _data.begin();
}
typename CharRow::const_iterator CharRow::cbegin() const noexcept
{
return _data.cbegin();
}
typename CharRow::iterator CharRow::end() noexcept
{
return _data.end();
}
typename CharRow::const_iterator CharRow::cend() const noexcept
{
return _data.cend();
}
// Routine Description:
// - Inspects the current internal string to find the left edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated left boundary of the internal string.
til::CoordType CharRow::MeasureLeft() const noexcept
{
auto it = _data.cbegin();
while (it != _data.cend() && it->IsSpace())
{
++it;
}
return gsl::narrow_cast<til::CoordType>(it - _data.cbegin());
}
// Routine Description:
// - Inspects the current internal string to find the right edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated right boundary of the internal string.
til::CoordType CharRow::MeasureRight() const
{
auto it = _data.crbegin();
while (it != _data.crend() && it->IsSpace())
{
++it;
}
return gsl::narrow_cast<til::CoordType>(_data.crend() - it);
}
void CharRow::ClearCell(const til::CoordType column)
{
_data.at(column).Reset();
}
// Routine Description:
// - Tells you whether or not this row contains any valid text.
// Arguments:
// - <none>
// Return Value:
// - True if there is valid text in this row. False otherwise.
bool CharRow::ContainsText() const noexcept
{
for (const auto& cell : _data)
{
if (!cell.IsSpace())
{
return true;
}
}
return false;
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
const DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column) const
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column)
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - resets text data at column
// Arguments:
// - column - column index to clear text data from
// Return Value:
// - <none>
// Note: will throw exception if column is out of bounds
void CharRow::ClearGlyph(const til::CoordType column)
{
_data.at(column).EraseChars();
}
// Routine Description:
// - returns text data at column as a const reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
const CharRow::reference CharRow::GlyphAt(const til::CoordType column) const
{
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
return { const_cast<CharRow&>(*this), column };
}
// Routine Description:
// - returns text data at column as a reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
CharRow::reference CharRow::GlyphAt(const til::CoordType column)
{
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
return { *this, column };
}
std::wstring CharRow::GetText(const til::CoordType start) const
{
std::wstring wstr;
wstr.reserve(_data.size());
for (til::CoordType i = start; i < gsl::narrow_cast<til::CoordType>(_data.size()); ++i)
{
const auto glyph = GlyphAt(i);
if (!DbcsAttrAt(i).IsTrailing())
{
for (const auto wch : glyph)
{
wstr.push_back(wch);
}
}
}
return wstr;
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - 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 til::CoordType column, const std::wstring_view wordDelimiters) const
{
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)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
}
// Routine Description:
// - calculates the key used by the given column of the char row to store glyph data in UnicodeStorage
// Arguments:
// - column - the column to generate the key for
// Return Value:
// - the til::point key for data access from UnicodeStorage for the column
til::point CharRow::GetStorageKey(const til::CoordType column) const noexcept
{
return { column, _pParent->GetId() };
}
// Routine Description:
// - Updates the pointer to the parent row (which might change if we shuffle the rows around)
// Arguments:
// - pParent - Pointer to the parent row
void CharRow::UpdateParent(ROW* const pParent)
{
_pParent = FAIL_FAST_IF_NULL(pParent);
}

View File

@@ -1,117 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRow.hpp
Abstract:
- contains data structure for UCS2 encoded character data of a row
Author(s):
- Michael Niksa (miniksa) 10-Apr-2014
- Paul Campbell (paulcam) 10-Apr-2014
Revision History:
- From components of output.h/.c
by Therese Stowell (ThereseS) 1990-1991
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
--*/
#pragma once
#include <til/small_vector.h>
#include "DbcsAttribute.hpp"
#include "CharRowCellReference.hpp"
#include "CharRowCell.hpp"
#include "UnicodeStorage.hpp"
class ROW;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
// the characters of one row of screen buffer
// we keep the following values so that we don't write
// more pixels to the screen than we have to:
// left is initialized to screenbuffer width. right is
// initialized to zero.
//
// [ foo.bar 12-12-61 ]
// ^ ^ ^ ^
// | | | |
// Chars Left Right end of Chars buffer
class CharRow final
{
public:
using glyph_type = wchar_t;
using value_type = CharRowCell;
using iterator = til::small_vector<value_type, 120>::iterator;
using const_iterator = til::small_vector<value_type, 120>::const_iterator;
using const_reverse_iterator = til::small_vector<value_type, 120>::const_reverse_iterator;
using reference = CharRowCellReference;
CharRow(til::CoordType rowWidth, ROW* const pParent) noexcept;
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 til::CoordType column) const;
DbcsAttribute& DbcsAttrAt(const til::CoordType column);
void ClearGlyph(const til::CoordType column);
const DelimiterClass DelimiterClassAt(const til::CoordType column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const reference GlyphAt(const til::CoordType column) const;
reference GlyphAt(const til::CoordType column);
// iterators
iterator begin() noexcept;
const_iterator cbegin() const noexcept;
const_iterator begin() const noexcept { return cbegin(); }
iterator end() noexcept;
const_iterator cend() const noexcept;
const_iterator end() const noexcept { return cend(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
til::point GetStorageKey(const til::CoordType column) const noexcept;
void UpdateParent(ROW* const pParent);
friend CharRowCellReference;
friend class ROW;
private:
void Reset() noexcept;
void ClearCell(const til::CoordType column);
std::wstring GetText(const til::CoordType start = 0) const;
protected:
// storage for glyph data and dbcs attributes
til::small_vector<value_type, 120> _data;
// ROW that this CharRow belongs to
ROW* _pParent;
};
template<typename InputIt1, typename InputIt2>
void OverwriteColumns(InputIt1 startChars, InputIt1 endChars, InputIt2 startAttrs, CharRow::iterator outIt)
{
std::transform(startChars,
endChars,
startAttrs,
outIt,
[](const wchar_t wch, const DbcsAttribute attr) {
return CharRow::value_type{ wch, attr };
});
}

View File

@@ -1,73 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRowCell.hpp"
#include "unicode.hpp"
// default glyph value, used for resetting the character data portion of a cell
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
// Routine Description:
// - "erases" the glyph. really sets it back to the default "empty" value
void CharRowCell::EraseChars() noexcept
{
if (_attr.IsGlyphStored())
{
_attr.SetGlyphStored(false);
}
_wch = DefaultValue;
}
// Routine Description:
// - resets this object back to the defaults it would have from the default constructor
void CharRowCell::Reset() noexcept
{
_attr.Reset();
_wch = DefaultValue;
}
// Routine Description:
// - checks if cell contains a space glyph
// Return Value:
// - true if cell contains a space glyph, false otherwise
bool CharRowCell::IsSpace() const noexcept
{
return !_attr.IsGlyphStored() && _wch == UNICODE_SPACE;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
DbcsAttribute& CharRowCell::DbcsAttr() noexcept
{
return _attr;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
const DbcsAttribute& CharRowCell::DbcsAttr() const noexcept
{
return _attr;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
wchar_t& CharRowCell::Char() noexcept
{
return _wch;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
const wchar_t& CharRowCell::Char() const noexcept
{
return _wch;
}

View File

@@ -1,65 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCell.hpp
Abstract:
- data structure for one cell of a char row. contains the char data for one
coordinate position in the output buffer (leading/trailing information and
the char itself.
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "unicode.hpp"
#if (defined(_M_IX86) || defined(_M_AMD64))
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
// to a rather large amount of useless memory allocated. so instead, pack CharRowCell by bytes instead of words.
#pragma pack(push, 1)
#endif
class CharRowCell final
{
public:
CharRowCell() noexcept = default;
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
:
_wch(wch),
_attr(attr)
{
}
void EraseChars() noexcept;
void Reset() noexcept;
bool IsSpace() const noexcept;
DbcsAttribute& DbcsAttr() noexcept;
const DbcsAttribute& DbcsAttr() const noexcept;
wchar_t& Char() noexcept;
const wchar_t& Char() const noexcept;
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
private:
wchar_t _wch{ UNICODE_SPACE };
DbcsAttribute _attr{};
};
#if (defined(_M_IX86) || defined(_M_AMD64))
#pragma pack(pop)
#endif
constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept
{
return (a._wch == b._wch &&
a._attr == b._attr);
}

View File

@@ -1,136 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
#include "CharRow.hpp"
// Routine Description:
// - assignment operator. will store extended glyph data in a separate storage location
// Arguments:
// - chars - the glyph data to store
void CharRowCellReference::operator=(const std::wstring_view chars)
{
THROW_HR_IF(E_INVALIDARG, chars.empty());
if (chars.size() == 1)
{
_cellData().Char() = chars.front();
_cellData().DbcsAttr().SetGlyphStored(false);
}
else
{
auto& storage = _parent.GetUnicodeStorage();
const auto key = _parent.GetStorageKey(_index);
storage.StoreGlyph(key, { chars.cbegin(), chars.cend() });
_cellData().DbcsAttr().SetGlyphStored(true);
}
}
// Routine Description:
// - implicit conversion to vector<wchar_t> operator.
// Return Value:
// - std::vector<wchar_t> of the glyph data in the referenced cell
CharRowCellReference::operator std::wstring_view() const
{
return _glyphData();
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
CharRowCell& CharRowCellReference::_cellData()
{
return _parent._data.at(_index);
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
const CharRowCell& CharRowCellReference::_cellData() const
{
return _parent._data.at(_index);
}
// Routine Description:
// - the glyph data of the referenced cell
// Return Value:
// - the glyph data
std::wstring_view CharRowCellReference::_glyphData() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& text = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return { text.data(), text.size() };
}
else
{
return { &_cellData().Char(), 1 };
}
}
// Routine Description:
// - gets read-only iterator to the beginning of the glyph data
// Return Value:
// - iterator of the glyph data
CharRowCellReference::const_iterator CharRowCellReference::begin() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
return _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index)).data();
}
else
{
return &_cellData().Char();
}
}
// Routine Description:
// - get read-only iterator to the end of the glyph data
// Return Value:
// - end iterator of the glyph data
#pragma warning(push)
#pragma warning(disable : 26481)
// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class
CharRowCellReference::const_iterator CharRowCellReference::end() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& chars = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return chars.data() + chars.size();
}
else
{
return &_cellData().Char() + 1;
}
}
#pragma warning(pop)
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
{
const auto& dbcsAttr = ref._cellData().DbcsAttr();
if (glyph.size() == 1 && dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() > 1 && !dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() == 1 && !dbcsAttr.IsGlyphStored())
{
return ref._cellData().Char() == glyph.front();
}
else
{
const auto& chars = ref._parent.GetUnicodeStorage().GetText(ref._parent.GetStorageKey(ref._index));
return chars == glyph;
}
}
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref)
{
return ref == glyph;
}

View File

@@ -1,63 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCellReference.hpp
Abstract:
- reference class for the glyph data of a char row cell
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCell.hpp"
#include <utility>
class CharRow;
class CharRowCellReference final
{
public:
using const_iterator = const wchar_t*;
CharRowCellReference(CharRow& parent, const til::CoordType index) noexcept :
_parent{ parent },
_index{ index }
{
}
~CharRowCellReference() = default;
CharRowCellReference(const CharRowCellReference&) noexcept = default;
CharRowCellReference(CharRowCellReference&&) noexcept = default;
void operator=(const CharRowCellReference&) = delete;
void operator=(CharRowCellReference&&) = delete;
void operator=(const std::wstring_view chars);
operator std::wstring_view() const;
const_iterator begin() const;
const_iterator end() const;
friend bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
friend bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);
private:
// what char row the object belongs to
CharRow& _parent;
// the index of the cell in the parent char row
til::CoordType _index;
CharRowCell& _cellData();
const CharRowCell& _cellData() const;
std::wstring_view _glyphData() const;
};
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);

View File

@@ -16,129 +16,22 @@ Revision History:
#pragma once
class DbcsAttribute final
enum class DbcsAttribute : uint8_t
{
public:
enum class Attribute : BYTE
{
Single = 0x00,
Leading = 0x01,
Trailing = 0x02
};
DbcsAttribute() noexcept :
_attribute{ Attribute::Single },
_glyphStored{ false }
{
}
DbcsAttribute(const Attribute attribute) noexcept :
_attribute{ attribute },
_glyphStored{ false }
{
}
constexpr bool IsSingle() const noexcept
{
return _attribute == Attribute::Single;
}
constexpr bool IsLeading() const noexcept
{
return _attribute == Attribute::Leading;
}
constexpr bool IsTrailing() const noexcept
{
return _attribute == Attribute::Trailing;
}
constexpr bool IsDbcs() const noexcept
{
return IsLeading() || IsTrailing();
}
constexpr bool IsGlyphStored() const noexcept
{
return _glyphStored;
}
void SetGlyphStored(const bool stored) noexcept
{
_glyphStored = stored;
}
void SetSingle() noexcept
{
_attribute = Attribute::Single;
}
void SetLeading() noexcept
{
_attribute = Attribute::Leading;
}
void SetTrailing() noexcept
{
_attribute = Attribute::Trailing;
}
void Reset() noexcept
{
SetSingle();
SetGlyphStored(false);
}
WORD GeneratePublicApiAttributeFormat() const noexcept
{
WORD publicAttribute = 0;
if (IsLeading())
{
WI_SetFlag(publicAttribute, COMMON_LVB_LEADING_BYTE);
}
if (IsTrailing())
{
WI_SetFlag(publicAttribute, COMMON_LVB_TRAILING_BYTE);
}
return publicAttribute;
}
static DbcsAttribute FromPublicApiAttributeFormat(WORD publicAttribute)
{
// it's not valid to be both a leading and trailing byte
if (WI_AreAllFlagsSet(publicAttribute, COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE))
{
THROW_HR(E_INVALIDARG);
}
DbcsAttribute attr;
if (WI_IsFlagSet(publicAttribute, COMMON_LVB_LEADING_BYTE))
{
attr.SetLeading();
}
else if (WI_IsFlagSet(publicAttribute, COMMON_LVB_TRAILING_BYTE))
{
attr.SetTrailing();
}
return attr;
}
friend constexpr bool operator==(const DbcsAttribute& a, const DbcsAttribute& b) noexcept;
private:
Attribute _attribute : 2;
bool _glyphStored : 1;
#ifdef UNIT_TESTING
friend class TextBufferTests;
#endif
Single,
Leading,
Trailing,
};
constexpr bool operator==(const DbcsAttribute& a, const DbcsAttribute& b) noexcept
constexpr WORD GeneratePublicApiAttributeFormat(DbcsAttribute attribute) noexcept
{
return a._attribute == b._attribute;
switch (attribute)
{
case DbcsAttribute::Leading:
return COMMON_LVB_LEADING_BYTE;
case DbcsAttribute::Trailing:
return COMMON_LVB_TRAILING_BYTE;
default:
return 0;
}
}
static_assert(sizeof(DbcsAttribute) == sizeof(BYTE), "DbcsAttribute should be one byte big. if this changes then it needs "
"either an implicit conversion to a BYTE or an update to all places "
"that assume it's a byte big");

View File

@@ -13,7 +13,7 @@ Abstract:
#pragma once
enum class LineRendition
enum class LineRendition : uint8_t
{
SingleWidth,
DoubleWidth,

View File

@@ -80,7 +80,7 @@ private:
// basic_string contains a small storage internally so we don't need
// to worry about heap allocation for short strings.
std::wstring _text;
DbcsAttribute _dbcsAttribute;
DbcsAttribute _dbcsAttribute = DbcsAttribute::Single;
TextAttribute _textAttribute;
TextAttributeBehavior _behavior;

View File

@@ -5,8 +5,9 @@
#include "OutputCellIterator.hpp"
#include <til/unicode.h>
#include "../../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../inc/conattrs.hpp"
@@ -240,13 +241,10 @@ OutputCellIterator& OutputCellIterator::operator++()
{
if (!_TryMoveTrailing())
{
if (_currentView.DbcsAttr().IsTrailing())
if (_currentView.DbcsAttr() == DbcsAttribute::Trailing)
{
auto dbcsAttr = _currentView.DbcsAttr();
dbcsAttr.SetLeading();
_currentView = OutputCellView(_currentView.Chars(),
dbcsAttr,
DbcsAttribute::Leading,
_currentView.TextAttr(),
_currentView.TextAttrBehavior());
}
@@ -336,13 +334,10 @@ const OutputCellView* OutputCellIterator::operator->() const noexcept
// - False if this wasn't applicable and the caller should update the view.
bool OutputCellIterator::_TryMoveTrailing() noexcept
{
if (_currentView.DbcsAttr().IsLeading())
if (_currentView.DbcsAttr() == DbcsAttribute::Leading)
{
auto dbcsAttr = _currentView.DbcsAttr();
dbcsAttr.SetTrailing();
_currentView = OutputCellView(_currentView.Chars(),
dbcsAttr,
DbcsAttribute::Trailing,
_currentView.TextAttr(),
_currentView.TextAttrBehavior());
return true;
@@ -398,13 +393,8 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
const TextAttribute attr,
const TextAttributeBehavior behavior)
{
const auto glyph = Utf16Parser::ParseNext(view);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(glyph))
{
dbcsAttr.SetLeading();
}
const auto glyph = til::utf16_next(view);
const auto dbcsAttr = IsGlyphFullWidth(glyph) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, attr, behavior);
}
@@ -420,13 +410,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
{
dbcsAttr.SetLeading();
}
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
}
@@ -457,13 +441,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noe
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
{
dbcsAttr.SetLeading();
}
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
}
@@ -498,14 +476,14 @@ OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noe
{
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
DbcsAttribute dbcsAttr;
DbcsAttribute dbcsAttr = DbcsAttribute::Single;
if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_LEADING_BYTE))
{
dbcsAttr.SetLeading();
dbcsAttr = DbcsAttribute::Leading;
}
else if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_TRAILING_BYTE))
{
dbcsAttr.SetTrailing();
dbcsAttr = DbcsAttribute::Trailing;
}
const TextAttribute textAttr(charInfo.Attributes);

View File

@@ -40,20 +40,7 @@ OutputCellView::OutputCellView(const std::wstring_view view,
// - Count of column cells on the screen
til::CoordType OutputCellView::Columns() const noexcept
{
if (DbcsAttr().IsSingle())
{
return 1;
}
else if (DbcsAttr().IsLeading())
{
return 2;
}
else if (DbcsAttr().IsTrailing())
{
return 1;
}
return 1;
return DbcsAttr() == DbcsAttribute::Leading ? 2 : 1;
}
// Routine Description:

View File

@@ -56,7 +56,7 @@ public:
private:
std::wstring_view _view;
DbcsAttribute _dbcsAttr;
DbcsAttribute _dbcsAttr = DbcsAttribute::Single;
TextAttribute _textAttr;
TextAttributeBehavior _behavior;
};

View File

@@ -3,29 +3,129 @@
#include "precomp.h"
#include "Row.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
// The STL is missing a std::iota_n analogue for std::iota, so I made my own.
template<typename OutIt, typename Diff, typename T>
constexpr OutIt iota_n(OutIt dest, Diff count, T val)
{
for (; count; --count, ++dest, ++val)
{
*dest = val;
}
return dest;
}
// ROW::ReplaceCharacters needs to calculate `val + count` after
// calling iota_n() and this function achieves both things at once.
template<typename OutIt, typename Diff, typename T>
constexpr OutIt iota_n_mut(OutIt dest, Diff count, T& val)
{
for (; count; --count, ++dest, ++val)
{
*dest = val;
}
return dest;
}
// Same as std::fill, but purpose-built for very small `last - first`
// where a trivial loop outperforms vectorization.
template<typename FwdIt, typename T>
constexpr FwdIt fill_small(FwdIt first, FwdIt last, const T val)
{
for (; first != last; ++first)
{
*first = val;
}
return first;
}
// Same as std::fill_n, but purpose-built for very small `count`
// where a trivial loop outperforms vectorization.
template<typename OutIt, typename Diff, typename T>
constexpr OutIt fill_n_small(OutIt dest, Diff count, const T val)
{
for (; count; --count, ++dest)
{
*dest = val;
}
return dest;
}
// Same as std::copy_n, but purpose-built for very short `count`
// where a trivial loop outperforms vectorization.
template<typename InIt, typename Diff, typename OutIt>
constexpr OutIt copy_n_small(InIt first, Diff count, OutIt dest)
{
for (; count; --count, ++dest, ++first)
{
*dest = *first;
}
return dest;
}
// Routine Description:
// - constructor
// Arguments:
// - rowId - the row index in the text buffer
// - rowWidth - the width of the row, cell elements
// - fillAttribute - the default text attribute
// - pParent - the text buffer that this row belongs to
// Return Value:
// - constructed object
ROW::ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
_id{ rowId },
_rowWidth{ rowWidth },
_charRow{ rowWidth, this },
_attrRow{ rowWidth, fillAttribute },
_lineRendition{ LineRendition::SingleWidth },
_wrapForced{ false },
_doubleBytePadded{ false },
_pParent{ pParent }
ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute) :
_charsBuffer{ charsBuffer },
_chars{ charsBuffer, rowWidth },
_charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u },
_attr{ rowWidth, fillAttribute },
_columnCount{ rowWidth }
{
if (_chars.data())
{
_init();
}
}
void swap(ROW& lhs, ROW& rhs) noexcept
{
std::swap(lhs._charsBuffer, rhs._charsBuffer);
std::swap(lhs._charsHeap, rhs._charsHeap);
std::swap(lhs._chars, rhs._chars);
std::swap(lhs._charOffsets, rhs._charOffsets);
std::swap(lhs._attr, rhs._attr);
std::swap(lhs._columnCount, rhs._columnCount);
std::swap(lhs._lineRendition, rhs._lineRendition);
std::swap(lhs._wrapForced, rhs._wrapForced);
std::swap(lhs._doubleBytePadded, rhs._doubleBytePadded);
}
void ROW::SetWrapForced(const bool wrap) noexcept
{
_wrapForced = wrap;
}
bool ROW::WasWrapForced() const noexcept
{
return _wrapForced;
}
void ROW::SetDoubleBytePadded(const bool doubleBytePadded) noexcept
{
_doubleBytePadded = doubleBytePadded;
}
bool ROW::WasDoubleBytePadded() const noexcept
{
return _doubleBytePadded;
}
void ROW::SetLineRendition(const LineRendition lineRendition) noexcept
{
_lineRendition = lineRendition;
}
LineRendition ROW::GetLineRendition() const noexcept
{
return _lineRendition;
}
// Routine Description:
@@ -34,42 +134,99 @@ ROW::ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAt
// - Attr - The default attribute (color) to fill
// Return Value:
// - <none>
bool ROW::Reset(const TextAttribute Attr)
void ROW::Reset(const TextAttribute& attr)
{
_charsHeap.reset();
_chars = { _charsBuffer, _columnCount };
_attr = { _columnCount, attr };
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_charRow.Reset();
try
{
_attrRow.Reset(Attr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}
return true;
_init();
}
void ROW::_init() noexcept
{
std::fill_n(_chars.begin(), _columnCount, UNICODE_SPACE);
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
}
// Routine Description:
// - resizes ROW to new width
// Arguments:
// - width - the new width, in cells
// Return Value:
// - S_OK if successful, otherwise relevant error
[[nodiscard]] HRESULT ROW::Resize(const til::CoordType width)
// - charsBuffer - a new backing buffer to use for _charsBuffer
// - charOffsetsBuffer - a new backing buffer to use for _charOffsets
// - rowWidth - the new width, in cells
// - fillAttribute - the attribute to use for any newly added, trailing cells
void ROW::Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute)
{
RETURN_IF_FAILED(_charRow.Resize(width));
try
// A default-constructed ROW has no cols/chars to copy.
// It can be detected by the lack of a _charsBuffer (among others).
//
// Otherwise, this block figures out how much we can copy into the new `rowWidth`.
uint16_t colsToCopy = 0;
uint16_t charsToCopy = 0;
if (_charsBuffer)
{
_attrRow.Resize(width);
colsToCopy = std::min(rowWidth, _columnCount);
// Safety: colsToCopy is [0, _columnCount].
charsToCopy = _uncheckedCharOffset(colsToCopy);
// Safety: colsToCopy is [0, _columnCount] due to colsToCopy != 0.
for (; colsToCopy != 0 && _uncheckedIsTrailer(colsToCopy); --colsToCopy)
{
}
}
CATCH_RETURN();
_rowWidth = width;
// If we grow the row width, we have to append a bunch of whitespace.
// `trailingWhitespace` stores that amount.
// Safety: The preceding block left colsToCopy in the range [0, rowWidth].
const uint16_t trailingWhitespace = rowWidth - colsToCopy;
return S_OK;
// Allocate memory for the new `_chars` array.
// Use the provided charsBuffer if possible, otherwise allocate a `_charsHeap`.
std::unique_ptr<wchar_t[]> charsHeap;
std::span chars{ charsBuffer, rowWidth };
const std::span charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u };
if (const uint16_t charsCapacity = charsToCopy + trailingWhitespace; charsCapacity > rowWidth)
{
charsHeap = std::make_unique_for_overwrite<wchar_t[]>(charsCapacity);
chars = { charsHeap.get(), charsCapacity };
}
// Copy chars and charOffsets over.
{
const auto it = std::copy_n(_chars.begin(), charsToCopy, chars.begin());
std::fill_n(it, trailingWhitespace, L' ');
}
{
const auto it = std::copy_n(_charOffsets.begin(), colsToCopy, charOffsets.begin());
// The _charOffsets array is 1 wider than newWidth indicates.
// This is because the extra column contains the past-the-end index into _chars.
iota_n(it, trailingWhitespace + 1u, charsToCopy);
}
_charsBuffer = charsBuffer;
_charsHeap = std::move(charsHeap);
_chars = chars;
_charOffsets = charOffsets;
_columnCount = rowWidth;
// .resize_trailing_extent() doesn't work if the vector is empty,
// since there's no trailing item that could be extended.
if (_attr.empty())
{
_attr = { rowWidth, fillAttribute };
}
else
{
_attr.resize_trailing_extent(rowWidth);
}
}
void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth)
{
_attr = attr;
_attr.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
}
// Routine Description:
@@ -78,20 +235,10 @@ bool ROW::Reset(const TextAttribute Attr)
// - column - 0-indexed column index
// Return Value:
// - <none>
void ROW::ClearColumn(const til::CoordType column)
void ROW::ClearCell(const til::CoordType column)
{
THROW_HR_IF(E_INVALIDARG, column >= _charRow.size());
_charRow.ClearCell(column);
}
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
static constexpr std::wstring_view space{ L" " };
ReplaceCharacters(column, 1, space);
}
// Routine Description:
@@ -103,17 +250,17 @@ 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 til::CoordType index, const std::optional<bool> wrap, std::optional<til::CoordType> limitRight)
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType columnBegin, 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());
THROW_HR_IF(E_INVALIDARG, columnBegin >= size());
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= size());
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
const auto finalColumnInRow = limitRight.value_or(size() - 1);
auto currentColor = it->TextAttr();
uint16_t colorUses = 0;
auto colorStarts = gsl::narrow_cast<uint16_t>(index);
auto colorStarts = gsl::narrow_cast<uint16_t>(columnBegin);
auto currentIndex = colorStarts;
while (it && currentIndex <= finalColumnInRow)
@@ -131,7 +278,7 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
{
// Otherwise, commit this color into the run and save off the new one.
// Now commit the new color runs into the attr row.
_attrRow.Replace(colorStarts, currentIndex, currentColor);
_attr.replace(colorStarts, currentIndex, currentColor);
currentColor = it->TextAttr();
colorUses = 1;
colorStarts = currentIndex;
@@ -141,31 +288,47 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
{
const auto fillingFirstColumn = currentIndex == 0;
const auto fillingLastColumn = currentIndex == finalColumnInRow;
const auto attr = it->DbcsAttr();
const auto& chars = it->Chars();
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
// for the trailing byte coming up before writing it.
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
switch (attr)
{
_charRow.ClearCell(currentIndex);
}
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
{
_charRow.ClearCell(currentIndex);
SetDoubleBytePadded(true);
}
// Otherwise, copy the data given and increment the iterator.
else
{
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
_charRow.GlyphAt(currentIndex) = it->Chars();
case DbcsAttribute::Leading:
if (fillingLastColumn)
{
// The wide char doesn't fit. Pad with whitespace.
// Don't increment the iterator. Instead we'll return from this function and the
// caller can call WriteCells() again on the next row with the same iterator position.
ClearCell(currentIndex);
SetDoubleBytePadded(true);
}
else
{
ReplaceCharacters(currentIndex, 2, chars);
++it;
}
break;
case DbcsAttribute::Trailing:
// Handling the trailing half of wide chars ensures that we correctly restore
// wide characters when a user backs up and restores the viewport via CHAR_INFOs.
if (fillingFirstColumn)
{
// The wide char doesn't fit. Pad with whitespace.
// Ignore the character. There's no correct alternative way to handle this situation.
ClearCell(currentIndex);
}
else
{
ReplaceCharacters(currentIndex - 1, 2, chars);
}
++it;
break;
default:
ReplaceCharacters(currentIndex, 1, chars);
++it;
break;
}
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
@@ -191,8 +354,334 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
// Now commit the final color into the attr row
if (colorUses)
{
_attrRow.Replace(colorStarts, currentIndex, currentColor);
_attr.replace(colorStarts, currentIndex, currentColor);
}
return it;
}
bool ROW::SetAttrToEnd(const til::CoordType columnBegin, const TextAttribute attr)
{
_attr.replace(_clampedColumnInclusive(columnBegin), _attr.size(), attr);
return true;
}
void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
{
_attr.replace(_clampedColumnInclusive(beginIndex), _clampedColumnInclusive(endIndex), newAttr);
}
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
{
const auto colBeg = _clampedUint16(columnBegin);
const auto colEnd = _clampedUint16(columnBegin + width);
if (colBeg >= colEnd || colEnd > _columnCount || chars.empty())
{
return;
}
// Safety:
// * colBeg is now [0, _columnCount)
// * colEnd is now (colBeg, _columnCount]
// Algorithm explanation
//
// Task:
// Replace the characters in cells [colBeg, colEnd) with a single `width`-wide glyph consisting of `chars`.
//
// Problem:
// Imagine that we have the following ROW contents:
// "xxyyzz"
// xx, yy, zz are 2 cell wide glyphs. We want to insert a 2 cell wide glyph ww at colBeg 1:
// ^^
// ww
// An incorrect result would be:
// "xwwyzz"
// The half cut off x and y glyph wouldn't make much sense, so we need to fill them with whitespace:
// " ww zz"
//
// Solution:
// Given the range we want to replace [colBeg, colEnd), we "extend" it to encompass leading (preceding)
// and trailing wide glyphs we partially overwrite resulting in the range [colExtBeg, colExtEnd), where
// colExtBeg <= colBeg and colExtEnd >= colEnd. In other words, the to be replaced range has been "extended".
// The amount of leading whitespace we need to insert is thus colBeg - colExtBeg
// and the amount of trailing whitespace colExtEnd - colEnd.
// Extend range downwards (leading whitespace)
uint16_t colExtBeg = colBeg;
// Safety: colExtBeg is [0, _columnCount], because colBeg is.
const uint16_t chExtBeg = _uncheckedCharOffset(colExtBeg);
// Safety: colExtBeg remains [0, _columnCount] due to colExtBeg != 0.
for (; colExtBeg != 0 && _uncheckedIsTrailer(colExtBeg); --colExtBeg)
{
}
// Extend range upwards (trailing whitespace)
uint16_t colExtEnd = colEnd;
// Safety: colExtEnd cannot be incremented past _columnCount, because the last
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
for (; _uncheckedIsTrailer(colExtEnd); ++colExtEnd)
{
}
// Safety: After the previous loop colExtEnd is [0, _columnCount].
const uint16_t chExtEnd = _uncheckedCharOffset(colExtEnd);
const uint16_t leadingSpaces = colBeg - colExtBeg;
const uint16_t trailingSpaces = colExtEnd - colEnd;
const size_t chExtEndNew = chars.size() + leadingSpaces + trailingSpaces + chExtBeg;
if (chExtEndNew != chExtEnd)
{
_resizeChars(colExtEnd, chExtBeg, chExtEnd, chExtEndNew);
}
// Add leading/trailing whitespace and copy chars
{
auto it = _chars.begin() + chExtBeg;
it = fill_n_small(it, leadingSpaces, L' ');
it = copy_n_small(chars.begin(), chars.size(), it);
it = fill_n_small(it, trailingSpaces, L' ');
}
// Update char offsets with leading/trailing whitespace and the chars columns.
{
auto chPos = chExtBeg;
auto it = _charOffsets.begin() + colExtBeg;
it = iota_n_mut(it, leadingSpaces, chPos);
*it++ = chPos;
it = fill_small(it, _charOffsets.begin() + colEnd, gsl::narrow_cast<uint16_t>(chPos | CharOffsetsTrailer));
chPos = gsl::narrow_cast<uint16_t>(chPos + chars.size());
it = iota_n_mut(it, trailingSpaces, chPos);
}
}
// This function represents the slow path of ReplaceCharacters(),
// as it reallocates the backing buffer and shifts the char offsets.
// The parameters are difficult to explain, but their names are identical to
// local variables in ReplaceCharacters() which I've attempted to document there.
void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew)
{
const auto diff = chExtEndNew - chExtEnd;
const auto currentLength = _charSize();
const auto newLength = currentLength + diff;
if (newLength <= _chars.size())
{
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, _chars.begin() + chExtEndNew);
}
else
{
const auto minCapacity = std::min<size_t>(UINT16_MAX, _chars.size() + (_chars.size() >> 1));
const auto newCapacity = gsl::narrow<uint16_t>(std::max(newLength, minCapacity));
auto charsHeap = std::make_unique_for_overwrite<wchar_t[]>(newCapacity);
const std::span chars{ charsHeap.get(), newCapacity };
std::copy_n(_chars.begin(), chExtBeg, chars.begin());
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, chars.begin() + chExtEndNew);
_charsHeap = std::move(charsHeap);
_chars = chars;
}
auto it = _charOffsets.begin() + colExtEnd;
const auto end = _charOffsets.end();
for (; it != end; ++it)
{
*it = gsl::narrow_cast<uint16_t>(*it + diff);
}
}
const til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() const noexcept
{
return _attr;
}
TextAttribute ROW::GetAttrByColumn(const til::CoordType column) const
{
return _attr.at(_clampedUint16(column));
}
std::vector<uint16_t> ROW::GetHyperlinks() const
{
std::vector<uint16_t> ids;
for (const auto& run : _attr.runs())
{
if (run.value.IsHyperlink())
{
ids.emplace_back(run.value.GetHyperlinkId());
}
}
return ids;
}
uint16_t ROW::size() const noexcept
{
return _columnCount;
}
til::CoordType ROW::MeasureLeft() const noexcept
{
const auto text = GetText();
const auto beg = text.begin();
const auto end = text.end();
auto it = beg;
for (; it != end; ++it)
{
if (*it != L' ')
{
break;
}
}
return gsl::narrow_cast<til::CoordType>(it - beg);
}
til::CoordType ROW::MeasureRight() const noexcept
{
const auto text = GetText();
const auto beg = text.begin();
const auto end = text.end();
auto it = end;
for (; it != beg; --it)
{
// it[-1] is safe as `it` is always greater than `beg` (loop invariant).
if (til::at(it, -1) != L' ')
{
break;
}
}
// We're supposed to return the measurement in cells and not characters
// and therefore simply calculating `it - beg` would be wrong.
//
// An example: The row is 10 cells wide and `it` points to the second character.
// `it - beg` would return 1, but it's possible it's actually 1 wide glyph and 8 whitespace.
return gsl::narrow_cast<til::CoordType>(_columnCount - (end - it));
}
bool ROW::ContainsText() const noexcept
{
const auto text = GetText();
const auto beg = text.begin();
const auto end = text.end();
auto it = beg;
for (; it != end; ++it)
{
if (*it != L' ')
{
return true;
}
}
return false;
}
std::wstring_view ROW::GlyphAt(til::CoordType column) const noexcept
{
auto col = _clampedColumn(column);
// Safety: col is [0, _columnCount).
const auto beg = _uncheckedCharOffset(col);
// Safety: col cannot be incremented past _columnCount, because the last
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
while (_uncheckedIsTrailer(++col))
{
}
// Safety: col is now (0, _columnCount].
const auto end = _uncheckedCharOffset(col);
return { _chars.begin() + beg, _chars.begin() + end };
}
DbcsAttribute ROW::DbcsAttrAt(til::CoordType column) const noexcept
{
const auto col = _clampedColumn(column);
auto attr = DbcsAttribute::Single;
// Safety: col is [0, _columnCount).
if (_uncheckedIsTrailer(col))
{
attr = DbcsAttribute::Trailing;
}
// Safety: col+1 is [1, _columnCount].
else if (_uncheckedIsTrailer(::base::strict_cast<size_t>(col) + 1u))
{
attr = DbcsAttribute::Leading;
}
return { attr };
}
std::wstring_view ROW::GetText() const noexcept
{
return { _chars.data(), _charSize() };
}
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
{
const auto col = _clampedColumn(column);
// Safety: col is [0, _columnCount).
const auto glyph = _uncheckedChar(_uncheckedCharOffset(col));
if (glyph <= L' ')
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
template<typename T>
constexpr uint16_t ROW::_clampedUint16(T v) noexcept
{
return static_cast<uint16_t>(std::max(T{ 0 }, std::min(T{ 65535 }, v)));
}
template<typename T>
constexpr uint16_t ROW::_clampedColumn(T v) const noexcept
{
return static_cast<uint16_t>(std::max(T{ 0 }, std::min<T>(_columnCount - 1u, v)));
}
template<typename T>
constexpr uint16_t ROW::_clampedColumnInclusive(T v) const noexcept
{
return static_cast<uint16_t>(std::max(T{ 0 }, std::min<T>(_columnCount, v)));
}
// Safety: off must be [0, _charSize()].
wchar_t ROW::_uncheckedChar(size_t off) const noexcept
{
return til::at(_chars, off);
}
uint16_t ROW::_charSize() const noexcept
{
// Safety: _charOffsets is an array of `_columnCount + 1` entries.
return til::at(_charOffsets, _columnCount);
}
// Safety: col must be [0, _columnCount].
uint16_t ROW::_uncheckedCharOffset(size_t col) const noexcept
{
return til::at(_charOffsets, col) & CharOffsetsMask;
}
// Safety: col must be [0, _columnCount].
bool ROW::_uncheckedIsTrailer(size_t col) const noexcept
{
return WI_IsFlagSet(til::at(_charOffsets, col), CharOffsetsTrailer);
}

View File

@@ -20,50 +20,68 @@ Revision History:
#pragma once
#include "AttrRow.hpp"
#include <span>
#include <til/rle.h>
#include "LineRendition.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
#include "CharRow.hpp"
#include "UnicodeStorage.hpp"
class TextBuffer;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
class ROW final
{
public:
ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
ROW() = default;
ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
til::CoordType size() const noexcept { return _rowWidth; }
ROW(const ROW& other) = delete;
ROW& operator=(const ROW& other) = delete;
void SetWrapForced(const bool wrap) noexcept { _wrapForced = wrap; }
bool WasWrapForced() const noexcept { return _wrapForced; }
explicit ROW(ROW&& other) = default;
ROW& operator=(ROW&& other) = default;
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept { _doubleBytePadded = doubleBytePadded; }
bool WasDoubleBytePadded() const noexcept { return _doubleBytePadded; }
friend void swap(ROW& lhs, ROW& rhs) noexcept;
const CharRow& GetCharRow() const noexcept { return _charRow; }
CharRow& GetCharRow() noexcept { return _charRow; }
void SetWrapForced(const bool wrap) noexcept;
bool WasWrapForced() const noexcept;
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept;
bool WasDoubleBytePadded() const noexcept;
void SetLineRendition(const LineRendition lineRendition) noexcept;
LineRendition GetLineRendition() const noexcept;
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
void Reset(const TextAttribute& attr);
void Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
void TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth);
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
void ClearCell(til::CoordType column);
OutputCellIterator WriteCells(OutputCellIterator it, til::CoordType columnBegin, std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
bool SetAttrToEnd(til::CoordType columnBegin, TextAttribute attr);
void ReplaceAttributes(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
void ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars);
til::CoordType GetId() const noexcept { return _id; }
void SetId(const til::CoordType id) noexcept { _id = id; }
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
TextAttribute GetAttrByColumn(til::CoordType column) const;
std::vector<uint16_t> GetHyperlinks() const;
uint16_t size() const noexcept;
til::CoordType MeasureLeft() const noexcept;
til::CoordType MeasureRight() const noexcept;
bool ContainsText() const noexcept;
std::wstring_view GlyphAt(til::CoordType column) const noexcept;
DbcsAttribute DbcsAttrAt(til::CoordType column) const noexcept;
std::wstring_view GetText() const noexcept;
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
bool Reset(const TextAttribute Attr);
[[nodiscard]] HRESULT Resize(const til::CoordType width);
void ClearColumn(const til::CoordType column);
std::wstring GetText(const til::CoordType start = 0) const { return _charRow.GetText(start); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
OutputCellIterator WriteCells(OutputCellIterator it, const til::CoordType index, const std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
auto AttrBegin() const noexcept { return _attr.begin(); }
auto AttrEnd() const noexcept { return _attr.end(); }
#ifdef UNIT_TESTING
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
@@ -71,23 +89,84 @@ public:
#endif
private:
CharRow _charRow;
ATTR_ROW _attrRow;
LineRendition _lineRendition;
til::CoordType _id;
til::CoordType _rowWidth;
// To simplify the detection of wide glyphs, we don't just store the simple character offset as described
// for _charOffsets. Instead we use the most significant bit to indicate whether any column is the
// trailing half of a wide glyph. This simplifies many implementation details via _uncheckedIsTrailer.
static constexpr uint16_t CharOffsetsTrailer = 0x8000;
static constexpr uint16_t CharOffsetsMask = 0x7fff;
template<typename T>
static constexpr uint16_t _clampedUint16(T v) noexcept;
template<typename T>
constexpr uint16_t _clampedColumn(T v) const noexcept;
template<typename T>
constexpr uint16_t _clampedColumnInclusive(T v) const noexcept;
wchar_t _uncheckedChar(size_t off) const noexcept;
uint16_t _charSize() const noexcept;
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
bool _uncheckedIsTrailer(size_t col) const noexcept;
void _init() noexcept;
void _resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew);
// These fields are a bit "wasteful", but it makes all this a bit more robust against
// programming errors during initial development (which is when this comment was written).
// * _chars and _charsHeap are redundant
// If _charsHeap is stored in _chars, we can still infer that
// _chars was allocated on the heap if _chars != _charsBuffer.
// * _chars doesn't need a size_t size()
// The size may never exceed an uint16_t anyways.
// * _charOffsets doesn't need a size() at all
// The length is already stored in _columns.
// Most text uses only a single wchar_t per codepoint / grapheme cluster.
// That's why TextBuffer allocates a large blob of virtual memory which we can use as
// a simplified chars buffer, without having to allocate any additional heap memory.
// _charsBuffer fits _columnCount characters at most.
wchar_t* _charsBuffer = nullptr;
// ...but if this ROW needs to store more than _columnCount characters
// then it will allocate a larger string on the heap and store it here.
// The capacity of this string on the heap is stored in _chars.size().
std::unique_ptr<wchar_t[]> _charsHeap;
// _chars either refers to our _charsBuffer or _charsHeap, defaulting to the former.
// _chars.size() is NOT the length of the string, but rather its capacity.
// _charOffsets[_columnCount] stores the length.
std::span<wchar_t> _chars;
// _charOffsets accelerates indexing into the above _chars string given a terminal column,
// by storing the character index/offset at which a column's text in _chars starts.
// It stores 1 more item than this row is wide, allowing it to store the
// past-the-end offset, which is thus equal to the length of the string.
//
// For instance given a 4 column ROW containing "abcd" it would store 01234,
// because each of "abcd" are 1 column wide and 1 wchar_t per column.
// Given "a\u732Bd" it would store 01123, because "\u732B" is a wide glyph
// and "11" indicates that both column 1 and 2 start at &_chars[1] (= wide glyph).
// The fact that the next offset is 2 tells us that the glyph is 1 wchar_t long.
// Given "a\uD83D\uDE00d" ("\uD83D\uDE00" is an Emoji) it would store 01134,
// because while it's 2 cells wide as indicated by 2 offsets that are identical (11),
// the next offset is 3, which indicates that the glyph is 3-1 = 2 wchar_t long.
//
// In other words, _charOffsets tells us both the width in chars and width in columns.
// See CharOffsetsTrailer for more information.
std::span<uint16_t> _charOffsets;
// _attr is a run-length-encoded vector of TextAttribute with a decompressed
// length equal to _columnCount (= 1 TextAttribute per column).
til::small_rle<TextAttribute, uint16_t, 1> _attr;
// The width of the row in visual columns.
uint16_t _columnCount = 0;
// Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes.
LineRendition _lineRendition = LineRendition::SingleWidth;
// 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;
bool _wrapForced = false;
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line
bool _doubleBytePadded;
TextBuffer* _pParent; // non ownership pointer
bool _doubleBytePadded = false;
};
#ifdef UNIT_TESTING
constexpr bool operator==(const ROW& a, const ROW& b) noexcept
{
// comparison is only used in the tests; this should suffice.
return (a._pParent == b._pParent &&
a._id == b._id);
return a._charsBuffer == b._charsBuffer;
}
#endif

View File

@@ -7,7 +7,7 @@
// Keeping TextColor compact helps us keeping TextAttribute compact,
// which in turn ensures that our buffer memory usage is low.
static_assert(sizeof(TextAttribute) == 14);
static_assert(sizeof(TextAttribute) == 12);
static_assert(alignof(TextAttribute) == 2);
// Ensure that we can memcpy() and memmove() the struct for performance.
static_assert(std::is_trivially_copyable_v<TextAttribute>);
@@ -116,7 +116,7 @@ WORD TextAttribute::GetLegacyAttributes() const noexcept
{
const auto fgIndex = _foreground.GetLegacyIndex(s_legacyDefaultForeground);
const auto bgIndex = _background.GetLegacyIndex(s_legacyDefaultBackground);
const WORD metaAttrs = _wAttrLegacy & META_ATTRS;
const WORD metaAttrs = static_cast<WORD>(_attrs) & USED_META_ATTRS;
const auto brighten = IsIntense() && _foreground.CanBeBrightened();
return fgIndex | (bgIndex << 4) | metaAttrs | (brighten ? FOREGROUND_INTENSITY : 0);
}
@@ -217,151 +217,151 @@ void TextAttribute::SetHyperlinkId(uint16_t id) noexcept
_hyperlinkId = id;
}
bool TextAttribute::IsLeadingByte() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_LEADING_BYTE);
}
bool TextAttribute::IsTrailingByte() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_LEADING_BYTE);
}
bool TextAttribute::IsTopHorizontalDisplayed() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL);
return WI_IsFlagSet(_attrs, CharacterAttributes::TopGridline);
}
bool TextAttribute::IsBottomHorizontalDisplayed() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_UNDERSCORE);
return WI_IsFlagSet(_attrs, CharacterAttributes::BottomGridline);
}
bool TextAttribute::IsLeftVerticalDisplayed() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_LVERTICAL);
return WI_IsFlagSet(_attrs, CharacterAttributes::LeftGridline);
}
bool TextAttribute::IsRightVerticalDisplayed() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL);
return WI_IsFlagSet(_attrs, CharacterAttributes::RightGridline);
}
void TextAttribute::SetLeftVerticalDisplayed(const bool isDisplayed) noexcept
{
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_LVERTICAL, isDisplayed);
WI_UpdateFlag(_attrs, CharacterAttributes::LeftGridline, isDisplayed);
}
void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept
{
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL, isDisplayed);
WI_UpdateFlag(_attrs, CharacterAttributes::RightGridline, isDisplayed);
}
bool TextAttribute::IsIntense() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Intense);
return WI_IsFlagSet(_attrs, CharacterAttributes::Intense);
}
bool TextAttribute::IsFaint() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Faint);
return WI_IsFlagSet(_attrs, CharacterAttributes::Faint);
}
bool TextAttribute::IsItalic() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Italics);
return WI_IsFlagSet(_attrs, CharacterAttributes::Italics);
}
bool TextAttribute::IsBlinking() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Blinking);
return WI_IsFlagSet(_attrs, CharacterAttributes::Blinking);
}
bool TextAttribute::IsInvisible() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Invisible);
return WI_IsFlagSet(_attrs, CharacterAttributes::Invisible);
}
bool TextAttribute::IsCrossedOut() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::CrossedOut);
return WI_IsFlagSet(_attrs, CharacterAttributes::CrossedOut);
}
bool TextAttribute::IsUnderlined() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Underlined);
return WI_IsFlagSet(_attrs, CharacterAttributes::Underlined);
}
bool TextAttribute::IsDoublyUnderlined() const noexcept
{
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::DoublyUnderlined);
return WI_IsFlagSet(_attrs, CharacterAttributes::DoublyUnderlined);
}
bool TextAttribute::IsOverlined() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL);
return WI_IsFlagSet(_attrs, CharacterAttributes::TopGridline);
}
bool TextAttribute::IsReverseVideo() const noexcept
{
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO);
return WI_IsFlagSet(_attrs, CharacterAttributes::ReverseVideo);
}
bool TextAttribute::IsProtected() const noexcept
{
return WI_IsFlagSet(_attrs, CharacterAttributes::Protected);
}
void TextAttribute::SetIntense(bool isIntense) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Intense, isIntense);
WI_UpdateFlag(_attrs, CharacterAttributes::Intense, isIntense);
}
void TextAttribute::SetFaint(bool isFaint) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Faint, isFaint);
WI_UpdateFlag(_attrs, CharacterAttributes::Faint, isFaint);
}
void TextAttribute::SetItalic(bool isItalic) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Italics, isItalic);
WI_UpdateFlag(_attrs, CharacterAttributes::Italics, isItalic);
}
void TextAttribute::SetBlinking(bool isBlinking) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Blinking, isBlinking);
WI_UpdateFlag(_attrs, CharacterAttributes::Blinking, isBlinking);
}
void TextAttribute::SetInvisible(bool isInvisible) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Invisible, isInvisible);
WI_UpdateFlag(_attrs, CharacterAttributes::Invisible, isInvisible);
}
void TextAttribute::SetCrossedOut(bool isCrossedOut) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::CrossedOut, isCrossedOut);
WI_UpdateFlag(_attrs, CharacterAttributes::CrossedOut, isCrossedOut);
}
void TextAttribute::SetUnderlined(bool isUnderlined) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Underlined, isUnderlined);
WI_UpdateFlag(_attrs, CharacterAttributes::Underlined, isUnderlined);
}
void TextAttribute::SetDoublyUnderlined(bool isDoublyUnderlined) noexcept
{
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::DoublyUnderlined, isDoublyUnderlined);
WI_UpdateFlag(_attrs, CharacterAttributes::DoublyUnderlined, isDoublyUnderlined);
}
void TextAttribute::SetOverlined(bool isOverlined) noexcept
{
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL, isOverlined);
WI_UpdateFlag(_attrs, CharacterAttributes::TopGridline, isOverlined);
}
void TextAttribute::SetReverseVideo(bool isReversed) noexcept
{
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO, isReversed);
WI_UpdateFlag(_attrs, CharacterAttributes::ReverseVideo, isReversed);
}
void TextAttribute::SetProtected(bool isProtected) noexcept
{
WI_UpdateFlag(_attrs, CharacterAttributes::Protected, isProtected);
}
// Routine Description:
// - swaps foreground and background color
void TextAttribute::Invert() noexcept
{
WI_ToggleFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO);
WI_ToggleFlag(_attrs, CharacterAttributes::ReverseVideo);
}
void TextAttribute::SetDefaultForeground() noexcept
@@ -375,11 +375,11 @@ void TextAttribute::SetDefaultBackground() noexcept
}
// Method description:
// - Resets only the meta and extended attributes
void TextAttribute::SetDefaultMetaAttrs() noexcept
// - Resets only the rendition character attributes, which includes everything
// except the Protected attribute.
void TextAttribute::SetDefaultRenditionAttributes() noexcept
{
_extendedAttrs = ExtendedAttributes::Normal;
_wAttrLegacy = 0;
_attrs &= ~CharacterAttributes::Rendition;
}
// Method Description:
@@ -398,10 +398,11 @@ bool TextAttribute::BackgroundIsDefault() const noexcept
}
// Routine Description:
// - Resets the meta and extended attributes, which is what the VT standard
// requires for most erasing and filling operations.
// - Resets the character attributes, which is what the VT standard
// requires for most erasing and filling operations. In modern
// applications it is also expected that hyperlinks are erased.
void TextAttribute::SetStandardErase() noexcept
{
SetDefaultMetaAttrs();
_attrs = CharacterAttributes::Normal;
_hyperlinkId = 0;
}

View File

@@ -31,31 +31,26 @@ class TextAttribute final
{
public:
constexpr TextAttribute() noexcept :
_wAttrLegacy{ 0 },
_attrs{ CharacterAttributes::Normal },
_foreground{},
_background{},
_extendedAttrs{ ExtendedAttributes::Normal },
_hyperlinkId{ 0 }
{
}
explicit constexpr TextAttribute(const WORD wLegacyAttr) noexcept :
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
_attrs{ gsl::narrow_cast<WORD>(wLegacyAttr & USED_META_ATTRS) },
_foreground{ gsl::at(s_legacyForegroundColorMap, wLegacyAttr & FG_ATTRS) },
_background{ gsl::at(s_legacyBackgroundColorMap, (wLegacyAttr & BG_ATTRS) >> 4) },
_extendedAttrs{ ExtendedAttributes::Normal },
_hyperlinkId{ 0 }
{
// If we're given lead/trailing byte information with the legacy color, strip it.
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
}
constexpr TextAttribute(const COLORREF rgbForeground,
const COLORREF rgbBackground) noexcept :
_wAttrLegacy{ 0 },
_attrs{ CharacterAttributes::Normal },
_foreground{ rgbForeground },
_background{ rgbBackground },
_extendedAttrs{ ExtendedAttributes::Normal },
_hyperlinkId{ 0 }
{
}
@@ -64,8 +59,6 @@ public:
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
WORD GetLegacyAttributes() const noexcept;
bool IsLeadingByte() const noexcept;
bool IsTrailingByte() const noexcept;
bool IsTopHorizontalDisplayed() const noexcept;
bool IsBottomHorizontalDisplayed() const noexcept;
bool IsLeftVerticalDisplayed() const noexcept;
@@ -97,6 +90,7 @@ public:
bool IsDoublyUnderlined() const noexcept;
bool IsOverlined() const noexcept;
bool IsReverseVideo() const noexcept;
bool IsProtected() const noexcept;
void SetIntense(bool isIntense) noexcept;
void SetFaint(bool isFaint) noexcept;
@@ -108,10 +102,15 @@ public:
void SetDoublyUnderlined(bool isDoublyUnderlined) noexcept;
void SetOverlined(bool isOverlined) noexcept;
void SetReverseVideo(bool isReversed) noexcept;
void SetProtected(bool isProtected) noexcept;
constexpr ExtendedAttributes GetExtendedAttributes() const noexcept
constexpr void SetCharacterAttributes(const CharacterAttributes attrs) noexcept
{
return _extendedAttrs;
_attrs = attrs;
}
constexpr CharacterAttributes GetCharacterAttributes() const noexcept
{
return _attrs;
}
bool IsHyperlink() const noexcept;
@@ -132,7 +131,7 @@ public:
void SetDefaultForeground() noexcept;
void SetDefaultBackground() noexcept;
void SetDefaultMetaAttrs() noexcept;
void SetDefaultRenditionAttributes() noexcept;
bool BackgroundIsDefault() const noexcept;
@@ -149,38 +148,33 @@ public:
const auto checkForeground = (inverted != IsReverseVideo());
return !IsAnyGridLineEnabled() && // grid lines have a visual representation
// crossed out, doubly and singly underlined have a visual representation
WI_AreAllFlagsClear(_extendedAttrs, ExtendedAttributes::CrossedOut | ExtendedAttributes::DoublyUnderlined | ExtendedAttributes::Underlined) &&
WI_AreAllFlagsClear(_attrs, CharacterAttributes::CrossedOut | CharacterAttributes::DoublyUnderlined | CharacterAttributes::Underlined) &&
// hyperlinks have a visual representation
!IsHyperlink() &&
// all other attributes do not have a visual representation
(_wAttrLegacy & META_ATTRS) == (other._wAttrLegacy & META_ATTRS) &&
_attrs == other._attrs &&
((checkForeground && _foreground == other._foreground) ||
(!checkForeground && _background == other._background)) &&
_extendedAttrs == other._extendedAttrs &&
IsHyperlink() == other.IsHyperlink();
}
constexpr bool IsAnyGridLineEnabled() const noexcept
{
return WI_IsAnyFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE);
return WI_IsAnyFlagSet(_attrs, CharacterAttributes::TopGridline | CharacterAttributes::LeftGridline | CharacterAttributes::RightGridline | CharacterAttributes::BottomGridline);
}
constexpr bool HasAnyExtendedAttributes() const noexcept
constexpr bool HasAnyVisualAttributes() const noexcept
{
return GetExtendedAttributes() != ExtendedAttributes::Normal ||
IsAnyGridLineEnabled() ||
GetHyperlinkId() != 0 ||
IsReverseVideo();
return GetCharacterAttributes() != CharacterAttributes::Normal || GetHyperlinkId() != 0;
}
private:
static std::array<TextColor, 16> s_legacyForegroundColorMap;
static std::array<TextColor, 16> s_legacyBackgroundColorMap;
uint16_t _wAttrLegacy; // sizeof: 2, alignof: 2
CharacterAttributes _attrs; // sizeof: 2, alignof: 2
uint16_t _hyperlinkId; // sizeof: 2, alignof: 2
TextColor _foreground; // sizeof: 4, alignof: 1
TextColor _background; // sizeof: 4, alignof: 1
ExtendedAttributes _extendedAttrs; // sizeof: 2, alignof: 2
#ifdef UNIT_TESTING
friend class TextBufferTests;
@@ -213,12 +207,11 @@ namespace WEX
static WEX::Common::NoThrowString ToString(const TextAttribute& attr)
{
return WEX::Common::NoThrowString().Format(
L"{FG:%s,BG:%s,intense:%d,wLegacy:(0x%04x),ext:(0x%02x)}",
L"{FG:%s,BG:%s,intense:%d,attrs:(0x%02x)}",
VerifyOutputTraits<TextColor>::ToString(attr._foreground).GetBuffer(),
VerifyOutputTraits<TextColor>::ToString(attr._background).GetBuffer(),
attr.IsIntense(),
attr._wAttrLegacy,
static_cast<DWORD>(attr._extendedAttrs));
static_cast<DWORD>(attr._attrs));
}
};
}

View File

@@ -1,98 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
UnicodeStorage::UnicodeStorage() noexcept :
_map{}
{
}
// Routine Description:
// - fetches the text associated with key
// Arguments:
// - key - the key into the storage
// Return Value:
// - the glyph data associated with key
// Note: will throw exception if key is not stored yet
const UnicodeStorage::mapped_type& UnicodeStorage::GetText(const key_type key) const
{
return _map.at(key);
}
// Routine Description:
// - stores glyph data associated with key.
// Arguments:
// - key - the key into the storage
// - glyph - the glyph data to store
void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
{
_map.insert_or_assign(key, glyph);
}
// Routine Description:
// - erases key and its associated data from the storage
// Arguments:
// - key - the key to remove
void UnicodeStorage::Erase(const key_type key) noexcept
{
_map.erase(key);
}
// Routine Description:
// - Remaps all of the stored items to new coordinate positions
// based on a bulk rearrangement of row IDs and potential row width resize.
// Arguments:
// - 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<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;
// Walk through every stored item.
for (const auto& pair : _map)
{
// Extract the old coordinate position
const auto oldCoord = pair.first;
// Only try to short-circuit based on width if we were told it changed
// by being given a new width value.
if (width.has_value())
{
// Get the column ID
const auto oldColId = oldCoord.X;
// If the column index is at/beyond the row width, don't bother copying it to the new map.
if (oldColId >= width.value())
{
continue;
}
}
// Get the row ID from the position as that's what we need to remap
const auto oldRowId = oldCoord.Y;
// Use the mapping given to convert the old row ID to the new row ID
const auto mapIter = rowMap.find(oldRowId);
// If there's no mapping to a new row, don't bother copying it to the new map. The row is gone.
if (mapIter == rowMap.end())
{
continue;
}
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 = til::point{ oldCoord.X, newRowId };
// Put the adjusted coordinate into the map with the original value.
newMap.emplace(newCoord, pair.second);
}
// Swap into the stored map, free the temporary when we exit.
_map.swap(newMap);
}

View File

@@ -1,65 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- UnicodeStorage.hpp
Abstract:
- dynamic storage location for glyphs that can't normally fit in the output buffer
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include <unordered_map>
#include <vector>
#include <til/hash.h>
// std::unordered_map needs help to know how to hash a til::point
namespace std
{
template<>
struct hash<til::point>
{
// Routine Description:
// - hashes a coord. coord will be hashed by storing the x and y values consecutively in the lower
// bits of a size_t.
// Arguments:
// - coord - the coord to hash
// Return Value:
// - the hashed coord
size_t operator()(const til::point coord) const noexcept
{
return til::hash(coord);
}
};
}
class UnicodeStorage final
{
public:
using key_type = typename til::point;
using mapped_type = typename std::vector<wchar_t>;
UnicodeStorage() noexcept;
const mapped_type& GetText(const key_type key) const;
void StoreGlyph(const key_type key, const mapped_type& glyph);
void Erase(const key_type key) noexcept;
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;
#ifdef UNIT_TESTING
friend class UnicodeStorageTests;
friend class TextBufferTests;
#endif
};

View File

@@ -29,9 +29,7 @@ Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
{
}
Cursor::~Cursor()
{
}
Cursor::~Cursor() = default;
til::point Cursor::GetPosition() const noexcept
{

View File

@@ -11,7 +11,6 @@
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
<ItemGroup>
<ClCompile Include="..\AttrRow.cpp" />
<ClCompile Include="..\cursor.cpp" />
<ClCompile Include="..\OutputCell.cpp" />
<ClCompile Include="..\OutputCellIterator.cpp" />
@@ -24,16 +23,11 @@
<ClCompile Include="..\textBuffer.cpp" />
<ClCompile Include="..\textBufferCellIterator.cpp" />
<ClCompile Include="..\textBufferTextIterator.cpp" />
<ClCompile Include="..\CharRow.cpp" />
<ClCompile Include="..\CharRowCell.cpp" />
<ClCompile Include="..\CharRowCellReference.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\UnicodeStorage.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AttrRow.hpp" />
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />
@@ -49,11 +43,7 @@
<ClInclude Include="..\textBuffer.hpp" />
<ClInclude Include="..\textBufferCellIterator.hpp" />
<ClInclude Include="..\textBufferTextIterator.hpp" />
<ClInclude Include="..\CharRow.hpp" />
<ClInclude Include="..\CharRowCell.hpp" />
<ClInclude Include="..\CharRowCellReference.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\UnicodeStorage.hpp" />
</ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />

View File

@@ -0,0 +1,3 @@
BUILD_PASS1_CONSUMES= \
onecore\windows\vcpkg|PASS1 \

View File

@@ -5,9 +5,9 @@
#include "search.h"
#include "CharRow.hpp"
#include <til/unicode.h>
#include "textBuffer.hpp"
#include "../types/inc/Utf16Parser.hpp"
#include "../types/inc/GlyphWidth.hpp"
using namespace Microsoft::Console::Types;
@@ -193,12 +193,11 @@ bool Search::_FindNeedleInHaystackAt(const til::point pos, til::point& start, ti
auto bufferPos = pos;
for (const auto& needleCell : _needle)
for (const auto& needleChars : _needle)
{
// Haystack is the buffer. Needle is the string we were given.
const auto hayIter = _uiaData.GetTextBuffer().GetTextDataAt(bufferPos);
const auto hayChars = *hayIter;
const auto needleChars = std::wstring_view(needleCell.data(), needleCell.size());
// If we didn't match at any point of the needle, return false.
if (!_CompareChars(hayChars, needleChars))
@@ -329,13 +328,12 @@ void Search::_UpdateNextPosition()
// - wstr - String that will be our search term
// Return Value:
// - Structured text data for comparison to screen buffer text data.
std::vector<std::vector<wchar_t>> Search::s_CreateNeedleFromString(const std::wstring_view wstr)
std::vector<std::wstring> Search::s_CreateNeedleFromString(const std::wstring_view wstr)
{
const auto charData = Utf16Parser::Parse(wstr);
std::vector<std::vector<wchar_t>> cells;
for (const auto chars : charData)
std::vector<std::wstring> cells;
for (const auto& chars : til::utf16_iterator{ wstr })
{
if (IsGlyphFullWidth(std::wstring_view{ chars.data(), chars.size() }))
if (IsGlyphFullWidth(chars))
{
cells.emplace_back(chars);
}

View File

@@ -68,7 +68,7 @@ private:
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_view wstr);
static std::vector<std::wstring> s_CreateNeedleFromString(const std::wstring_view wstr);
bool _reachedEnd = false;
til::point _coordNext;
@@ -76,7 +76,7 @@ private:
til::point _coordSelEnd;
const til::point _coordAnchor;
const std::vector<std::vector<wchar_t>> _needle;
const std::vector<std::wstring> _needle;
const Direction _direction;
const Sensitivity _sensitivity;
Microsoft::Console::Types::IUiaData& _uiaData;

View File

@@ -29,7 +29,6 @@ PRECOMPILED_CXX = 1
PRECOMPILED_INCLUDE = ..\precomp.h
SOURCES= \
..\AttrRow.cpp \
..\cursor.cpp \
..\OutputCell.cpp \
..\OutputCellIterator.cpp \
@@ -41,10 +40,6 @@ SOURCES= \
..\textBuffer.cpp \
..\textBufferCellIterator.cpp \
..\textBufferTextIterator.cpp \
..\CharRow.cpp \
..\CharRowCell.cpp \
..\CharRowCellReference.cpp \
..\UnicodeStorage.cpp \
..\search.cpp \
INCLUDES= \

View File

@@ -4,15 +4,83 @@
#include "precomp.h"
#include "textBuffer.hpp"
#include "CharRow.hpp"
#include <til/hash.h>
#include <til/unicode.h>
#include "../renderer/base/renderer.hpp"
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#pragma hdrstop
namespace
{
struct BufferAllocator
{
BufferAllocator(til::size sz)
{
const auto w = gsl::narrow<uint16_t>(sz.width);
const auto h = gsl::narrow<uint16_t>(sz.height);
const auto charsBytes = w * sizeof(wchar_t);
// The ROW::_indices array stores 1 more item than the buffer is wide.
// That extra column stores the past-the-end _chars pointer.
const auto indicesBytes = w * sizeof(uint16_t) + sizeof(uint16_t);
const auto rowStride = charsBytes + indicesBytes;
// 65535*65535 cells would result in a charsAreaSize of 8GiB.
// --> Use uint64_t so that we can safely do our calculations even on x86.
const auto allocSize = gsl::narrow<size_t>(::base::strict_cast<uint64_t>(rowStride) * ::base::strict_cast<uint64_t>(h));
_buffer = wil::unique_virtualalloc_ptr<std::byte>{ static_cast<std::byte*>(VirtualAlloc(nullptr, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) };
THROW_IF_NULL_ALLOC(_buffer);
_data = std::span{ _buffer.get(), allocSize }.begin();
_rowStride = rowStride;
_indicesOffset = charsBytes;
_width = w;
_height = h;
}
BufferAllocator& operator++() noexcept
{
_data += _rowStride;
return *this;
}
wchar_t* chars() const noexcept
{
return til::bit_cast<wchar_t*>(&*_data);
}
uint16_t* indices() const noexcept
{
return til::bit_cast<uint16_t*>(&*(_data + _indicesOffset));
}
uint16_t width() const noexcept
{
return _width;
}
uint16_t height() const noexcept
{
return _height;
}
wil::unique_virtualalloc_ptr<std::byte>&& take() noexcept
{
return std::move(_buffer);
}
private:
wil::unique_virtualalloc_ptr<std::byte> _buffer;
std::span<std::byte>::iterator _data;
size_t _rowStride;
size_t _indicesOffset;
uint16_t _width;
uint16_t _height;
};
}
using namespace Microsoft::Console;
using namespace Microsoft::Console::Types;
@@ -35,24 +103,20 @@ TextBuffer::TextBuffer(const til::size screenBufferSize,
const UINT cursorSize,
const bool isActiveBuffer,
Microsoft::Console::Render::Renderer& renderer) :
_firstRow{ 0 },
_renderer{ renderer },
_currentAttributes{ defaultAttributes },
_cursor{ cursorSize, *this },
_storage{},
_unicodeStorage{},
_isActiveBuffer{ isActiveBuffer },
_renderer{ renderer },
_size{},
_currentHyperlinkId{ 1 },
_currentPatternId{ 0 }
_isActiveBuffer{ isActiveBuffer }
{
// initialize ROWs
_storage.reserve(gsl::narrow<size_t>(screenBufferSize.Y));
for (til::CoordType i = 0; i < screenBufferSize.Y; ++i)
BufferAllocator allocator{ screenBufferSize };
_storage.reserve(allocator.height());
for (til::CoordType i = 0; i < screenBufferSize.Y; ++i, ++allocator)
{
_storage.emplace_back(i, screenBufferSize.X, _currentAttributes, this);
_storage.emplace_back(allocator.chars(), allocator.indices(), allocator.width(), _currentAttributes);
}
_charBuffer = allocator.take();
_UpdateSize();
}
@@ -199,10 +263,10 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// To figure out if the sequence is valid, we have to look at the character that comes before the current one
const auto coordPrevPosition = _GetPreviousFromCursor();
auto& prevRow = GetRowByOffset(coordPrevPosition.Y);
DbcsAttribute prevDbcsAttr;
DbcsAttribute prevDbcsAttr = DbcsAttribute::Single;
try
{
prevDbcsAttr = prevRow.GetCharRow().DbcsAttrAt(coordPrevPosition.X);
prevDbcsAttr = prevRow.DbcsAttrAt(coordPrevPosition.X);
}
catch (...)
{
@@ -229,21 +293,21 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// T T Fail, uncorrectable. New trailing byte must have had leading before it.
// Check for only failing portions of the matrix:
if (prevDbcsAttr.IsSingle() && dbcsAttribute.IsTrailing())
if (prevDbcsAttr == DbcsAttribute::Single && dbcsAttribute == DbcsAttribute::Trailing)
{
// N, T failing case (uncorrectable)
fValidSequence = false;
}
else if (prevDbcsAttr.IsLeading())
else if (prevDbcsAttr == DbcsAttribute::Leading)
{
if (dbcsAttribute.IsSingle() || dbcsAttribute.IsLeading())
if (dbcsAttribute == DbcsAttribute::Single || dbcsAttribute == DbcsAttribute::Leading)
{
// L, N and L, L failing cases (correctable)
fValidSequence = false;
fCorrectableByErase = true;
}
}
else if (prevDbcsAttr.IsTrailing() && dbcsAttribute.IsTrailing())
else if (prevDbcsAttr == DbcsAttribute::Trailing && dbcsAttribute == DbcsAttribute::Trailing)
{
// T, T failing case (uncorrectable)
fValidSequence = false;
@@ -255,7 +319,7 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// Erase previous character into an N type.
try
{
prevRow.ClearColumn(coordPrevPosition.X);
prevRow.ClearCell(coordPrevPosition.X);
}
catch (...)
{
@@ -289,7 +353,7 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
auto fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence
// We only need to compensate for leading bytes
if (dbcsAttribute.IsLeading())
if (dbcsAttribute == DbcsAttribute::Leading)
{
const auto cursorPosition = GetCursor().GetPosition();
const auto lineWidth = GetLineWidth(cursorPosition.Y);
@@ -418,12 +482,20 @@ bool TextBuffer::InsertCharacter(const std::wstring_view chars,
auto& Row = GetRowByOffset(iRow);
// Store character and double byte data
auto& charRow = Row.GetCharRow();
try
{
charRow.GlyphAt(iCol) = chars;
charRow.DbcsAttrAt(iCol) = dbcsAttribute;
switch (dbcsAttribute)
{
case DbcsAttribute::Leading:
Row.ReplaceCharacters(iCol, 2, chars);
break;
case DbcsAttribute::Trailing:
Row.ReplaceCharacters(iCol - 1, 2, chars);
break;
default:
Row.ReplaceCharacters(iCol, 1, chars);
break;
}
}
catch (...)
{
@@ -432,7 +504,7 @@ bool TextBuffer::InsertCharacter(const std::wstring_view chars,
}
// Store color data
fSuccess = Row.GetAttrRow().SetAttrToEnd(iCol, attr);
fSuccess = Row.SetAttrToEnd(iCol, attr);
if (fSuccess)
{
// Advance the cursor
@@ -572,8 +644,7 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode)
// the current background color, but with no meta attributes set.
fillAttributes.SetStandardErase();
}
const auto fSuccess = _storage.at(_firstRow).Reset(fillAttributes);
if (fSuccess)
GetRowByOffset(0).Reset(fillAttributes);
{
// Now proceed to increment.
// Incrementing it will cause the next line down to become the new "top" of the window (the new "0" in logical coordinates)
@@ -585,7 +656,7 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode)
_firstRow = 0;
}
}
return fSuccess;
return true;
}
//Routine Description:
@@ -610,7 +681,7 @@ til::point TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::C
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 = currRow.GetCharRow().MeasureRight() - 1;
coordEndOfText.X = currRow.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();
@@ -621,7 +692,7 @@ til::point TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::C
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 = backupRow.GetCharRow().MeasureRight() - 1;
coordEndOfText.X = backupRow.MeasureRight() - 1;
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
}
@@ -779,10 +850,6 @@ void TextBuffer::ScrollRows(const til::CoordType firstRow, const til::CoordType
// - end
std::rotate(_storage.begin() + firstRow, _storage.begin() + firstRow + size, _storage.begin() + firstRow + size + delta);
}
// Renumber the IDs now that we've rearranged where the rows sit within the buffer.
// Refreshing should also delegate to the UnicodeStorage to re-key all the stored unicode sequences (where applicable).
_RefreshRowIDs(std::nullopt);
}
Cursor& TextBuffer::GetCursor() noexcept
@@ -898,10 +965,10 @@ void TextBuffer::Reset()
// - Success if successful. Invalid parameter if screen buffer size is unexpected. No memory if allocation failed.
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const til::size newSize) noexcept
{
RETURN_HR_IF(E_INVALIDARG, newSize.X < 0 || newSize.Y < 0);
try
{
BufferAllocator allocator{ newSize };
const auto currentSize = GetSize().Dimensions();
const auto attributes = GetCurrentAttributes();
@@ -913,49 +980,30 @@ void TextBuffer::Reset()
const auto TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
// rotate rows until the top row is at index 0
for (auto i = 0; i < TopRowIndex; i++)
{
_storage.emplace_back(std::move(_storage.front()));
_storage.erase(_storage.begin());
}
std::rotate(_storage.begin(), _storage.begin() + TopRowIndex, _storage.end());
_SetFirstRowIndex(0);
// realloc in the Y direction
// remove rows if we're shrinking
while (_storage.size() > static_cast<size_t>(newSize.Y))
{
_storage.pop_back();
}
// add rows if we're growing
while (_storage.size() < static_cast<size_t>(newSize.Y))
{
_storage.emplace_back(gsl::narrow_cast<til::CoordType>(_storage.size()), newSize.X, attributes, this);
}
_storage.resize(allocator.height());
// Now that we've tampered with the row placement, refresh all the row IDs.
// Also take advantage of the row ID refresh loop to resize the rows in the X dimension
// and cleanup the UnicodeStorage characters that might fall outside the resized buffer.
_RefreshRowIDs(newSize.X);
// realloc in the X direction
for (auto& it : _storage)
{
it.Resize(allocator.chars(), allocator.indices(), allocator.width(), attributes);
++allocator;
}
// Update the cached size value
_UpdateSize();
_charBuffer = allocator.take();
}
CATCH_RETURN();
return S_OK;
}
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept
{
return _unicodeStorage;
}
UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept
{
return _unicodeStorage;
}
void TextBuffer::SetAsActiveBuffer(const bool isActiveBuffer) noexcept
{
_isActiveBuffer = isActiveBuffer;
@@ -1019,42 +1067,6 @@ void TextBuffer::TriggerNewTextNotification(const std::wstring_view newText)
}
}
// Routine Description:
// - Method to help refresh all the Row IDs after manipulating the row
// by shuffling pointers around.
// - This will also update parent pointers that are stored in depth within the buffer
// (e.g. it will update CharRow parents pointing at Rows that might have been moved around)
// - Optionally takes a new row width if we're resizing to perform a resize operation and cleanup
// 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<til::CoordType> newRowWidth)
{
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
rowMap.emplace(it.GetId(), i);
// Update the IDs
it.SetId(i++);
// Also update the char row parent pointers as they can get shuffled up in the rotates.
it.GetCharRow().UpdateParent(&it);
// Resize the rows in the X dimension if we have a new width
if (newRowWidth.has_value())
{
// Realloc in the X direction
THROW_IF_FAILED(it.Resize(newRowWidth.value()));
}
}
// Give the new mapping to Unicode Storage
_unicodeStorage.Remap(rowMap, newRowWidth);
}
// Routine Description:
// - Retrieves the first row from the underlying buffer.
// Arguments:
@@ -1066,27 +1078,6 @@ ROW& TextBuffer::_GetFirstRow() noexcept
return GetRowByOffset(0);
}
// Routine Description:
// - Retrieves the row that comes before the given row.
// - Does not wrap around the screen buffer.
// Arguments:
// - The current row.
// Return Value:
// - reference to the previous row
// Note:
// - will throw exception if called with the first row of the text buffer
ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
{
auto prevRowIndex = Row.GetId() - 1;
if (prevRowIndex < 0)
{
prevRowIndex = TotalRowCount() - 1;
}
THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow);
return _storage.at(prevRowIndex);
}
// Method Description:
// - get delimiter class for buffer cell position
// - used for double click selection and uia word navigation
@@ -1095,9 +1086,9 @@ 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
DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const
DelimiterClass TextBuffer::_GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const noexcept
{
return GetRowByOffset(pos.Y).GetCharRow().DelimiterClassAt(pos.X, wordDelimiters);
return GetRowByOffset(pos.Y).DelimiterClassAt(pos.X, wordDelimiters);
}
// Method Description:
@@ -1161,7 +1152,7 @@ til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - 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
til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const noexcept
{
auto result = target;
const auto bufferSize = GetSize();
@@ -1206,7 +1197,7 @@ til::point TextBuffer::_GetWordStartForAccessibility(const til::point target, co
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - 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
til::point TextBuffer::_GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept
{
auto result = target;
const auto bufferSize = GetSize();
@@ -1327,7 +1318,7 @@ til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, cons
// - wordDelimiters - what characters are we considering for the separation of words
// Return Value:
// - 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
til::point TextBuffer::_GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept
{
const auto bufferSize = GetSize();
@@ -1362,7 +1353,7 @@ void TextBuffer::_PruneHyperlinks()
// If the buffer does not contain the same reference, we can remove that hyperlink from our map
// This way, obsolete hyperlink references are cleared from our hyperlink map instead of hanging around
// Get all the hyperlink references in the row we're erasing
const auto hyperlinks = _storage.at(_firstRow).GetAttrRow().GetHyperlinks();
const auto hyperlinks = GetRowByOffset(0).GetHyperlinks();
if (!hyperlinks.empty())
{
@@ -1378,7 +1369,7 @@ void TextBuffer::_PruneHyperlinks()
// to see if those references are anywhere else
for (til::CoordType i = 1; i < total; ++i)
{
const auto nextRowRefs = GetRowByOffset(i).GetAttrRow().GetHyperlinks();
const auto nextRowRefs = GetRowByOffset(i).GetHyperlinks();
for (auto id : nextRowRefs)
{
if (firstRowRefs.find(id) != firstRowRefs.end())
@@ -1472,7 +1463,7 @@ til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::po
}
// limit is exclusive, so we need to move back to be within valid bounds
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr() == DbcsAttribute::Trailing)
{
bufferSize.DecrementInBounds(resultPos, true);
}
@@ -1499,7 +1490,7 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
resultPos = limit;
}
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr() == DbcsAttribute::Leading)
{
bufferSize.IncrementInBounds(resultPos, true);
}
@@ -1547,7 +1538,7 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
const bool success{ ++iter };
// Move again if we're on a wide glyph
if (success && iter->DbcsAttr().IsTrailing())
if (success && iter->DbcsAttr() == DbcsAttribute::Trailing)
{
++iter;
}
@@ -1579,7 +1570,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
// try to move. If we can't, we're done.
const auto success = bufferSize.DecrementInBounds(resultPos, true);
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr() == DbcsAttribute::Leading)
{
bufferSize.DecrementInBounds(resultPos, true);
}
@@ -1726,7 +1717,7 @@ void TextBuffer::_ExpandTextRow(til::inclusive_rect& textRow) const
// expand left side of rect
til::point targetPoint{ textRow.Left, textRow.Top };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsTrailing())
if (GetCellDataAt(targetPoint)->DbcsAttr() == DbcsAttribute::Trailing)
{
if (targetPoint.X == bufferSize.Left())
{
@@ -1741,7 +1732,7 @@ void TextBuffer::_ExpandTextRow(til::inclusive_rect& textRow) const
// expand right side of rect
targetPoint = { textRow.Right, textRow.Bottom };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsLeading())
if (GetCellDataAt(targetPoint)->DbcsAttr() == DbcsAttribute::Leading)
{
if (targetPoint.X == bufferSize.RightInclusive())
{
@@ -1811,7 +1802,7 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
{
const auto& cell = *it;
if (!cell.DbcsAttr().IsTrailing())
if (cell.DbcsAttr() != DbcsAttribute::Trailing)
{
const auto chars = cell.Chars();
selectionText.append(chars);
@@ -1911,7 +1902,7 @@ std::wstring TextBuffer::GetPlainText(const til::point& start, const til::point&
for (; it && spanLength > 0; ++it, --spanLength)
{
const auto& cell = *it;
if (!cell.DbcsAttr().IsTrailing())
if (cell.DbcsAttr() != DbcsAttribute::Trailing)
{
const auto chars = cell.Chars();
text.append(chars);
@@ -2352,12 +2343,11 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// 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 = charRow.MeasureRight();
auto iRight = row.MeasureRight();
// If we're starting a new row, try and preserve the line rendition
// from the row in the original buffer.
const auto newBufferPos = newBuffer.GetCursor().GetPosition();
const auto newBufferPos = newCursor.GetPosition();
if (newBufferPos.X == 0)
{
auto& newRow = newBuffer.GetRowByOffset(newBufferPos.Y);
@@ -2404,9 +2394,9 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
try
{
// TODO: MSFT: 19446208 - this should just use an iterator and the inserter...
const auto glyph = row.GetCharRow().GlyphAt(iOldCol);
const auto dbcsAttr = row.GetCharRow().DbcsAttrAt(iOldCol);
const auto textAttr = row.GetAttrRow().GetAttrByColumn(iOldCol);
const auto glyph = row.GlyphAt(iOldCol);
const auto dbcsAttr = row.DbcsAttrAt(iOldCol);
const auto textAttr = row.GetAttrByColumn(iOldCol);
if (!newBuffer.InsertCharacter(glyph, dbcsAttr, textAttr))
{
@@ -2450,8 +2440,8 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
try
{
// TODO: MSFT: 19446208 - this should just use an iterator and the inserter...
const auto textAttr = row.GetAttrRow().GetAttrByColumn(copyAttrCol);
if (!newRow.GetAttrRow().SetAttrToEnd(newAttrColumn, textAttr))
const auto textAttr = row.GetAttrByColumn(copyAttrCol);
if (!newRow.SetAttrToEnd(newAttrColumn, textAttr))
{
break;
}
@@ -2565,8 +2555,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// the last attr when wider.
auto& newRow = newBuffer.GetRowByOffset(newRowY);
const auto newWidth = newBuffer.GetLineWidth(newRowY);
newRow.GetAttrRow() = row.GetAttrRow();
newRow.GetAttrRow().Resize(newWidth);
newRow.TransferAttributes(row.Attributes(), newWidth);
newRowY++;
}
@@ -2821,16 +2810,14 @@ PointTree TextBuffer::GetPatterns(const til::CoordType firstRow, const til::Coor
// match and the previous match, so we use the size of the prefix
// along with the size of the match to determine the locations
til::CoordType prefixSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->prefix().str()))
for (const auto str = i->prefix().str(); const auto& glyph : til::utf16_iterator{ str })
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
prefixSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto start = lenUpToThis + prefixSize;
til::CoordType matchSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->str()))
for (const auto str = i->str(); const auto& glyph : til::utf16_iterator{ str })
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
matchSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto end = start + matchSize;

View File

@@ -54,7 +54,6 @@ filling in the last row, and updating the screen.
#include "cursor.h"
#include "Row.hpp"
#include "TextAttribute.hpp"
#include "UnicodeStorage.hpp"
#include "../types/inc/Viewport.hpp"
#include "../buffer/out/textBufferCellIterator.hpp"
@@ -140,9 +139,6 @@ public:
[[nodiscard]] HRESULT ResizeTraditional(const til::size newSize) noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
UnicodeStorage& GetUnicodeStorage() noexcept;
void SetAsActiveBuffer(const bool isActiveBuffer) noexcept;
bool IsActiveBuffer() const noexcept;
@@ -221,59 +217,42 @@ public:
private:
void _UpdateSize();
Microsoft::Console::Types::Viewport _size;
std::vector<ROW> _storage;
Cursor _cursor;
til::CoordType _firstRow; // indexes top row (not necessarily 0)
TextAttribute _currentAttributes;
// storage location for glyphs that can't fit into the buffer normally
UnicodeStorage _unicodeStorage;
bool _isActiveBuffer;
Microsoft::Console::Render::Renderer& _renderer;
struct Clickable
{
std::wstring uri;
std::wstring params;
};
std::unordered_map<uint16_t, Clickable> _hyperlinkMap;
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
uint16_t _currentHyperlinkId;
void _RefreshRowIDs(std::optional<til::CoordType> newRowWidth);
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
til::point _GetPreviousFromCursor() const noexcept;
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() noexcept;
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(til::inclusive_rect& selectionRow) 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;
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const noexcept;
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
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;
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
void _PruneHyperlinks();
static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text);
Microsoft::Console::Render::Renderer& _renderer;
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
uint16_t _currentHyperlinkId = 1;
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
size_t _currentPatternId;
size_t _currentPatternId = 0;
wil::unique_virtualalloc_ptr<std::byte> _charBuffer;
std::vector<ROW> _storage;
TextAttribute _currentAttributes;
til::CoordType _firstRow = 0; // indexes top row (not necessarily 0)
Cursor _cursor;
Microsoft::Console::Types::Viewport _size;
bool _isActiveBuffer = false;
#ifdef UNIT_TESTING
friend class TextBufferTests;

View File

@@ -5,7 +5,6 @@
#include "textBufferCellIterator.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/viewport.hpp"
@@ -37,7 +36,7 @@ TextBufferCellIterator::TextBufferCellIterator(const TextBuffer& buffer, til::po
_bounds(limits),
_exceeded(false),
_view({}, {}, {}, TextAttributeBehavior::Stored),
_attrIter(s_GetRow(buffer, pos)->GetAttrRow().cbegin())
_attrIter(s_GetRow(buffer, pos)->AttrBegin())
{
// Throw if the bounds rectangle is not limited to the inside of the given buffer.
THROW_HR_IF(E_INVALIDARG, !buffer.GetSize().IsInBounds(limits));
@@ -165,16 +164,15 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
_attrIter += diff;
_view.UpdateTextAttribute(*_attrIter);
const auto& charRow = _pRow->GetCharRow();
_view.UpdateText(charRow.GlyphAt(newX));
_view.UpdateDbcsAttribute(charRow.DbcsAttrAt(newX));
_view.UpdateText(_pRow->GlyphAt(newX));
_view.UpdateDbcsAttribute(_pRow->DbcsAttrAt(newX));
_pos.X = newX;
}
else
{
// cold path (_GenerateView is slow)
_pRow = s_GetRow(_buffer, { newX, newY });
_attrIter = _pRow->GetAttrRow().cbegin() + newX;
_attrIter = _pRow->AttrBegin() + newX;
_pos.X = newX;
_pos.Y = newY;
_GenerateView();
@@ -289,12 +287,12 @@ 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 til::point newPos)
void TextBufferCellIterator::_SetPos(const til::point newPos) noexcept
{
if (newPos.Y != _pos.Y)
{
_pRow = s_GetRow(_buffer, newPos);
_attrIter = _pRow->GetAttrRow().cbegin();
_attrIter = _pRow->AttrBegin();
_pos.X = 0;
}
@@ -324,10 +322,10 @@ const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const til:
// Routine Description:
// - Updates the internal view. Call after updating row, attribute, or positions.
void TextBufferCellIterator::_GenerateView()
void TextBufferCellIterator::_GenerateView() noexcept
{
_view = OutputCellView(_pRow->GetCharRow().GlyphAt(_pos.X),
_pRow->GetCharRow().DbcsAttrAt(_pos.X),
_view = OutputCellView(_pRow->GlyphAt(_pos.X),
_pRow->DbcsAttrAt(_pos.X),
*_attrIter,
TextAttributeBehavior::Stored);
}

View File

@@ -15,8 +15,7 @@ Author(s):
#pragma once
#include "CharRow.hpp"
#include "AttrRow.hpp"
#include "Row.hpp"
#include "OutputCellView.hpp"
#include "../../types/inc/viewport.hpp"
@@ -50,14 +49,14 @@ public:
til::point Pos() const noexcept;
protected:
void _SetPos(const til::point newPos);
void _GenerateView();
void _SetPos(const til::point newPos) noexcept;
void _GenerateView() noexcept;
static const ROW* s_GetRow(const TextBuffer& buffer, const til::point pos) noexcept;
til::small_rle<TextAttribute, uint16_t, 1>::const_iterator _attrIter;
OutputCellView _view;
const ROW* _pRow;
ATTR_ROW::const_iterator _attrIter;
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;

View File

@@ -5,7 +5,6 @@
#include "textBufferTextIterator.hpp"
#include "CharRow.hpp"
#include "Row.hpp"
#pragma hdrstop

View File

@@ -7,7 +7,6 @@
#include "../textBuffer.hpp"
#include "../../renderer/inc/DummyRenderer.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include <IDataSource.h>
@@ -683,7 +682,7 @@ namespace
{
STDMETHODIMP Advance(IDataRow** ppDataRow) override
{
if (_index < std::extent<decltype(testCases)>::value)
if (_index < std::extent_v<decltype(testCases)>)
{
Microsoft::WRL::MakeAndInitialize<ArrayIndexTaefAdapterRow>(ppDataRow, _index++);
}
@@ -737,34 +736,22 @@ class ReflowTests
{
auto buffer = std::make_unique<TextBuffer>(testBuffer.size, TextAttribute{ 0x7 }, 0, false, renderer);
til::CoordType i{};
til::CoordType y = 0;
for (const auto& testRow : testBuffer.rows)
{
auto& row{ buffer->GetRowByOffset(i) };
auto& row{ buffer->GetRowByOffset(y) };
auto& charRow{ row.GetCharRow() };
row.SetWrapForced(testRow.wrap);
til::CoordType j{};
for (auto it{ charRow.begin() }; it != charRow.end(); ++it)
til::CoordType x = 0;
for (const auto& ch : testRow.text)
{
// Yes, we're about to manually create a buffer. It is unpleasant.
const auto ch{ til::at(testRow.text, j) };
it->Char() = ch;
if (IsGlyphFullWidth(ch))
{
it->DbcsAttr().SetLeading();
it++;
it->Char() = ch;
it->DbcsAttr().SetTrailing();
}
else
{
it->DbcsAttr().SetSingle();
}
j++;
const til::CoordType width = IsGlyphFullWidth(ch) ? 2 : 1;
row.ReplaceCharacters(x, width, { &ch, 1 });
x += width;
}
i++;
y++;
}
buffer->GetCursor().SetPosition(testBuffer.cursor);
@@ -783,42 +770,44 @@ class ReflowTests
VERIFY_ARE_EQUAL(testBuffer.cursor, buffer.GetCursor().GetPosition());
VERIFY_ARE_EQUAL(testBuffer.size, buffer.GetSize().Dimensions());
til::CoordType i{};
til::CoordType y = 0;
for (const auto& testRow : testBuffer.rows)
{
NoThrowString indexString;
const auto& row{ buffer.GetRowByOffset(i) };
const auto& row{ buffer.GetRowByOffset(y) };
const auto& charRow{ row.GetCharRow() };
indexString.Format(L"[Row %d]", i);
indexString.Format(L"[Row %d]", y);
VERIFY_ARE_EQUAL(testRow.wrap, row.WasWrapForced(), indexString);
til::CoordType j{};
for (auto it{ charRow.begin() }; it != charRow.end(); ++it)
til::CoordType x = 0;
til::CoordType j = 0;
for (const auto& ch : testRow.text)
{
indexString.Format(L"[Cell %d, %d; Text line index %d]", it - charRow.begin(), i, j);
// Yes, we're about to manually create a buffer. It is unpleasant.
const auto ch{ til::at(testRow.text, j) };
indexString.Format(L"[Cell %d, %d; Text line index %d]", x, y, j);
if (IsGlyphFullWidth(ch))
{
// Char is full width in test buffer, so
// ensure that real buffer is LEAD, TRAIL (ch)
VERIFY_IS_TRUE(it->DbcsAttr().IsLeading(), indexString);
VERIFY_ARE_EQUAL(ch, it->Char(), indexString);
VERIFY_ARE_EQUAL(row.DbcsAttrAt(x), DbcsAttribute::Leading, indexString);
VERIFY_ARE_EQUAL(ch, row.GlyphAt(x).front(), indexString);
++x;
it++;
VERIFY_IS_TRUE(it->DbcsAttr().IsTrailing(), indexString);
VERIFY_ARE_EQUAL(row.DbcsAttrAt(x), DbcsAttribute::Trailing, indexString);
VERIFY_ARE_EQUAL(ch, row.GlyphAt(x).front(), indexString);
++x;
}
else
{
VERIFY_IS_TRUE(it->DbcsAttr().IsSingle(), indexString);
VERIFY_ARE_EQUAL(row.DbcsAttrAt(x), DbcsAttribute::Single, indexString);
VERIFY_ARE_EQUAL(ch, row.GlyphAt(x).front(), indexString);
++x;
}
VERIFY_ARE_EQUAL(ch, it->Char(), indexString);
j++;
}
i++;
y++;
}
}

View File

@@ -75,7 +75,7 @@ void TextAttributeTests::TestRoundtripMetaBits()
auto attr = TextAttribute(expectedLegacy);
VERIFY_IS_TRUE(attr.IsLegacy());
VERIFY_ARE_EQUAL(expectedLegacy, attr.GetLegacyAttributes());
VERIFY_ARE_EQUAL(flag, attr._wAttrLegacy);
VERIFY_ARE_EQUAL(flag, static_cast<WORD>(attr._attrs));
}
}

View File

@@ -14,7 +14,6 @@
<ClCompile Include="ReflowTests.cpp" />
<ClCompile Include="TextColorTests.cpp" />
<ClCompile Include="TextAttributeTests.cpp" />
<ClCompile Include="UnicodeStorageTests.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@@ -42,4 +41,4 @@
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
</Project>
</Project>

View File

@@ -1,51 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"
#include "../UnicodeStorage.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class UnicodeStorageTests
{
TEST_CLASS(UnicodeStorageTests);
TEST_METHOD(CanOverwriteEmoji)
{
UnicodeStorage storage;
const til::point coord{ 1, 3 };
const std::vector<wchar_t> newMoon{ 0xD83C, 0xDF11 };
const std::vector<wchar_t> fullMoon{ 0xD83C, 0xDF15 };
// store initial glyph
storage.StoreGlyph(coord, newMoon);
// verify it was stored
auto findIt = storage._map.find(coord);
VERIFY_ARE_NOT_EQUAL(findIt, storage._map.end());
const auto& newMoonGlyph = findIt->second;
VERIFY_ARE_EQUAL(newMoonGlyph.size(), newMoon.size());
for (size_t i = 0; i < newMoon.size(); ++i)
{
VERIFY_ARE_EQUAL(newMoonGlyph.at(i), newMoon.at(i));
}
// overwrite it
storage.StoreGlyph(coord, fullMoon);
// verify the glyph was overwritten
findIt = storage._map.find(coord);
VERIFY_ARE_NOT_EQUAL(findIt, storage._map.end());
const auto& fullMoonGlyph = findIt->second;
VERIFY_ARE_EQUAL(fullMoonGlyph.size(), fullMoon.size());
for (size_t i = 0; i < fullMoon.size(); ++i)
{
VERIFY_ARE_EQUAL(fullMoonGlyph.at(i), fullMoon.at(i));
}
}
};

View File

@@ -0,0 +1,3 @@
BUILD_PASS1_CONSUMES= \
onecore\windows\vcpkg|PASS1 \

View File

@@ -26,7 +26,7 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>

View File

@@ -27,7 +27,7 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>

View File

@@ -27,7 +27,7 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>
@@ -188,8 +188,8 @@
<com:ComInterface>
<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="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/> <!-- ITerminalHandoff -->
<com:Interface Id="AA6B364F-4A50-4176-9002-0AE755E7B5EF" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/> <!-- ITerminalHandoff2 -->
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/> <!-- ITerminalHandoff -->
<com:Interface Id="AA6B364F-4A50-4176-9002-0AE755E7B5EF" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/> <!-- ITerminalHandoff2 -->
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
</com:ComInterface>
</com:Extension>

View File

@@ -41,11 +41,13 @@ Author(s):
#include <winrt/Windows.system.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.Graphics.Imaging.Interop.h>
#include <winrt/windows.ui.core.h>
#include <winrt/Windows.ui.input.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
#include <winrt/Windows.ui.xaml.input.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#include <winrt/Windows.UI.Xaml.Documents.h>

View File

@@ -503,7 +503,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr));
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr));
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@@ -521,7 +521,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr));
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr));
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@@ -843,7 +843,7 @@ namespace TerminalAppLocalTests
// | 1 | 2 |
// | | |
// -------------------
page->_SplitPane(SplitDirection::Right, 0.5f, page->_MakePane(nullptr, true, nullptr));
page->_SplitPane(SplitDirection::Right, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr));
secondId = tab->_activePane->Id().value();
});
Sleep(250);
@@ -861,7 +861,7 @@ namespace TerminalAppLocalTests
// | 3 | |
// | | |
// -------------------
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr));
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr));
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
// Split again to make the 3rd tab
thirdId = tab->_activePane->Id().value();
@@ -881,7 +881,7 @@ namespace TerminalAppLocalTests
// | 3 | 4 |
// | | |
// -------------------
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr));
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr));
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
fourthId = tab->_activePane->Id().value();
});

View File

@@ -28,7 +28,7 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22621.0" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
</Dependencies>

View File

@@ -9,7 +9,7 @@
<uap:SupportedUsers>multiple</uap:SupportedUsers>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />

View File

@@ -4,11 +4,7 @@
#include "pch.h"
#include "HwndTerminal.hpp"
#include <windowsx.h>
#include "../../types/TermControlUiaProvider.hpp"
#include <DefaultSettings.h>
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../../types/viewport.cpp"
#include "../../types/inc/GlyphWidth.hpp"
@@ -54,6 +50,17 @@ LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
LPARAM lParam) noexcept
try
{
if (WM_NCCREATE == uMsg)
{
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
auto cs = reinterpret_cast<CREATESTRUCT*>(lParam);
HwndTerminal* that = static_cast<HwndTerminal*>(cs->lpCreateParams);
that->_hwnd = wil::unique_hwnd(hwnd);
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(that));
return DefWindowProc(hwnd, WM_NCCREATE, wParam, lParam);
}
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
auto terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
@@ -169,7 +176,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
}
HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, 14, CP_UTF8 },
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
@@ -182,7 +189,7 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
if (RegisterTermClass(hInstance))
{
_hwnd = wil::unique_hwnd(CreateWindowExW(
CreateWindowExW(
0,
term_window_class,
nullptr,
@@ -197,10 +204,7 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
parentHwnd,
nullptr,
hInstance,
nullptr));
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
SetWindowLongPtr(_hwnd.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
this);
}
}
@@ -324,14 +328,15 @@ IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
{
// If TermControlUiaProvider throws during construction,
// we don't want to try constructing an instance again and again.
// _uiaProviderInitialized helps us prevent this.
if (!_uiaProviderInitialized)
if (!_uiaProvider)
{
try
{
auto lock = _terminal->LockForWriting();
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, this->GetUiaData(), this));
_uiaProviderInitialized = true;
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<HwndTerminalAutomationPeer>(&_uiaProvider, this->GetUiaData(), this));
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(_uiaProvider.Get());
LOG_IF_FAILED(_uiaEngine->Enable());
_renderer->AddRenderEngine(_uiaEngine.get());
}
catch (...)
{
@@ -380,29 +385,10 @@ void HwndTerminal::SendOutput(std::wstring_view data)
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{
// In order for UIA to hook up properly there needs to be a "static" window hosting the
// inner win32 control. If the static window is not present then WM_GETOBJECT messages
// will not reach the child control, and the uia element will not be present in the tree.
auto _hostWindow = CreateWindowEx(
0,
L"static",
nullptr,
WS_CHILD |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS |
WS_VISIBLE,
0,
0,
0,
0,
parentHwnd,
nullptr,
nullptr,
nullptr);
auto _terminal = std::make_unique<HwndTerminal>(_hostWindow);
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd);
RETURN_IF_FAILED(_terminal->Initialize());
*hwnd = _hostWindow;
*hwnd = _terminal->GetHwnd();
*terminal = _terminal.release();
return S_OK;
@@ -725,6 +711,10 @@ try
{
modifiers |= ControlKeyStates::EnhancedKey;
}
if (vkey && keyDown && _uiaProvider)
{
_uiaProvider->RecordKeyEvent(vkey);
}
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
}
CATCH_LOG();
@@ -799,7 +789,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
publicTerminal->_terminal->SetCursorStyle(static_cast<DispatchTypes::CursorStyle>(theme.CursorStyle));
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
publicTerminal->_UpdateFont(newDpi);
// When the font changes the terminal dimensions need to be recalculated since the available row and column
@@ -833,12 +823,20 @@ void __stdcall TerminalSetFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = true;
if (auto uiaEngine = publicTerminal->_uiaEngine.get())
{
LOG_IF_FAILED(uiaEngine->Enable());
}
}
void __stdcall TerminalKillFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = false;
if (auto uiaEngine = publicTerminal->_uiaEngine.get())
{
LOG_IF_FAILED(uiaEngine->Disable());
}
}
// Routine Description:

View File

@@ -5,10 +5,10 @@
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include <UIAutomationCore.h>
#include "../../types/IControlAccessibilityInfo.h"
#include "../../types/TermControlUiaProvider.hpp"
#include "HwndTerminalAutomationPeer.hpp"
using namespace Microsoft::Console::VirtualTerminal;
@@ -74,15 +74,15 @@ private:
FontInfo _actualFont;
int _currentDpi;
std::function<void(wchar_t*)> _pfnWriteCallback;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
::Microsoft::WRL::ComPtr<HwndTerminalAutomationPeer> _uiaProvider;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer;
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine;
bool _focused{ false };
bool _uiaProviderInitialized{ false };
std::chrono::milliseconds _multiClickTime;
unsigned int _multiClickCounter{};

View File

@@ -0,0 +1,159 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "HwndTerminalAutomationPeer.hpp"
#include "../../types/UiaTracing.h"
#include <UIAutomationCoreApi.h>
#pragma warning(suppress : 4471) // We don't control UIAutomationClient
#include <UIAutomationClient.h>
using namespace Microsoft::Console::Types;
static constexpr wchar_t UNICODE_NEWLINE{ L'\n' };
// Method Description:
// - creates a copy of the provided text with all of the control characters removed
// Arguments:
// - text: the string we're sanitizing
// Return Value:
// - a copy of "sanitized" with all of the control characters removed
static std::wstring Sanitize(std::wstring_view text)
{
std::wstring sanitized{ text };
sanitized.erase(std::remove_if(sanitized.begin(), sanitized.end(), [](wchar_t c) {
return (c < UNICODE_SPACE && c != UNICODE_NEWLINE) || c == 0x7F /*DEL*/;
}),
sanitized.end());
return sanitized;
}
// Method Description:
// - verifies if a given string has text that would be read by a screen reader.
// - a string of control characters, for example, would not be read.
// Arguments:
// - text: the string we're validating
// Return Value:
// - true, if the text is readable. false, otherwise.
static constexpr bool IsReadable(std::wstring_view text)
{
for (const auto c : text)
{
if (c > UNICODE_SPACE)
{
return true;
}
}
return false;
}
void HwndTerminalAutomationPeer::RecordKeyEvent(const WORD vkey)
{
if (const auto charCode{ MapVirtualKey(vkey, MAPVK_VK_TO_CHAR) })
{
if (const auto keyEventChar{ gsl::narrow_cast<wchar_t>(charCode) }; IsReadable({ &keyEventChar, 1 }))
{
_keyEvents.emplace_back(keyEventChar);
}
}
}
// Implementation of IRawElementProviderSimple::get_PropertyValue.
// Gets custom properties.
IFACEMETHODIMP HwndTerminalAutomationPeer::GetPropertyValue(_In_ PROPERTYID propertyId,
_Out_ VARIANT* pVariant) noexcept
{
pVariant->vt = VT_EMPTY;
// Returning the default will leave the property as the default
// so we only really need to touch it for the properties we want to implement
if (propertyId == UIA_ClassNamePropertyId)
{
// IMPORTANT: Do NOT change the name. Screen readers like may be dependent on this being "WpfTermControl".
pVariant->bstrVal = SysAllocString(L"WPFTermControl");
if (pVariant->bstrVal != nullptr)
{
pVariant->vt = VT_BSTR;
}
}
else
{
// fall back to shared implementation
return TermControlUiaProvider::GetPropertyValue(propertyId, pVariant);
}
return S_OK;
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has changed and should be updated
// Arguments:
// - <none>
// Return Value:
// - <none>
void HwndTerminalAutomationPeer::SignalSelectionChanged()
{
UiaTracing::Signal::SelectionChanged();
LOG_IF_FAILED(UiaRaiseAutomationEvent(this, UIA_Text_TextSelectionChangedEventId));
}
// Method Description:
// - Signals the ui automation client that the terminal's output has changed and should be updated
// Arguments:
// - <none>
// Return Value:
// - <none>
void HwndTerminalAutomationPeer::SignalTextChanged()
{
UiaTracing::Signal::TextChanged();
LOG_IF_FAILED(UiaRaiseAutomationEvent(this, UIA_Text_TextChangedEventId));
}
// Method Description:
// - Signals the ui automation client that the cursor's state has changed and should be updated
// Arguments:
// - <none>
// Return Value:
// - <none>
void HwndTerminalAutomationPeer::SignalCursorChanged()
{
UiaTracing::Signal::CursorChanged();
LOG_IF_FAILED(UiaRaiseAutomationEvent(this, UIA_Text_TextSelectionChangedEventId));
}
void HwndTerminalAutomationPeer::NotifyNewOutput(std::wstring_view newOutput)
{
// Try to suppress any events (or event data)
// that is just the keypress the user made
auto sanitized{ Sanitize(newOutput) };
while (!_keyEvents.empty() && IsReadable(sanitized))
{
if (til::toupper_ascii(sanitized.front()) == _keyEvents.front())
{
// the key event's character (i.e. the "A" key) matches
// the output character (i.e. "a" or "A" text).
// We can assume that the output character resulted from
// the pressed key, so we can ignore it.
sanitized = sanitized.substr(1);
_keyEvents.pop_front();
}
else
{
// The output doesn't match,
// so clear the input stack and
// move on to fire the event.
_keyEvents.clear();
break;
}
}
// Suppress event if the remaining text is not readable
if (!IsReadable(sanitized))
{
return;
}
const auto sanitizedBstr = wil::make_bstr_nothrow(sanitized.c_str());
static auto activityId = wil::make_bstr_nothrow(L"TerminalTextOutput");
LOG_IF_FAILED(UiaRaiseNotificationEvent(this, NotificationKind_ActionCompleted, NotificationProcessing_All, sanitizedBstr.get(), activityId.get()));
}

View File

@@ -0,0 +1,42 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- HwndTerminalAutomationPeer.hpp
Abstract:
- This module provides UI Automation access to the HwndTerminal
to support both automation tests and accessibility (screen
reading) applications. This mainly interacts with TermControlUiaProvider
to allow for shared code with Windows Terminal accessibility providers.
Author(s):
- Carlos Zamora (CaZamor) 2022
--*/
#pragma once
#include "../types/TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h"
class HwndTerminalAutomationPeer :
public ::Microsoft::Console::Types::IUiaEventDispatcher,
public ::Microsoft::Terminal::TermControlUiaProvider
{
public:
void RecordKeyEvent(const WORD vkey);
IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp,
_Out_ VARIANT* pVariant) noexcept override;
#pragma region IUiaEventDispatcher
void SignalSelectionChanged() override;
void SignalTextChanged() override;
void SignalCursorChanged() override;
void NotifyNewOutput(std::wstring_view newOutput) override;
#pragma endregion
private:
std::deque<wchar_t> _keyEvents;
};

View File

@@ -15,9 +15,11 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="HwndTerminal.cpp" />
<ClCompile Include="HwndTerminalAutomationPeer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="HwndTerminal.hpp" />
<ClInclude Include="HwndTerminalAutomationPeer.hpp" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
@@ -39,6 +41,9 @@
<ProjectReference Include="$(SolutionDir)src\renderer\dx\lib\dx.vcxproj">
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\uia\lib\uia.vcxproj">
<Project>{48d21369-3d7b-4431-9967-24e81292cf63}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
@@ -55,4 +60,4 @@
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>
</Project>

View File

@@ -24,6 +24,9 @@
<ClCompile Include="HwndTerminal.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HwndTerminalAutomationPeer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
@@ -32,5 +35,8 @@
<ClInclude Include="HwndTerminal.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HwndTerminalAutomationPeer.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -9,3 +9,4 @@
#endif
#include <LibraryIncludes.h>
#include <UIAutomationCore.h>

View File

@@ -38,9 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
}
Monarch::~Monarch()
{
}
Monarch::~Monarch() = default;
uint64_t Monarch::GetPID()
{

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