Compare commits

...

87 Commits

Author SHA1 Message Date
Mike Griese
37af3f00fe Just try some things.
This is work that I was doing during #2455 to try and get the TabTests working again. It doesn't work, as much as I'd like them to.

  I have mail out to try and get them working but I think we're stumped right now.
2019-09-13 08:34:06 -07:00
Mike Griese
00bf9f6757 Presumably fixes #2455
Catch all calls to FindProfile and MakeSettings, which might fail if the profile doesn't exist
2019-09-12 13:15:40 -07:00
Mike Griese
3a89c6e5fa Add some tests for #2455 2019-09-12 13:12:55 -07:00
Martin Lopes
537258a60f Edits doc section Configuring Windows Terminal (#2719)
* Edits doc section `Configuring Windows Terminal`

* Converts into a procedure.
* Uses `⌵` character to replace the `down` UI element.

* Additional minor edit

Updates formatting, edits for brevity.

* Fixed json path

Added `8wekyb3d8bbwe` to file path.
2019-09-11 09:24:20 -07:00
kynapse
b5fe4ffd54 Update link to Background Images and Icons section (#2725) 2019-09-11 09:21:15 -07:00
James Holderness
12d2e170dd Correct the boundaries of the scrolling commands (#2505)
There are a number of VT escape sequences that rely on the `ScrollRegion`
function to scroll the viewport (RI, DL, IL, SU, SD, ICH, and DCH) , and all of
them have got the clipping rect or scroll boundaries wrong in some way,
resulting in content being scrolled off the screen that should have been
clipped, revealed areas not being correctly filled, or parts of the screen not
being moved that should have been. This PR attempts to fix all of those issues.

The `ScrollRegion` function is what ultimately handles the scrolling, but it's
typically called via the `ApiRoutines::ScrollConsoleScreenBufferWImpl` method,
and it's the callers of that method that have needed correcting.

One "mistake" that many of these operations made, was in setting a clipping
rect that was different from the scrolling rect. This should never have been
necessary, since the area being scrolled is also the boundary into which the
content needs to be clipped, so the easiest thing to do is just use the same
rect for both parameters.

Another common mistake was in clipping the horizontal boundaries to the width
of the viewport. But it's really the buffer width that represents the active
width of the screen - the viewport width and offset are merely a window on that
active area. As such, the viewport should only be used to clip vertically - the
horizontal extent should typically be the full buffer width.

On that note, there is really no need to actually calculate the buffer width
when we want to set any of the scrolling parameters to that width. The
`ScrollRegion` function already takes care of clipping everything within the
buffer boundary, so we can simply set the `Left` of the rect to `0` and the
`Right` to `SHORT_MAX`.

More details on individual commands:

* RI (the `DoSrvPrivateReverseLineFeed` function)
  This now uses a single rect for both the scroll region and clipping boundary,
  and the width is set to `SHORT_MAX` to cover the full buffer width. Also the
  bottom of the scrolling region is now the bottom of the viewport (rather than
  bottom-1), otherwise it would be off by one.

* DL and IL (the `DoSrvPrivateModifyLinesImpl` function)
  Again this uses a single rect for both the scroll region and clipping
  boundary, and the width is set to `SHORT_MAX` to cover the full width. The
  most significant change, though, is that the bottom boundary is now the
  viewport bottom rather than the buffer bottom. Using the buffer bottom
  prevented it clipping the content that scrolled off screen when inserting,
  and failed to fill the revealed area when deleting.

* SU and SD (the `AdaptDispatch::_ScrollMovement` method)
  This was already using a single rect for both the scroll region and clipping
  boundary, but it was previously constrained to the width of the viewport
  rather than the buffer width, so some areas of the screen weren't correctly
  scrolled. Also, the bottom boundary was off by 1, because it was using an
  exclusive rect while the `ScrollRegion` function expects inclusive rects.

* ICH and DCH (the `AdaptDispatch::_InsertDeleteHelper` method)
  This method has been considerably simplified, because it was reimplementing a
  lot of functionality that was already provided by the `ScrollRegion`
  function. And like many of the other cases, it has been updated to use a
  single rect for both the scroll region and clipping boundary, and clip to the
  full buffer width rather than the viewport width.

I should add that if we were following the specs exactly, then the SU and SD
commands should technically be panning the viewport over the buffer instead of
moving the buffer contents within the viewport boundary. So SU would be the
equivalent of a newline at the bottom of the viewport (assuming no margins).
And SD would assumedly do the opposite, scrolling the back buffer back into
view (an RI at the top of the viewport should do the same).

This doesn't seem to be something that is consistently implemented, though.
Some terminals do implement SU as a viewport pan, but I haven't seen anyone
implement SD or RI as a pan. If we do want to do something about this, I think
it's best addressed as a separate issue.

## Validation Steps Performed

There were already existing tests for the SU, SD, ICH, and DCH commands, but
they were implemented as adapter tests, which weren't effectively testing
anything - the `ScrollConsoleScreenBufferW` method used in those tests was just
a mock (an incomplete reimplementation of the `ScrollRegion` function), so
confirming that the mock produced the correct result told you nothing about the
validity of the real code.

To address that, I've now reimplemented those adapter tests as screen buffer
tests. For the most part I've tried to duplicate the functionality of the
original tests, but there are significant differences to account for the fact
that scrolling region now covers the full width of the buffer rather than just
the viewport width.

I've also extended those tests with additional coverage for the RI, DL, and IL
commands, which are really just a variation of the SU and SD functionality.

Closes #2174
2019-09-10 18:20:46 -07:00
Fredi Machado
2da3b49c9e Fix json settings documentation (#2699)
* Fix json settings documentation

The ctrl+c issue was fixed in [#2446](https://github.com/microsoft/terminal/pull/2446)

* Update UsingJsonSettings.md
2019-09-10 16:08:15 -07:00
Michael Niksa
2063197605 Add SECURITY.md to repo (#2720)
Open Source program office guidelines encourage us to add this information to our repository. So I'm doing it.

Sourced from https://github.com/microsoft/microsoft.github.io/blob/master/SECURITY.MD
2019-09-10 15:56:50 -07:00
Carlos Zamora
2ac24979da Stylus Selection Support (#2586) 2019-09-10 10:29:31 -06:00
Michael Niksa
429af0e6fa Merge pull request #2607 from microsoft/dev/miniksa/audit-a
Crank up static analysis audit
2019-09-09 17:12:09 -07:00
Michael Niksa
18bacfe973 A few PR comments. A constexpr here, a misleading comment there, and an extraneous local. 2019-09-09 16:01:28 -07:00
Mike Griese
bac69f7cab When inserting/deleting lines, preserve RGB/256 attributes (#2668)
* This fixes #832 by not mucking with roundtripping attributes. Still needs a test

* Add a test

* Lets just make this test test everything

  @miniksa https://media0.giphy.com/media/d7mMzaGDYkz4ZBziP6/giphy.gif

* Remove dead code
2019-09-09 15:06:50 +00:00
Mike Griese
ce34c7320c Prevent "Options" propsheet from reverting cursor shape settings (#2663)
* this actually fixes #1219

* the terminal page should check the checkbox on the options page

* Discard these changes from #2651

* Add comments, pull function out to helper
2019-09-09 14:45:05 +00:00
Martin Lopes
badbbc43a4 doc: amend docs procedure for Running a Different Shell (#2605)
* Amends user-docs procedure

Amends docs procedure for `Running a Different Shell`:
* Adds an overview sentence.
* Adds some light rephrasing.
* Proposes using the countersink arrow `⌵` to depict the `down` GUI element.

* Adds link to WSL installation guide
2019-09-08 19:20:17 -07:00
Michael Niksa
fecddafad5 Changed feedback hub request rule (#2680)
We were using a tag to trigger the bot for the verbose feedback hub response.

But...
1. We have run into several instances of the bot aggressively replying multiple times before the tag is removed.
2. We asked for a "comment contains" function in the bot and the Fabric Bot team obliged.

So I've changed it to `/duplicate` from the tag trigger and will remove the tag.
2019-09-06 08:55:13 -05:00
Michael Niksa
d8ff47a0d3 Some of the PR feedback. 2019-09-05 17:21:54 -07:00
Mike Griese
125e1771ae Add some logging around startup, connection start timing (#2544)
Adds a number of TL events we can use to track startup time better. Adds events for:
* Initial exe start
* Time the window is created
* time we start loading settings
* time we finish loading setings
* time when a connection recieves its first byte

Also updates our `ConnectionCreated` event to include the session GUID, so that we can correlate that with the connection's `RecievedFirstByte` event.
2019-09-05 15:38:42 -05:00
Mike Griese
c58033cda2 Don't crash when restore-down'ing the alt buffer (#2666)
## Summary of the Pull Request

When a user had "Disable Scroll Forward" enabled and switched to the alt buffer and maximized the console, then restored down, we'd crash. Now we don't.

## References

## PR Checklist
* [x] Closes #1206 
* [x] I work here
* [x] Tests added/passed

## Detailed Description of the Pull Request / Additional comments

The problem is that we'd previously try to "anchor" the viewport to the virtual bottom when resizing like this. This would also cause us to move the top of the viewport down, into the buffer. However, if the alt buffer is getting smaller, we don't want to do this - if we anchor to the old _virtualBottom, the bottom of the viewport will actually be outside the current buffer.

This could theoretically happen with the main buffer too, but it's much easier to repro with the alt buffer.
2019-09-05 15:37:27 -05:00
Michael Niksa
fc81adf32f use the array size for the read bounds. using extent on the newly-converted-to-array type doesn't give the correct value. 2019-09-05 13:09:36 -07:00
Michael Niksa
689c21e802 PR feedback. 2019-09-05 11:17:13 -07:00
Michael Niksa
96cc7727bc Add GH issue IDs to all the suppress/disables that I left behind as they were a bit too challenging to solve with this giant PR 2019-09-05 11:14:43 -07:00
Michael Niksa
886d018bb4 warnings as errors for cppwinrt projects, then fix the warnings (#2660)
Fixes #1155.
2019-09-04 16:43:45 -07:00
Michael Niksa
d0c207bc9c fix remaining issues that appeared on merge. 2019-09-04 15:45:22 -07:00
Kaiyu Wang
ce3028e12f Clean up boundary between terminal app and terminal page (#2208)
* change 1: add settings pointer and some member variables to page

* clean up the boundary between Page and App - First working version

* First CR review change

* Sync and remove declaration of TraceLogger provider

* Code review round 2 - apply missed new changes

* remove useless comment

* CR change round 3

* CR minor changes

* apply changes from Aug 6th to Aug 14th

* Code review changes round 4

* Apply changes on Aug 16

* Cr changes on 8/20

* CR changes on 8-26

* correct syncing mistakes and fix formatting issues

* CR changes on 8-29

* CR changes 9-4

* apply new changes of App

* Format fix
2019-09-04 14:34:06 -07:00
Michael Niksa
b7c1e05060 code formatter, you're killing me. 2019-09-04 13:40:10 -07:00
Michael Niksa
3bff2a3eb0 fix merge conflict with master 2019-09-04 13:35:31 -07:00
Michael Niksa
7c66e66ca1 Fix redefinition of class name for constexpr method I moved from CPP to HPP. 2019-09-04 12:49:15 -07:00
Dustin L. Howett (MSFT)
e0762f6bb3 Open-source the PseudoConsole family of functions in a new DLL (#2611)
This pull request introduces a copy of the code from kernel32.dll that
implements CreatePseudoConsole, ClosePseudoConsole and
ResizePseudoConsole. Apart from some light modifications to fit into the
infrastructure in this project and support launching OpenConsole.exe, it
is intended to be 1:1 with the code that ships in Windows.

Any guideline violations in this code are likely intentional. Since this
was built into kernel32, it uses the STL only _very sparingly._

Consumers of this library must make sure that conpty.lib lives earlier
in the link line than onecoreuap_apiset, onecoreuap, onecore_apiset,
onecore or kernel32.

Refs #1130.
2019-09-04 12:03:44 -07:00
Konstantin Yakushev
51f53535d1 Add support for short hex color codes like #CCC (#2658)
This adds a few lines to support shorthand color hex codes like #ABC. They are treated as equivalent of #AABBCC.

Fixes #2639.
2019-09-04 11:45:35 -07:00
Rich Turner
21067a7629 Fixes #1918 - Added docs for image/icon settings & paths (#2545)
* Fixes #1918 - Added docs for image/icon settings & paths

* Described URI Schemes & their use
* Added guidance re. background images
* Added notes re. icons (inc. sizing)
* Added example JSON & screenshot of background & icon
2019-09-04 11:21:39 -07:00
Michael Niksa
7d9534bfa8 constexprs have to go into the headers or other usages can't find them. Imagine that. 2019-09-04 10:59:18 -07:00
Michael Niksa
6735311fc9 Suppress last two errors (C26455 default constructor throw in DxEngine because it's due for refactoring soon anyway & C26444 custom construction/destruction on OutputCellIterator because I can't see what's going on and it needs more investigation and shouldn't hold this up). Also run codeformat. 2019-09-03 16:18:19 -07:00
Michael Niksa
4204733c34 C26481, don't use pointer arithmetic. Convert to measuring string within known limit and using view. 2019-09-03 15:48:02 -07:00
Michael Niksa
23b4a466f5 C26429, C26481, don't use pointer arithmetic, test for nullness. Also eliminated completely unused GetTextRaw. Left todo behind for pointers as iterator boundaries in CharRowCellReference to fix later. 2019-09-03 15:41:37 -07:00
Michael Niksa
01bd77003c C26429, mark as not_null if not testing for nullness. 2019-09-03 15:23:44 -07:00
Michael Niksa
ae25a32913 C26497, you can mark this thing as constexpr. 2019-09-03 15:18:01 -07:00
Michael Niksa
93aa9455e2 C26429, test for nullness or mark as not_null (and a few cascading warnings. 2019-09-03 15:14:44 -07:00
Michael Niksa
41f209f6d3 C26440, default constructors should be noexcept. 2019-09-03 15:10:33 -07:00
Michael Niksa
244fb72fee C26490, no reinterpret_cast. Just use the actual struct and copy instead of relying on the wink/nudge fact they're defined the same way. 2019-09-03 15:09:30 -07:00
Michael Niksa
3a0da64276 C26490, no reinterpret_cast. Suppress on OutputCellIterator because fixing it will make trouble in the Windows build if we're not careful thanks to non-differentiation of wchar_t and DWORD. 2019-09-03 15:08:48 -07:00
Michael Niksa
b2c093fa2f C26455, default constructor may not throw, mark as nothrow (another trivial one) 2019-09-03 15:04:42 -07:00
Michael Niksa
87f5852a72 Define actual constructor for CodepointWidthDetector as default isn't cutting it. 2019-09-03 15:03:54 -07:00
Michael Niksa
e14a59a1b6 C26455, default constructor may not throw. Mark noexcept. (Trivial cases.) 2019-09-03 14:57:14 -07:00
Michael Niksa
cd144e98c6 C26436, destructor definition required for class with virtual methods. 2019-09-03 14:52:00 -07:00
Michael Niksa
c7f0a3439d C26490, don't reinterpret_cast. It looks like the buffer can easily be char. Also use brace initialization per feedback. 2019-09-03 14:39:23 -07:00
Michael Niksa
5d60d69e86 C26426, global initializer calls non-constexpr. This is an easy move to wstring_view. 2019-09-03 14:33:00 -07:00
Michael Niksa
072bbfd09d C26426, global initializer calls non-constexpr. This needs further consideration. I brifely tried to turn GlyphWidth into a singleton class but it cascaded into interesting far corners of the code because IsGlyphFullWidth was liberally used everywhere for a long time. I'm punting here to a future work item. 2019-09-03 14:32:44 -07:00
Michael Niksa
b87f8f9070 C26426, global initializers calling non-constexpr. Suppress for default settings as changing to wstring_view cascades through the entire codebase (non-trivial, string_views aren't guaranteed as Z terminated.) 2019-09-03 14:30:40 -07:00
Michael Niksa
b78d9176ae C26434, do not hide base class methods. Overriding this one because it's going to require design changes that need a future todo. 2019-09-03 14:22:02 -07:00
Michael Niksa
3bbd8f4c97 C26443, overriding destructors shouldn't declare virtual nor override. 2019-09-03 14:14:07 -07:00
Michael Niksa
2d3f285894 C26432, rule-of-five (if you define one of destruct/copy/move, then define them all) 2019-09-03 13:45:16 -07:00
Michael Niksa
49ff36bfc3 Reflect inbox changes in 8c63dff
[Git2Git] Git Train: Merge of building/rs_onecore_dep_uxp/190820-1847 into official/rs_onecore_dep_uxp Retrieved from https://microsoft.visualstudio.com os OS official/rs_onecore_dep_uxp 73e964d4046c37df3030970cae1ae32e83103fb5

(cherry picked from commit 8c63dff982093db1af7e2bb46b49af884dfec0c5)
2019-09-03 13:32:31 -07:00
Michael Niksa
d8bc94f13c forgot all return paths to _FillRectangle. 2019-09-03 13:30:03 -07:00
Michael Niksa
dd49c3ed51 C26460, use const on params that are unchanged (and remove some unnecessary span refs). 2019-09-03 13:02:09 -07:00
Michael Niksa
9678dd894c C26414, don't use smart pointers for locals 2019-09-03 11:27:43 -07:00
Michael Niksa
45e599368f C26430, not tested for nullness on all paths. I will just always check for null as a defense against a bad QI implementation. 2019-09-03 11:20:27 -07:00
Michael Niksa
594dca993b C26429, mark gsl::not_null on places where we don't test for null (shouldn't need to, internal methods only. 2019-09-03 11:18:28 -07:00
Michael Niksa
c956913a28 C26497, use constexpr for functions that could be evaluated at compile time. 2019-09-03 10:30:06 -07:00
Michael Niksa
b180406b07 C26445, wstring_view byref may indicate a lifetime issue 2019-09-03 10:19:59 -07:00
Michael Niksa
bbdfdf91eb C26462, const local variables that are unchanged. 2019-09-03 10:04:30 -07:00
Michael Niksa
d5d7cf420d C26494, uninitalized local variables 2019-09-03 10:02:18 -07:00
Michael Niksa
81ab5803aa C26473, do not cast pointer back to the same type. 2019-09-03 09:44:19 -07:00
Michael Niksa
7d4096bbbf C26485, refactor to avoid array-to-pointer decay. 2019-09-03 09:40:31 -07:00
Michael Niksa
230e7f43e0 C26466, disable dynamic_cast rule because we're not RTTI due to OS policy. Also reinstitute C6001 and C6011 because they're not actually a part of the 'core checks' and they're goodness we had before I turned them off at the beginning of this series. 2019-09-03 09:15:49 -07:00
Michael Niksa
cdfbf8f106 C26474, don't use static_cast when an implicit cast is acceptable. 2019-09-03 08:53:54 -07:00
Michael Niksa
30e8e7f3a3 C26429, symbols not tested for nullness. 2019-09-03 08:46:24 -07:00
Dustin L. Howett (MSFT)
feb5b18296 doc: move cascadia specs and rename them to spec format (#2593) 2019-08-29 17:32:27 -07:00
Carlos Zamora
7ec6bfc01c catch failure to open clipboard (#2590) 2019-08-29 17:31:53 -07:00
Michael Niksa
4f1157c044 C26447,C26440 - is noexcept but can throw or doesn't throw but not noexcept 2019-08-29 15:23:07 -07:00
Michael Niksa
8c3a629b52 C26481, don't use pointer arithemetic. use span. 2019-08-29 14:08:47 -07:00
Michael Niksa
8579d8905a C26451, promote before arithmetic if storing in larger result size (or use safe math) 2019-08-29 13:41:51 -07:00
Michael Niksa
50e2d0c433 C26433, overrides should be explicit. 2019-08-29 13:23:32 -07:00
Michael Niksa
8ea7401dc9 C26472, no static_cast for arithmetic conversions. narrow or narrow_cast 2019-08-29 13:19:01 -07:00
Michael Niksa
a381f6a042 C26435, choose one of virtual, override, or final 2019-08-29 13:07:08 -07:00
Michael Niksa
c63289b114 C26493, no C-style casts. 2019-08-29 12:45:16 -07:00
Michael Niksa
b33a59816e C26496, mark const if it's never written after creation 2019-08-29 11:27:39 -07:00
Michael Niksa
bd2d5ddb4b C26477, don't use 0 or NULL, use nullptr. 2019-08-29 11:12:55 -07:00
Michael Niksa
23897b1bd4 [Complex] C26446, Use .at instead of array indices - Reword UTF8OutPipeReader to use std::array so we can use .at and move some pointers to iterators. 2019-08-29 11:09:44 -07:00
Michael Niksa
65dec36cb1 C26446, Use .at instead of array indices 2019-08-29 11:05:32 -07:00
Michael Niksa
1989eb9d00 Make warnings errors for static analysis. 2019-08-29 10:27:29 -07:00
Kayla Cinnamon
cb02ca7534 Changed default padding to 8,8,8,8 and default font size to 11 (#2378)
* changed default padding to 5,5,5,5 and default font size to 11

* updated documentation

* changed padding to 8
2019-08-29 09:47:01 -07:00
brightbluejay
5de63096ac Update building.md (#2501)
minor spelling corrections
2019-08-29 09:46:32 -07:00
Michael Niksa
f93adb9540 Added more bot rules (#2502)
* Added more bot rules

* Update bot.md
2019-08-29 09:45:02 -07:00
drebelsky
0d12a25b2d Fix typo (#2538)
changed "an file" to "a file"
2019-08-29 09:41:10 -07:00
Martin Lopes
5e38bcd754 Fixed typo in user-docs (#2592)
Fixed typo "the the".
2019-08-28 17:43:29 -07:00
Richard Szalay
f4294b17d7 Clean up Pane (#2494)
* Merge pane splitting methods

Having separate Horizontal/Vertical versions made it hard to manage, and App.cpp already made use of Pane::SplitState so it made sense to have that be the descriminator

* Rename Tab::(Can)AddSplit to (Can)SplitPane to align with Pane methods

Split was used as a noun in Tab but a verb in Pane, which felt odd

* Remove unused local variable in Pane::_CanSplit

* Remove redundant 'else' branches in Pane

Improves readibility for all 'low hanging fruit' cases where the 'if' was returning.
2019-08-28 07:40:16 -07:00
James Holderness
974e95ebf7 Make the RIS command clear the display and scrollback correctly (#2367)
When the scrollback buffer is empty, the RIS escape sequence (Reset to Initial
State) will fail to clear the screen, or reset any of the state. And when there
is something in the scrollback, it doesn't get cleared completely, and the
screen may get filled with the wrong background color (it should use the
default color, but it actually uses the previously active background color).
This commit attempts to fix those issues.

The initial failure is caused by the `SCREEN_INFORMATION::WriteRect` method
throwing an exception when passed an empty viewport. And the reason it's passed
an empty viewport is because that's what the `Viewport::Subtract` method
returns when the result of the subtraction is nothing.  The PR fixes the
problem by making the `Viewport::Subtract` method actually return nothing in
that situation. 

This is a change in the defined behavior that also required the associated
viewport tests to be updated. However, it does seem a sensible change, since
the `Subtract` method never returns empty viewports under any other
circumstances. And the only place the method seems to be used is in the
`ScrollRegion` implementation, where the previous behavior is guaranteed to
throw an exception.

The other issues are fixed simply by changing the order in which things are
reset in the `AdaptDispatch::HardReset` method. The call to `SoftReset` needed
to be made first, so that the SGR attributes would be reset before the screen
was cleared, thus making sure that the default background color would be used.
And the screen needed to be cleared before the scrollback was erased, otherwise
the last view of the screen would be retained in the scrollback buffer.

These changes also required existing adapter tests to be updated, but not
because of a change in the expected behaviour. It's just that certain tests
relied on the `SoftReset` happening later in the order, so weren't expecting it
to be called if say the scrollback erase had failed. It doesn't seem like the
tests were deliberately trying to verify that the SoftReset _hadn't_ been
called.

In addition to the updates to existing tests, this PR also add a new screen
buffer test which verifies the display and scrollback are correctly cleared
under the conditions that were previously failing.

Fixes #2307.
2019-08-27 18:45:38 -07:00
142 changed files with 5080 additions and 3839 deletions

View File

@@ -248,6 +248,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "s
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\winconpty.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|ARM64 = AuditMode|ARM64
@@ -1029,6 +1031,21 @@ Global
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.ActiveCfg = Release|ARM64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.ActiveCfg = Release|x64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.ActiveCfg = Release|Win32
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.Build.0 = Debug|ARM64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.ActiveCfg = Debug|x64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.Build.0 = Debug|x64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.ActiveCfg = Debug|Win32
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.Build.0 = Debug|Win32
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.ActiveCfg = Release|ARM64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.Build.0 = Release|ARM64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.ActiveCfg = Release|x64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.Build.0 = Release|x64
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.ActiveCfg = Release|Win32
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1090,6 +1107,7 @@ Global
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202}
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

41
SECURITY.md Normal file
View File

@@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.2 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View File

@@ -14,6 +14,7 @@
"/.vs/",
"/build/",
"/src/cascadia/",
"/src/winconpty/",
"/.nuget/",
"/.github/",
"/samples/"

View File

@@ -33,6 +33,17 @@ We'll be using tags, primarily, to help us understand what needs attention, what
## Rules
### Triage Shorthand
- All rules in this category apply to triaging issues. They're shorthand comments that the triage team can use in order to complete the triage process faster.
- Only individuals with `Write` or `Admin` privileges on the repository can use these responses.
#### Duplicate Issues
- When a comment on the thread says `/dup #<issue ID>`...
1. Reply with a comment explaining that the issue is a duplicate and recommend that the opener and interested parties follow the issue on the listed ID number.
1. Close the issue
1. Remove all `Needs-*` tags
1. Add `Resolution-Duplicate`
### Issue Management
#### Mark as Triage Needed
@@ -65,10 +76,12 @@ We'll be using tags, primarily, to help us understand what needs attention, what
- Then close the issue automatically informing the opener that they can resolve the problem and reopen the issue. (See Bug/Feature templates for example situations.)
#### Help ask for Feedback Hub
- If an issue is tagged `Needs-Feedback-Hub`
- Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link.
- And remove the `Needs-Feedback-Hub` tag
- And add the `Needs-Author-Feedback` tag
- When a comment on the thread says `/feedback`...
1. Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link.
1. And add the `Needs-Author-Feedback` tag
#### Remove Help Wanted from In PR issues
- If an issue gets the `In-PR` tag when a new PR is created, we will remove the `Help-Wanted` tag to avoid someone trying to work on an issue where another person has already submitted a proposed fix.
### PR Management

View File

@@ -1,7 +1,7 @@
# How to build Openconsole
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and powershell in /tools.
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and PowerShell in /tools.
When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box.
@@ -33,4 +33,4 @@ Openconsole has three configuration types:
- Release
- AuditMode
AuditMode is an experimental mode that enables some additional static analyis from CppCoreCheck.
AuditMode is an experimental mode that enables some additional static analysis from CppCoreCheck.

View File

@@ -27,11 +27,11 @@ Properties listed below are specific to each unique profile.
| `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
| `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( &#x2583; ), `"bar"` ( &#x2503; ), `"underscore"` ( &#x2581; ), `"filledBox"` ( &#x2588; ), `"emptyBox"` ( &#x25AF; ) |
| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
| `fontSize` | _Required_ | Integer | `10` | Sets the font size. |
| `fontSize` | _Required_ | Integer | `12` | Sets the font size. |
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
| `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
| `name` | _Required_ | String | `PowerShell Core` | Name of the profile. Displays in the dropdown menu. <br>Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overriden by using `tabTitle`. |
| `padding` | _Required_ | String | `0, 0, 0, 0` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
| `padding` | _Required_ | String | `8, 8, 8, 8` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
| `snapOnInput` | _Required_ | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. |
| `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
| `useAcrylic` | _Required_ | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. |
@@ -43,7 +43,7 @@ Properties listed below are specific to each unique profile.
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Colors use hex color format: `"#rrggbb"`. Ordering is as follows: `[black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]` |
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. See [Background Images and Icons](./SettingsSchema.md#background-images-and-icons) below for help on specifying your own icons |
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
| `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. |
@@ -133,3 +133,48 @@ Bindings listed below are per the implementation in `src/cascadia/TerminalApp/Ap
- moveFocusUp
- moveFocusDown
## Background Images and Icons
Some Terminal settings allow you to specify custom background images and icons. It is recommended that custom images and icons are stored in system-provided folders and are referred to using the correct [URI Schemes](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes). URI Schemes provide a way to reference files independent of their physical paths (which may change in the future).
The most useful URI schemes to remember when customizing background images and icons are:
| URI Scheme | Corresponding Physical Path | Use / description |
| --- | --- | ---|
| `ms-appdata:///Local/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\` | Per-machine files |
| `ms-appdata:///Roaming/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState\` | Common files |
> ⚠ Note: Do not rely on file references using the `ms-appx` URI Scheme (i.e. icons). These files are considered an internal implementation detail and may change name/location or may be omitted in the future.
### Icons
Terminal displays icons for each of your profiles which Terminal generates for any built-in shells - PowerShell Core, PowerShell, and any installed Linux/WSL distros. Each profile refers to a stock icon via the `ms-appx` URI Scheme.
> ⚠ Note: Do not rely on the files referenced by the `ms-appx` URI Scheme - they are considered an internal implementation detail and may change name/location or may be omitted in the future.
You can refer to you own icons if you wish, e.g.:
```json
"icon" : "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\icon-ubuntu-32.png",
```
> 👉 Tip: Icons should be sized to 32x32px in an appropriate raster image format (e.g. .PNG, .GIF, or .ICO) to avoid having to scale your icons during runtime (causing a noticeable delay and loss of quality.)
### Custom Background Images
You can apply a background image to each of your profiles, allowing you to configure/brand/style each of your profiles independently from one another if you wish.
To do so, specify your preferred `backgroundImage`, position it using `backgroundImageAlignment`, set its opacity with `backgroundImageOpacity`, and/or specify how your image fill the available space using `backgroundImageStretchMode`.
For example:
```json
"backgroundImage": "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\bg-ubuntu-256.png",
"backgroundImageAlignment": "bottomRight",
"backgroundImageOpacity": 0.1,
"backgroundImageStretchMode": "none"
```
> 👉 Tip: You can easily roam your collection of images and icons across all your machines by storing your icons and images in OneDrive (as shown above).
With these settings, your Terminal's Ubuntu profile would look similar to this:
![Custom icon and background image](../images/custom-icon-and-background-image.jpg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

View File

@@ -1,6 +1,8 @@
---
author: "Mike Griese @zadjii-msft"
created on: 2019-May-16
created on: 2019-05-16
last updated: 2019-07-07
issue id: 523
---
# Panes in the Windows Terminal

View File

@@ -171,5 +171,5 @@ You can even set multiple keybindings for a single action if you'd like. For exa
will bind both <kbd>ctrl+shift+v</kbd> and
<kbd>shift+Insert</kbd> to `paste`.
Note: If you set your copy keybinding to `"ctrl+c"`, you won't be able to send an interrupt to the commandline application using <kbd>Ctrl+C</kbd>. This is a bug, and being tracked by [#2258](https://github.com/microsoft/terminal/issues/2285).
Note: If you set your copy keybinding to `"ctrl+c"`, you'll only be able to send an interrupt to the commandline application using <kbd>Ctrl+C</kbd> when there's no text selection.
Additionally, if you set `paste` to `"ctrl+v"`, commandline applications won't be able to read a ctrl+v from the input. For these reasons, we suggest `"ctrl+shift+c"` and `"ctrl+shift+v"`

View File

@@ -36,12 +36,14 @@ default shell is displayed (default shortcut `Ctrl+Shift+1`).
## Running a Different Shell
Note: The following text assumes you have WSL installed.
Note: This section assumes you already have _Windows Subsystem for Linux_ (WSL) installed. For more information, see [the installation guide](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
To choose a different shell (e.g. `cmd.exe` or WSL `bash`) then
Windows Terminal uses PowerShell as its default shell. You can also use Windows Terminal to launch other shells, such as `cmd.exe` or WSL's `bash`:
1. Select the `down` button next to the `+` in the tab bar
2. Choose your new shell from the list (more on how to extend the list in the config section)
1. In the tab bar, click the `` button to view the available shells.
2. Choose your shell from the dropdown list. The new shell session will open in a new tab.
To customize the shell list, see the _Configuring Windows Terminal_ section below.
## Starting a new PowerShell tab with admin privilege
@@ -65,16 +67,15 @@ Not currently supported "out of the box". See issue [#1060](https://github.com/m
## Configuring Windows Terminal
At the time of writing all Windows Terminal settings are managed via a json file.
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/RoamingState`.
From the `down` button in the top bar select Settings (default shortcut `Ctrl+,`).
To open the settings file from Windows Terminal:
Your default json editor will open up the Terminal settings file. The file can be found
at `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>/RoamingState`
1. Click the `⌵` button in the top bar.
2. From the dropdown list, click `Settings`. You can also use a shortcut: `Ctrl+,`.
3. Your default `json` editor will open the settings file.
An introduction to the the various settings can be found [here](UsingJsonSettings.md).
The list of valid settings can be found in the [Profiles.json Documentation](../cascadia/SettingsSchema.md) doc.
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section.
## Tips and Tricks:

View File

@@ -1,11 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Console Rules" Description="These rules enforce static analysis on console code." ToolsVersion="15.0">
<Include Path="cppcorecheckrules.ruleset" Action="Default" />
<Include Path="cppcorecheckrules.ruleset" Action="Error" />
<Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
<Rule Id="C6001" Action="Error" />
<Rule Id="C6011" Action="Error" />
<Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
<Rule Id="C6001" Action="Error" />
<Rule Id="C6011" Action="Error" />
<!-- We can't do dynamic cast because RTTI is off. -->
<!-- RTTI is off because Windows OS policies believe RTTI has too much binary size impact for the value and is less portable than RTTI-off modules. -->
<Rule Id="C26466" Action="None" />
</Rules>
</RuleSet>

View File

@@ -46,7 +46,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
{
// Get the attribute that covers the final column of old width.
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
auto& run = _list[runPos];
auto& run = _list.at(runPos);
// Extend its length by the additional columns we're adding.
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
@@ -60,7 +60,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
// Get the attribute that covers the final column of the new width
size_t CountOfAttr = 0;
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
auto& run = _list[runPos];
auto& run = _list.at(runPos);
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
@@ -108,7 +108,7 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
{
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
const auto runPos = FindAttrIndex(column, pApplies);
return _list[runPos].GetAttributes();
return _list.at(runPos).GetAttributes();
}
// Routine Description:
@@ -290,10 +290,10 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
// two elements in our internal list.
else if (_list.size() == 2 && newAttrs.at(0).GetLength() == 1)
{
auto left = _list.begin();
const auto left = _list.begin();
if (iStart == left->GetLength() && NewAttr == left->GetAttributes())
{
auto right = left + 1;
const auto right = left + 1;
left->IncrementLength();
right->DecrementLength();

View File

@@ -6,21 +6,21 @@
#include "AttrRowIterator.hpp"
#include "AttrRow.hpp"
AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow)
AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow) noexcept
{
AttrRowIterator it{ attrRow };
it._setToEnd();
return it;
}
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) :
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept :
_pAttrRow{ attrRow },
_run{ attrRow->_list.cbegin() },
_currentAttributeIndex{ 0 }
{
}
AttrRowIterator::operator bool() const noexcept
AttrRowIterator::operator bool() const
{
return _run < _pAttrRow->_list.cend();
}
@@ -139,7 +139,7 @@ void AttrRowIterator::_decrement(size_t count)
// Routine Description:
// - sets fields on the iterator to describe the end() state of the ATTR_ROW
void AttrRowIterator::_setToEnd()
void AttrRowIterator::_setToEnd() noexcept
{
_run = _pAttrRow->_list.cend();
_currentAttributeIndex = 0;

View File

@@ -29,11 +29,11 @@ public:
using pointer = TextAttribute*;
using reference = TextAttribute&;
static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow);
static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow) noexcept;
AttrRowIterator(const ATTR_ROW* const attrRow);
AttrRowIterator(const ATTR_ROW* const attrRow) noexcept;
operator bool() const noexcept;
operator bool() const;
bool operator==(const AttrRowIterator& it) const;
bool operator!=(const AttrRowIterator& it) const;
@@ -57,5 +57,5 @@ private:
void _increment(size_t count);
void _decrement(size_t count);
void _setToEnd();
void _setToEnd() noexcept;
};

View File

@@ -84,7 +84,7 @@ size_t CharRow::size() const noexcept
// - sRowWidth - The width of the row.
// Return Value:
// - <none>
void CharRow::Reset()
void CharRow::Reset() noexcept
{
for (auto& cell : _data)
{
@@ -209,7 +209,7 @@ const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
// Note: will throw exception if column is out of bounds
DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
{
return const_cast<DbcsAttribute&>(static_cast<const CharRow* const>(this)->DbcsAttrAt(column));
return _data.at(column).DbcsAttr();
}
// Routine Description:
@@ -250,29 +250,6 @@ CharRow::reference CharRow::GlyphAt(const size_t column)
return { *this, column };
}
// Routine Description:
// - returns string containing text data exactly how it's stored internally, including doubling of
// leading/trailing cells.
// Arguments:
// - none
// Return Value:
// - text stored in char row
// - Note: will throw exception if out of memory
std::wstring CharRow::GetTextRaw() const
{
std::wstring wstr;
wstr.reserve(_data.size());
for (size_t i = 0; i < _data.size(); ++i)
{
auto glyph = GlyphAt(i);
for (auto it = glyph.begin(); it != glyph.end(); ++it)
{
wstr.push_back(*it);
}
}
return wstr;
}
std::wstring CharRow::GetText() const
{
std::wstring wstr;
@@ -280,24 +257,24 @@ std::wstring CharRow::GetText() const
for (size_t i = 0; i < _data.size(); ++i)
{
auto glyph = GlyphAt(i);
const auto glyph = GlyphAt(i);
if (!DbcsAttrAt(i).IsTrailing())
{
for (auto it = glyph.begin(); it != glyph.end(); ++it)
for (const auto wch : glyph)
{
wstr.push_back(*it);
wstr.push_back(wch);
}
}
}
return wstr;
}
UnicodeStorage& CharRow::GetUnicodeStorage()
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& CharRow::GetUnicodeStorage() const
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
}
@@ -308,7 +285,7 @@ const UnicodeStorage& CharRow::GetUnicodeStorage() const
// - column - the column to generate the key for
// Return Value:
// - the COORD key for data access from UnicodeStorage for the column
COORD CharRow::GetStorageKey(const size_t column) const
COORD CharRow::GetStorageKey(const size_t column) const noexcept
{
return { gsl::narrow<SHORT>(column), _pParent->GetId() };
}

View File

@@ -53,7 +53,7 @@ public:
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept;
bool WasDoubleBytePadded() const noexcept;
size_t size() const noexcept;
void Reset();
void Reset() noexcept;
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
size_t MeasureLeft() const;
size_t MeasureRight() const noexcept;
@@ -64,9 +64,6 @@ public:
void ClearGlyph(const size_t column);
std::wstring GetText() const;
// other functions implemented at the template class level
std::wstring GetTextRaw() const;
// working with glyphs
const reference GlyphAt(const size_t column) const;
reference GlyphAt(const size_t column);
@@ -78,9 +75,9 @@ public:
iterator end() noexcept;
const_iterator cend() const noexcept;
UnicodeStorage& GetUnicodeStorage();
const UnicodeStorage& GetUnicodeStorage() const;
COORD GetStorageKey(const size_t column) const;
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
COORD GetStorageKey(const size_t column) const noexcept;
void UpdateParent(ROW* const pParent) noexcept;

View File

@@ -8,13 +8,13 @@
// default glyph value, used for reseting the character data portion of a cell
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
CharRowCell::CharRowCell() :
CharRowCell::CharRowCell() noexcept :
_wch{ DefaultValue },
_attr{}
{
}
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) :
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept :
_wch{ wch },
_attr{ attr }
{
@@ -22,7 +22,7 @@ CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) :
// Routine Description:
// - "erases" the glyph. really sets it back to the default "empty" value
void CharRowCell::EraseChars()
void CharRowCell::EraseChars() noexcept
{
if (_attr.IsGlyphStored())
{

View File

@@ -27,10 +27,10 @@ Author(s):
class CharRowCell final
{
public:
CharRowCell();
CharRowCell(const wchar_t wch, const DbcsAttribute attr);
CharRowCell() noexcept;
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept;
void EraseChars();
void EraseChars() noexcept;
void Reset() noexcept;
bool IsSpace() const noexcept;

View File

@@ -91,6 +91,9 @@ CharRowCellReference::const_iterator CharRowCellReference::begin() const
// - 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())
@@ -103,6 +106,7 @@ CharRowCellReference::const_iterator CharRowCellReference::end() const
return &_cellData().Char() + 1;
}
}
#pragma warning(pop)
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
{

View File

@@ -25,7 +25,7 @@ class CharRowCellReference final
public:
using const_iterator = const wchar_t*;
CharRowCellReference(CharRow& parent, const size_t index) :
CharRowCellReference(CharRow& parent, const size_t index) noexcept :
_parent{ parent },
_index{ index }
{

View File

@@ -63,7 +63,7 @@ public:
return _glyphStored;
}
void SetGlyphStored(const bool stored)
void SetGlyphStored(const bool stored) noexcept
{
_glyphStored = stored;
}

View File

@@ -11,7 +11,7 @@
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
OutputCell::OutputCell() :
OutputCell::OutputCell() noexcept :
_text{},
_dbcsAttribute{},
_textAttribute{ InvalidTextAttribute },
@@ -111,7 +111,5 @@ void OutputCell::_setFromOutputCellView(const OutputCellView& cell)
_dbcsAttribute = cell.DbcsAttr();
_textAttribute = cell.TextAttr();
_behavior = cell.TextAttrBehavior();
const auto& view = cell.Chars();
_text = view;
_text = cell.Chars();
}

View File

@@ -25,7 +25,7 @@ Author:
class InvalidCharInfoConversionException : public std::exception
{
const char* what() const noexcept
const char* what() const noexcept override
{
return "Cannot convert to CHAR_INFO without explicit TextAttribute";
}
@@ -34,7 +34,7 @@ class InvalidCharInfoConversionException : public std::exception
class OutputCell final
{
public:
OutputCell();
OutputCell() noexcept;
OutputCell(const std::wstring_view charData,
const DbcsAttribute dbcsAttribute,

View File

@@ -17,7 +17,7 @@ static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLO
// Arguments:
// - wch - The character to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) :
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch)),
_run(),
@@ -33,7 +33,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimi
// Arguments:
// - attr - The color attribute to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) :
OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(attr)),
_run(),
@@ -50,7 +50,7 @@ OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t f
// - wch - The character to use for filling
// - attr - The color attribute to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) :
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch, attr)),
_run(),
@@ -66,7 +66,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute&
// Arguments:
// - charInfo - The legacy character and color data to use for fililng (uses Unicode portion of text data)
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) :
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(charInfo)),
_run(),
@@ -116,7 +116,12 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const
// razzle cannot distinguish between a std::wstring_view and a std::basic_string_view<WORD>
// NOTE: This one internally casts to wchar_t because Razzle sees WORD and wchar_t as the same type
// despite that Visual Studio build can tell the difference.
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs, const bool /*unused*/) :
#pragma warning(push)
#pragma warning(suppress : 26490)
// Suppresses reinterpret_cast. We're only doing this because Windows doesn't understand the type difference between wchar_t and DWORD.
// It is not worth trying to separate that out further or risking performance over this particular warning here.
// TODO GH 2673 - Investigate real wchar_t flag in Windows and resolve this audit issue
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs, const bool /*unused*/) noexcept :
_mode(Mode::LegacyAttr),
_currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))),
_run(std::wstring_view(reinterpret_cast<const wchar_t*>(legacyAttrs.data()), legacyAttrs.size())),
@@ -126,12 +131,13 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacy
_fillLimit(0)
{
}
#pragma warning(pop)
// Routine Description:
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
// Arguments:
// - charInfos - Multiple cell with unicode text and legacy color data.
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) :
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept :
_mode(Mode::CharInfo),
_currentView(s_GenerateView(charInfos.at(0))),
_run(charInfos),
@@ -315,7 +321,7 @@ OutputCellIterator OutputCellIterator::operator++(int)
// - Reference the view to fully-formed output cell data representing the underlying data source.
// Return Value:
// - Reference to the view
const OutputCellView& OutputCellIterator::operator*() const
const OutputCellView& OutputCellIterator::operator*() const noexcept
{
return _currentView;
}
@@ -324,7 +330,7 @@ const OutputCellView& OutputCellIterator::operator*() const
// - Get pointer to the view to fully-formed output cell data representing the underlying data source.
// Return Value:
// - Pointer to the view
const OutputCellView* OutputCellIterator::operator->() const
const OutputCellView* OutputCellIterator::operator->() const noexcept
{
return &_currentView;
}
@@ -338,7 +344,7 @@ const OutputCellView* OutputCellIterator::operator->() const
// - True if we just turned a lead half into a trailing half (and caller doesn't
// need to further update the view).
// - False if this wasn't applicable and the caller should update the view.
bool OutputCellIterator::_TryMoveTrailing()
bool OutputCellIterator::_TryMoveTrailing() noexcept
{
if (_currentView.DbcsAttr().IsLeading())
{
@@ -421,7 +427,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch)
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
@@ -443,7 +449,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch)
// - attr - View representing a single color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr)
OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noexcept
{
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
}
@@ -458,7 +464,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr)
// - attr - View representing a single color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr)
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
@@ -480,12 +486,12 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const Text
// - legacyAttr - View representing a single legacy color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr)
OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept
{
WORD cleanAttr = legacyAttr;
WI_ClearAllFlags(cleanAttr, COMMON_LVB_SBCSDBCS); // don't use legacy lead/trailing byte flags for colors
TextAttribute attr(cleanAttr);
const TextAttribute attr(cleanAttr);
return s_GenerateView(attr);
}
@@ -498,7 +504,7 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt
// - charInfo - character and attribute pair representing a single cell
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo)
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept
{
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);

View File

@@ -33,14 +33,14 @@ public:
using pointer = OutputCellView*;
using reference = OutputCellView&;
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0);
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0);
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0);
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0);
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const std::wstring_view utf16Text);
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute);
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes, const bool unused);
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos);
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes, const bool unused) noexcept;
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept;
OutputCellIterator(const std::basic_string_view<OutputCell> cells);
~OutputCellIterator() = default;
@@ -55,8 +55,8 @@ public:
OutputCellIterator& operator++();
OutputCellIterator operator++(int);
const OutputCellView& operator*() const;
const OutputCellView* operator->() const;
const OutputCellView& operator*() const noexcept;
const OutputCellView* operator->() const noexcept;
private:
enum class Mode
@@ -97,7 +97,7 @@ private:
TextAttribute _attr;
bool _TryMoveTrailing();
bool _TryMoveTrailing() noexcept;
static OutputCellView s_GenerateView(const std::wstring_view view);
@@ -108,11 +108,11 @@ private:
const TextAttribute attr,
const TextAttributeBehavior behavior);
static OutputCellView s_GenerateView(const wchar_t& wch);
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr);
static OutputCellView s_GenerateView(const TextAttribute& attr);
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr);
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo);
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept;
static OutputCellView s_GenerateView(const OutputCell& cell);

View File

@@ -7,7 +7,7 @@
// Routine Description:
// - Constucts an empty in-memory region for holding output buffer cell data.
OutputCellRect::OutputCellRect() :
OutputCellRect::OutputCellRect() noexcept :
_rows(0),
_cols(0)
{
@@ -64,7 +64,7 @@ OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
// - Pointer to the location in the rectangle that represents the start of the requested row.
OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
{
return (_storage.data() + (row * _cols));
return &_storage.at(row * _cols);
}
// Routine Description:
@@ -76,7 +76,7 @@ OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
// - Pointer to the location in the rectangle that represents the start of the requested row.
const OutputCell* OutputCellRect::_FindRowOffset(const size_t row) const
{
return (_storage.data() + (row * _cols));
return &_storage.at(row * _cols);
}
// Routine Description:

View File

@@ -29,7 +29,7 @@ Revision History:
class OutputCellRect final
{
public:
OutputCellRect();
OutputCellRect() noexcept;
OutputCellRect(const size_t rows, const size_t cols);
gsl::span<OutputCell> GetRow(const size_t row);

View File

@@ -15,7 +15,7 @@
OutputCellView::OutputCellView(const std::wstring_view view,
const DbcsAttribute dbcsAttr,
const TextAttribute textAttr,
const TextAttributeBehavior behavior) :
const TextAttributeBehavior behavior) noexcept :
_view(view),
_dbcsAttr(dbcsAttr),
_textAttr(textAttr),
@@ -27,7 +27,9 @@ OutputCellView::OutputCellView(const std::wstring_view view,
// - Returns reference to view over text data
// Return Value:
// - Reference to UTF-16 character data
const std::wstring_view& OutputCellView::Chars() const noexcept
// C26445 - suppressed to enable the `TextBufferTextIterator::operator->` method which needs a non-temporary memory location holding the wstring_view.
// TODO: GH 2681 - remove this suppression by reconciling the probably bad design of the iterators that leads to this being required.
[[gsl::suppress(26445)]] const std::wstring_view& OutputCellView::Chars() const noexcept
{
return _view;
}

View File

@@ -28,7 +28,7 @@ public:
OutputCellView(const std::wstring_view view,
const DbcsAttribute dbcsAttr,
const TextAttribute textAttr,
const TextAttributeBehavior behavior);
const TextAttributeBehavior behavior) noexcept;
const std::wstring_view& Chars() const noexcept;
size_t Columns() const noexcept;

View File

@@ -30,14 +30,14 @@ size_t ROW::size() const noexcept
return _rowWidth;
}
const CharRow& ROW::GetCharRow() const
const CharRow& ROW::GetCharRow() const noexcept
{
return _charRow;
}
CharRow& ROW::GetCharRow()
CharRow& ROW::GetCharRow() noexcept
{
return const_cast<CharRow&>(static_cast<const ROW* const>(this)->GetCharRow());
return _charRow;
}
const ATTR_ROW& ROW::GetAttrRow() const noexcept
@@ -47,7 +47,7 @@ const ATTR_ROW& ROW::GetAttrRow() const noexcept
ATTR_ROW& ROW::GetAttrRow() noexcept
{
return const_cast<ATTR_ROW&>(static_cast<const ROW* const>(this)->GetAttrRow());
return _attrRow;
}
SHORT ROW::GetId() const noexcept
@@ -132,12 +132,12 @@ RowCellIterator ROW::AsCellIter(const size_t startIndex, const size_t count) con
return RowCellIterator(*this, startIndex, count);
}
UnicodeStorage& ROW::GetUnicodeStorage()
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& ROW::GetUnicodeStorage() const
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
}

View File

@@ -36,8 +36,8 @@ public:
size_t size() const noexcept;
const CharRow& GetCharRow() const;
CharRow& GetCharRow();
const CharRow& GetCharRow() const noexcept;
CharRow& GetCharRow() noexcept;
const ATTR_ROW& GetAttrRow() const noexcept;
ATTR_ROW& GetAttrRow() noexcept;
@@ -54,8 +54,8 @@ public:
RowCellIterator AsCellIter(const size_t startIndex) const;
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const;
UnicodeStorage& GetUnicodeStorage();
const UnicodeStorage& GetUnicodeStorage() const;
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const bool setWrap, std::optional<size_t> limitRight = std::nullopt);

View File

@@ -37,38 +37,38 @@ bool RowCellIterator::operator!=(const RowCellIterator& it) const noexcept
return !(*this == it);
}
RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement)
RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement) noexcept
{
_pos += movement;
return (*this);
}
RowCellIterator& RowCellIterator::operator++()
RowCellIterator& RowCellIterator::operator++() noexcept
{
return this->operator+=(1);
}
RowCellIterator RowCellIterator::operator++(int)
RowCellIterator RowCellIterator::operator++(int) noexcept
{
auto temp(*this);
operator++();
return temp;
}
RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement)
RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement) noexcept
{
auto temp(*this);
temp += movement;
return temp;
}
const OutputCellView& RowCellIterator::operator*() const
const OutputCellView& RowCellIterator::operator*() const noexcept
{
return _view;
}
const OutputCellView* RowCellIterator::operator->() const
const OutputCellView* RowCellIterator::operator->() const noexcept
{
return &_view;
}

View File

@@ -38,13 +38,13 @@ public:
bool operator==(const RowCellIterator& it) const noexcept;
bool operator!=(const RowCellIterator& it) const noexcept;
RowCellIterator& operator+=(const ptrdiff_t& movement);
RowCellIterator& operator++();
RowCellIterator operator++(int);
RowCellIterator operator+(const ptrdiff_t& movement);
RowCellIterator& operator+=(const ptrdiff_t& movement) noexcept;
RowCellIterator& operator++() noexcept;
RowCellIterator operator++(int) noexcept;
RowCellIterator operator+(const ptrdiff_t& movement) noexcept;
const OutputCellView& operator*() const;
const OutputCellView* operator->() const;
const OutputCellView& operator*() const noexcept;
const OutputCellView* operator->() const noexcept;
private:
const ROW& _row;

View File

@@ -16,7 +16,7 @@ bool TextAttribute::IsLegacy() const noexcept
// - color that should be displayed as the foreground color
COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultFgColor,
COLORREF defaultBgColor) const
COLORREF defaultBgColor) const noexcept
{
return _IsReverseVideo() ? _GetRgbBackground(colorTable, defaultBgColor) : _GetRgbForeground(colorTable, defaultFgColor);
}
@@ -29,7 +29,7 @@ COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view<COLORREF>
// - color that should be displayed as the background color
COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultFgColor,
COLORREF defaultBgColor) const
COLORREF defaultBgColor) const noexcept
{
return _IsReverseVideo() ? _GetRgbForeground(colorTable, defaultFgColor) : _GetRgbBackground(colorTable, defaultBgColor);
}
@@ -42,7 +42,7 @@ COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view<COLORREF>
// Return Value:
// - color that is stored as the foreground color
COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultColor) const
COLORREF defaultColor) const noexcept
{
return _foreground.GetColor(colorTable, defaultColor, _isBold);
}
@@ -55,7 +55,7 @@ COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view<COLORREF> color
// Return Value:
// - color that is stored as the background color
COLORREF TextAttribute::_GetRgbBackground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultColor) const
COLORREF defaultColor) const noexcept
{
return _background.GetColor(colorTable, defaultColor, false);
}
@@ -75,22 +75,22 @@ WORD TextAttribute::GetMetaAttributes() const noexcept
return wMeta;
}
void TextAttribute::SetForeground(const COLORREF rgbForeground)
void TextAttribute::SetForeground(const COLORREF rgbForeground) noexcept
{
_foreground = TextColor(rgbForeground);
}
void TextAttribute::SetBackground(const COLORREF rgbBackground)
void TextAttribute::SetBackground(const COLORREF rgbBackground) noexcept
{
_background = TextColor(rgbBackground);
}
void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept
{
_wAttrLegacy = static_cast<WORD>(wLegacy & META_ATTRS);
_wAttrLegacy = gsl::narrow_cast<WORD>(wLegacy & META_ATTRS);
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
BYTE fgIndex = static_cast<BYTE>(wLegacy & FG_ATTRS);
BYTE bgIndex = static_cast<BYTE>(wLegacy & BG_ATTRS) >> 4;
const BYTE fgIndex = gsl::narrow_cast<BYTE>(wLegacy & FG_ATTRS);
const BYTE bgIndex = gsl::narrow_cast<BYTE>(wLegacy & BG_ATTRS) >> 4;
_foreground = TextColor(fgIndex);
_background = TextColor(bgIndex);
}
@@ -98,16 +98,16 @@ void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept
void TextAttribute::SetLegacyAttributes(const WORD attrs,
const bool setForeground,
const bool setBackground,
const bool setMeta)
const bool setMeta) noexcept
{
if (setForeground)
{
BYTE fgIndex = (BYTE)(attrs & FG_ATTRS);
const BYTE fgIndex = gsl::narrow_cast<BYTE>(attrs & FG_ATTRS);
_foreground = TextColor(fgIndex);
}
if (setBackground)
{
BYTE bgIndex = (BYTE)(attrs & BG_ATTRS) >> 4;
const BYTE bgIndex = gsl::narrow_cast<BYTE>(attrs & BG_ATTRS) >> 4;
_background = TextColor(bgIndex);
}
if (setMeta)
@@ -133,17 +133,17 @@ void TextAttribute::SetIndexedAttributes(const std::optional<const BYTE> foregro
{
if (foreground)
{
BYTE fgIndex = (*foreground) & 0xFF;
const BYTE fgIndex = (*foreground) & 0xFF;
_foreground = TextColor(fgIndex);
}
if (background)
{
BYTE bgIndex = (*background) & 0xFF;
const BYTE bgIndex = (*background) & 0xFF;
_background = TextColor(bgIndex);
}
}
void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground)
void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept
{
if (fIsForeground)
{

View File

@@ -39,9 +39,9 @@ public:
}
constexpr TextAttribute(const WORD wLegacyAttr) noexcept :
_wAttrLegacy{ static_cast<WORD>(wLegacyAttr & META_ATTRS) },
_foreground{ static_cast<BYTE>(wLegacyAttr & FG_ATTRS) },
_background{ static_cast<BYTE>((wLegacyAttr & BG_ATTRS) >> 4) },
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
_foreground{ gsl::narrow_cast<BYTE>(wLegacyAttr & FG_ATTRS) },
_background{ gsl::narrow_cast<BYTE>((wLegacyAttr & BG_ATTRS) >> 4) },
_isBold{ false }
{
// If we're given lead/trailing byte information with the legacy color, strip it.
@@ -59,9 +59,9 @@ public:
constexpr WORD GetLegacyAttributes() const noexcept
{
BYTE fg = (_foreground.GetIndex() & FG_ATTRS);
BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS;
WORD meta = (_wAttrLegacy & META_ATTRS);
const BYTE fg = (_foreground.GetIndex() & FG_ATTRS);
const BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS;
const WORD meta = (_wAttrLegacy & META_ATTRS);
return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0);
}
@@ -80,20 +80,20 @@ public:
constexpr WORD GetLegacyAttributes(const BYTE defaultFgIndex,
const BYTE defaultBgIndex) const noexcept
{
BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex;
BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex;
BYTE fg = (fgIndex & FG_ATTRS);
BYTE bg = (bgIndex << 4) & BG_ATTRS;
WORD meta = (_wAttrLegacy & META_ATTRS);
const BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex;
const BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex;
const BYTE fg = (fgIndex & FG_ATTRS);
const BYTE bg = (bgIndex << 4) & BG_ATTRS;
const WORD meta = (_wAttrLegacy & META_ATTRS);
return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0);
}
COLORREF CalculateRgbForeground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultFgColor,
COLORREF defaultBgColor) const;
COLORREF defaultBgColor) const noexcept;
COLORREF CalculateRgbBackground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultFgColor,
COLORREF defaultBgColor) const;
COLORREF defaultBgColor) const noexcept;
bool IsLeadingByte() const noexcept;
bool IsTrailingByte() const noexcept;
@@ -110,7 +110,7 @@ public:
void SetLegacyAttributes(const WORD attrs,
const bool setForeground,
const bool setBackground,
const bool setMeta);
const bool setMeta) noexcept;
void SetIndexedAttributes(const std::optional<const BYTE> foreground,
const std::optional<const BYTE> background) noexcept;
@@ -133,9 +133,9 @@ public:
bool IsLegacy() const noexcept;
bool IsBold() const noexcept;
void SetForeground(const COLORREF rgbForeground);
void SetBackground(const COLORREF rgbBackground);
void SetColor(const COLORREF rgbColor, const bool fIsForeground);
void SetForeground(const COLORREF rgbForeground) noexcept;
void SetBackground(const COLORREF rgbBackground) noexcept;
void SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept;
void SetDefaultForeground() noexcept;
void SetDefaultBackground() noexcept;
@@ -150,9 +150,9 @@ public:
private:
COLORREF _GetRgbForeground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultColor) const;
COLORREF defaultColor) const noexcept;
COLORREF _GetRgbBackground(std::basic_string_view<COLORREF> colorTable,
COLORREF defaultColor) const;
COLORREF defaultColor) const noexcept;
bool _IsReverseVideo() const noexcept;
void _SetBoldness(const bool isBold) noexcept;

View File

@@ -11,7 +11,7 @@
// - rgbColor: the COLORREF containing the color information for this TextColor
// Return Value:
// - <none>
void TextColor::SetColor(const COLORREF rgbColor)
void TextColor::SetColor(const COLORREF rgbColor) noexcept
{
_meta = ColorType::IsRgb;
_red = GetRValue(rgbColor);
@@ -25,7 +25,7 @@ void TextColor::SetColor(const COLORREF rgbColor)
// - index: the index of the colortable we should use for this TextColor.
// Return Value:
// - <none>
void TextColor::SetIndex(const BYTE index)
void TextColor::SetIndex(const BYTE index) noexcept
{
_meta = ColorType::IsIndex;
_index = index;
@@ -38,7 +38,7 @@ void TextColor::SetIndex(const BYTE index)
// - <none>
// Return Value:
// - <none>
void TextColor::SetDefault()
void TextColor::SetDefault() noexcept
{
_meta = ColorType::IsDefault;
}
@@ -63,7 +63,7 @@ void TextColor::SetDefault()
// - a COLORREF containing the real value of this TextColor.
COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
const COLORREF defaultColor,
bool brighten) const
bool brighten) const noexcept
{
if (IsDefault())
{
@@ -81,9 +81,9 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
// If we find a match, return instead the bright version of this color
for (size_t i = 0; i < 8; i++)
{
if (colorTable[i] == defaultColor)
if (colorTable.at(i) == defaultColor)
{
return colorTable[i + 8];
return colorTable.at(i + 8);
}
}
}
@@ -102,12 +102,12 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
if (brighten && _index < 8)
{
FAIL_FAST_IF(colorTable.size() < 16);
FAIL_FAST_IF((size_t)(_index + 8) > (size_t)(colorTable.size()));
return colorTable[_index + 8];
FAIL_FAST_IF(gsl::narrow_cast<size_t>(_index) + 8 > colorTable.size());
return colorTable.at(gsl::narrow_cast<size_t>(_index) + 8);
}
else
{
return colorTable[_index];
return colorTable.at(_index);
}
}
}
@@ -119,7 +119,7 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
// - <none>
// Return Value:
// - a COLORREF containing our stored value
COLORREF TextColor::_GetRGB() const
COLORREF TextColor::_GetRGB() const noexcept
{
return RGB(_red, _green, _blue);
}

View File

@@ -91,13 +91,13 @@ public:
return _meta == ColorType::IsRgb;
}
void SetColor(const COLORREF rgbColor);
void SetIndex(const BYTE index);
void SetDefault();
void SetColor(const COLORREF rgbColor) noexcept;
void SetIndex(const BYTE index) noexcept;
void SetDefault() noexcept;
COLORREF GetColor(std::basic_string_view<COLORREF> colorTable,
const COLORREF defaultColor,
const bool brighten) const;
const bool brighten) const noexcept;
constexpr BYTE GetIndex() const noexcept
{
@@ -113,7 +113,7 @@ private:
BYTE _green;
BYTE _blue;
COLORREF _GetRGB() const;
COLORREF _GetRGB() const noexcept;
#ifdef UNIT_TESTING
friend class TextBufferTests;

View File

@@ -4,7 +4,7 @@
#include "precomp.h"
#include "UnicodeStorage.hpp"
UnicodeStorage::UnicodeStorage() :
UnicodeStorage::UnicodeStorage() noexcept :
_map{}
{
}
@@ -35,7 +35,7 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
// - erases key and its associated data from the storage
// Arguments:
// - key - the key to remove
void UnicodeStorage::Erase(const key_type key) noexcept
void UnicodeStorage::Erase(const key_type key)
{
_map.erase(key);
}

View File

@@ -47,13 +47,13 @@ public:
using key_type = typename COORD;
using mapped_type = typename std::vector<wchar_t>;
UnicodeStorage();
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 Erase(const key_type key);
void Remap(const std::map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width);

View File

@@ -11,7 +11,7 @@
// - Constructor to set default properties for Cursor
// Arguments:
// - ulSize - The height of the cursor within this buffer
Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) :
Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
_parentBuffer{ parentBuffer },
_cPosition{ 0 },
_fHasMoved(false),
@@ -87,36 +87,36 @@ ULONG Cursor::GetSize() const noexcept
return _ulSize;
}
void Cursor::SetHasMoved(const bool fHasMoved)
void Cursor::SetHasMoved(const bool fHasMoved) noexcept
{
_fHasMoved = fHasMoved;
}
void Cursor::SetIsVisible(const bool fIsVisible)
void Cursor::SetIsVisible(const bool fIsVisible) noexcept
{
_fIsVisible = fIsVisible;
_RedrawCursor();
}
void Cursor::SetIsOn(const bool fIsOn)
void Cursor::SetIsOn(const bool fIsOn) noexcept
{
_fIsOn = fIsOn;
_RedrawCursorAlways();
}
void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed)
void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) noexcept
{
_fBlinkingAllowed = fBlinkingAllowed;
_RedrawCursorAlways();
}
void Cursor::SetIsDouble(const bool fIsDouble)
void Cursor::SetIsDouble(const bool fIsDouble) noexcept
{
_fIsDouble = fIsDouble;
_RedrawCursor();
}
void Cursor::SetIsConversionArea(const bool fIsConversionArea)
void Cursor::SetIsConversionArea(const bool fIsConversionArea) noexcept
{
// Functionally the same as "Hide cursor"
// Never called with TRUE, it's only used in the creation of a
@@ -125,19 +125,19 @@ void Cursor::SetIsConversionArea(const bool fIsConversionArea)
_RedrawCursorAlways();
}
void Cursor::SetIsPopupShown(const bool fIsPopupShown)
void Cursor::SetIsPopupShown(const bool fIsPopupShown) noexcept
{
// Functionally the same as "Hide cursor"
_fIsPopupShown = fIsPopupShown;
_RedrawCursorAlways();
}
void Cursor::SetDelay(const bool fDelay)
void Cursor::SetDelay(const bool fDelay) noexcept
{
_fDelay = fDelay;
}
void Cursor::SetSize(const ULONG ulSize)
void Cursor::SetSize(const ULONG ulSize) noexcept
{
_ulSize = ulSize;
_RedrawCursor();
@@ -195,7 +195,7 @@ void Cursor::_RedrawCursorAlways() noexcept
CATCH_LOG();
}
void Cursor::SetPosition(const COORD cPosition)
void Cursor::SetPosition(const COORD cPosition) noexcept
{
_RedrawCursor();
_cPosition.X = cPosition.X;
@@ -204,50 +204,50 @@ void Cursor::SetPosition(const COORD cPosition)
ResetDelayEOLWrap();
}
void Cursor::SetXPosition(const int NewX)
void Cursor::SetXPosition(const int NewX) noexcept
{
_RedrawCursor();
_cPosition.X = (SHORT)NewX;
_cPosition.X = gsl::narrow<SHORT>(NewX);
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::SetYPosition(const int NewY)
void Cursor::SetYPosition(const int NewY) noexcept
{
_RedrawCursor();
_cPosition.Y = (SHORT)NewY;
_cPosition.Y = gsl::narrow<SHORT>(NewY);
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::IncrementXPosition(const int DeltaX)
void Cursor::IncrementXPosition(const int DeltaX) noexcept
{
_RedrawCursor();
_cPosition.X += (SHORT)DeltaX;
_cPosition.X += gsl::narrow<SHORT>(DeltaX);
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::IncrementYPosition(const int DeltaY)
void Cursor::IncrementYPosition(const int DeltaY) noexcept
{
_RedrawCursor();
_cPosition.Y += (SHORT)DeltaY;
_cPosition.Y += gsl::narrow<SHORT>(DeltaY);
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::DecrementXPosition(const int DeltaX)
void Cursor::DecrementXPosition(const int DeltaX) noexcept
{
_RedrawCursor();
_cPosition.X -= (SHORT)DeltaX;
_cPosition.X -= gsl::narrow<SHORT>(DeltaX);
_RedrawCursor();
ResetDelayEOLWrap();
}
void Cursor::DecrementYPosition(const int DeltaY)
void Cursor::DecrementYPosition(const int DeltaY) noexcept
{
_RedrawCursor();
_cPosition.Y -= (SHORT)DeltaY;
_cPosition.Y -= gsl::narrow<SHORT>(DeltaY);
_RedrawCursor();
ResetDelayEOLWrap();
}
@@ -262,7 +262,7 @@ void Cursor::DecrementYPosition(const int DeltaY)
// - OtherCursor - The cursor to copy properties from
// Return Value:
// - <none>
void Cursor::CopyProperties(const Cursor& OtherCursor)
void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept
{
// We shouldn't copy the position as it will be already rearranged by the resize operation.
//_cPosition = pOtherCursor->_cPosition;
@@ -288,34 +288,34 @@ void Cursor::CopyProperties(const Cursor& OtherCursor)
_color = OtherCursor._color;
}
void Cursor::DelayEOLWrap(const COORD coordDelayedAt)
void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept
{
_coordDelayedAt = coordDelayedAt;
_fDelayedEolWrap = true;
}
void Cursor::ResetDelayEOLWrap()
void Cursor::ResetDelayEOLWrap() noexcept
{
_coordDelayedAt = { 0 };
_fDelayedEolWrap = false;
}
COORD Cursor::GetDelayedAtPosition() const
COORD Cursor::GetDelayedAtPosition() const noexcept
{
return _coordDelayedAt;
}
bool Cursor::IsDelayedEOLWrap() const
bool Cursor::IsDelayedEOLWrap() const noexcept
{
return _fDelayedEolWrap;
}
void Cursor::StartDeferDrawing()
void Cursor::StartDeferDrawing() noexcept
{
_fDeferCursorRedraw = true;
}
void Cursor::EndDeferDrawing()
void Cursor::EndDeferDrawing() noexcept
{
if (_fHaveDeferredCursorRedraw)
{
@@ -325,27 +325,27 @@ void Cursor::EndDeferDrawing()
_fDeferCursorRedraw = FALSE;
}
const CursorType Cursor::GetType() const
const CursorType Cursor::GetType() const noexcept
{
return _cursorType;
}
const bool Cursor::IsUsingColor() const
const bool Cursor::IsUsingColor() const noexcept
{
return GetColor() != INVALID_COLOR;
}
const COLORREF Cursor::GetColor() const
const COLORREF Cursor::GetColor() const noexcept
{
return _color;
}
void Cursor::SetColor(const unsigned int color)
void Cursor::SetColor(const unsigned int color) noexcept
{
_color = (COLORREF)color;
_color = gsl::narrow_cast<COLORREF>(color);
}
void Cursor::SetType(const CursorType type)
void Cursor::SetType(const CursorType type) noexcept
{
_cursorType = type;
}

View File

@@ -28,7 +28,7 @@ class Cursor final
public:
static const unsigned int s_InvertCursorColor = INVALID_COLOR;
Cursor(const ULONG ulSize, TextBuffer& parentBuffer);
Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept;
~Cursor();
@@ -50,41 +50,41 @@ public:
ULONG GetSize() const noexcept;
COORD GetPosition() const noexcept;
const CursorType GetType() const;
const bool IsUsingColor() const;
const COLORREF GetColor() const;
const CursorType GetType() const noexcept;
const bool IsUsingColor() const noexcept;
const COLORREF GetColor() const noexcept;
void StartDeferDrawing();
void EndDeferDrawing();
void StartDeferDrawing() noexcept;
void EndDeferDrawing() noexcept;
void SetHasMoved(const bool fHasMoved);
void SetIsVisible(const bool fIsVisible);
void SetIsOn(const bool fIsOn);
void SetBlinkingAllowed(const bool fIsOn);
void SetIsDouble(const bool fIsDouble);
void SetIsConversionArea(const bool fIsConversionArea);
void SetIsPopupShown(const bool fIsPopupShown);
void SetDelay(const bool fDelay);
void SetSize(const ULONG ulSize);
void SetHasMoved(const bool fHasMoved) noexcept;
void SetIsVisible(const bool fIsVisible) noexcept;
void SetIsOn(const bool fIsOn) noexcept;
void SetBlinkingAllowed(const bool fIsOn) noexcept;
void SetIsDouble(const bool fIsDouble) noexcept;
void SetIsConversionArea(const bool fIsConversionArea) noexcept;
void SetIsPopupShown(const bool fIsPopupShown) noexcept;
void SetDelay(const bool fDelay) noexcept;
void SetSize(const ULONG ulSize) noexcept;
void SetStyle(const ULONG ulSize, const COLORREF color, const CursorType type) noexcept;
void SetPosition(const COORD cPosition);
void SetXPosition(const int NewX);
void SetYPosition(const int NewY);
void IncrementXPosition(const int DeltaX);
void IncrementYPosition(const int DeltaY);
void DecrementXPosition(const int DeltaX);
void DecrementYPosition(const int DeltaY);
void SetPosition(const COORD cPosition) noexcept;
void SetXPosition(const int NewX) noexcept;
void SetYPosition(const int NewY) noexcept;
void IncrementXPosition(const int DeltaX) noexcept;
void IncrementYPosition(const int DeltaY) noexcept;
void DecrementXPosition(const int DeltaX) noexcept;
void DecrementYPosition(const int DeltaY) noexcept;
void CopyProperties(const Cursor& OtherCursor);
void CopyProperties(const Cursor& OtherCursor) noexcept;
void DelayEOLWrap(const COORD coordDelayedAt);
void ResetDelayEOLWrap();
COORD GetDelayedAtPosition() const;
bool IsDelayedEOLWrap() const;
void DelayEOLWrap(const COORD coordDelayedAt) noexcept;
void ResetDelayEOLWrap() noexcept;
COORD GetDelayedAtPosition() const noexcept;
bool IsDelayedEOLWrap() const noexcept;
void SetColor(const unsigned int color);
void SetType(const CursorType type);
void SetColor(const unsigned int color) noexcept;
void SetType(const CursorType type) noexcept;
private:
TextBuffer& _parentBuffer;

View File

@@ -49,7 +49,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
// - OtherBuffer - The text buffer to copy properties from
// Return Value:
// - <none>
void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer)
void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) noexcept
{
GetCursor().CopyProperties(OtherBuffer.GetCursor());
}
@@ -60,9 +60,9 @@ void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer)
// - <none>
// Return Value:
// - Total number of rows in the buffer
UINT TextBuffer::TotalRowCount() const
UINT TextBuffer::TotalRowCount() const noexcept
{
return static_cast<UINT>(_storage.size());
return gsl::narrow<UINT>(_storage.size());
}
// Routine Description:
@@ -78,7 +78,7 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const size_t offsetIndex = (_firstRow + index) % totalRows;
return _storage[offsetIndex];
return _storage.at(offsetIndex);
}
// Routine Description:
@@ -90,7 +90,11 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
// - reference to the requested row. Asserts if out of bounds.
ROW& TextBuffer::GetRowByOffset(const size_t index)
{
return const_cast<ROW&>(static_cast<const TextBuffer*>(this)->GetRowByOffset(index));
const size_t totalRows = TotalRowCount();
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const size_t offsetIndex = (_firstRow + index) % totalRows;
return _storage.at(offsetIndex);
}
// Routine Description:
@@ -542,7 +546,7 @@ bool TextBuffer::IncrementCircularBuffer()
_renderTarget.TriggerCircling();
// First, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed.
bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes);
const bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes);
if (fSuccess)
{
// Now proceed to increment.
@@ -584,9 +588,9 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View
// Search the given viewport by starting at the bottom.
coordEndOfText.Y = viewport.BottomInclusive();
const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y);
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 = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(currRow.GetCharRow().MeasureRight()) - 1;
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
const auto viewportTop = viewport.Top();
@@ -594,10 +598,10 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View
while (fDoBackUp)
{
coordEndOfText.Y--;
pCurrRow = &GetRowByOffset(coordEndOfText.Y);
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 = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(backupRow.GetCharRow().MeasureRight()) - 1;
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
}
@@ -640,7 +644,7 @@ COORD TextBuffer::_GetPreviousFromCursor() const
return coordPosition;
}
const SHORT TextBuffer::GetFirstRowIndex() const
const SHORT TextBuffer::GetFirstRowIndex() const noexcept
{
return _firstRow;
}
@@ -649,7 +653,7 @@ const Viewport TextBuffer::GetSize() const
return Viewport::FromDimensions({ 0, 0 }, { gsl::narrow<SHORT>(_storage.at(0).size()), gsl::narrow<SHORT>(_storage.size()) });
}
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex)
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) noexcept
{
_firstRow = FirstRowIndex;
}
@@ -755,12 +759,12 @@ void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT
_RefreshRowIDs(std::nullopt);
}
Cursor& TextBuffer::GetCursor()
Cursor& TextBuffer::GetCursor() noexcept
{
return _cursor;
}
const Cursor& TextBuffer::GetCursor() const
const Cursor& TextBuffer::GetCursor() const noexcept
{
return _cursor;
}
@@ -795,7 +799,7 @@ void TextBuffer::Reset()
// - newSize - new size of screen.
// Return Value:
// - Success if successful. Invalid parameter if screen buffer size is unexpected. No memory if allocation failed.
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize) noexcept
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize)
{
RETURN_HR_IF(E_INVALIDARG, newSize.X < 0 || newSize.Y < 0);
@@ -812,7 +816,7 @@ void TextBuffer::Reset()
// rotate rows until the top row is at index 0
try
{
const ROW& newTopRow = _storage[TopRowIndex];
const ROW& newTopRow = _storage.at(TopRowIndex);
while (&newTopRow != &_storage.front())
{
_storage.push_back(std::move(_storage.front()));
@@ -843,12 +847,12 @@ void TextBuffer::Reset()
return S_OK;
}
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept
{
return _unicodeStorage;
}
UnicodeStorage& TextBuffer::GetUnicodeStorage()
UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept
{
return _unicodeStorage;
}
@@ -923,7 +927,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
}
THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow);
return _storage[prevRowIndex];
return _storage.at(prevRowIndex);
}
// Method Description:
@@ -932,7 +936,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
// - <none>
// Return Value:
// - This buffer's current render target.
Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget()
Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcept
{
return _renderTarget;
}
@@ -977,9 +981,9 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
std::vector<COLORREF> selectionBkAttr;
// preallocate to avoid reallocs
selectionText.reserve(highlight.Width() + 2); // + 2 for \r\n if we munged it
selectionFgAttr.reserve(highlight.Width() + 2);
selectionBkAttr.reserve(highlight.Width() + 2);
selectionText.reserve(gsl::narrow<size_t>(highlight.Width()) + 2); // + 2 for \r\n if we munged it
selectionFgAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
selectionBkAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
// copy char data into the string buffer, skipping trailing bytes
while (it)
@@ -998,6 +1002,8 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
selectionBkAttr.push_back(CellBkAttr);
}
}
#pragma warning(suppress : 26444)
// TODO GH 2675: figure out why there's custom construction/destruction happening here
it++;
}
@@ -1059,6 +1065,10 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
{
try
{
// TODO: GH 602 the font name needs to be passed and stored around as an actual bounded type, not an implicit bounds on LF_FACESIZE
const auto faceLength = wcsnlen_s(fontFaceName, LF_FACESIZE);
const std::wstring_view faceNameView{ fontFaceName, faceLength };
std::ostringstream htmlBuilder;
// First we have to add some standard
@@ -1084,12 +1094,9 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
htmlBuilder << ";";
htmlBuilder << "font-family:";
if (fontFaceName[0] != '\0')
{
htmlBuilder << "'";
htmlBuilder << ConvertToA(CP_UTF8, fontFaceName);
htmlBuilder << "',";
}
htmlBuilder << "'";
htmlBuilder << ConvertToA(CP_UTF8, faceNameView);
htmlBuilder << "',";
// even with different font, add monospace as fallback
htmlBuilder << "monospace;";
@@ -1109,7 +1116,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
bool hasWrittenAnyText = false;
std::optional<COLORREF> fgColor = std::nullopt;
std::optional<COLORREF> bkColor = std::nullopt;
for (UINT row = 0; row < rows.text.size(); row++)
for (size_t row = 0; row < rows.text.size(); row++)
{
size_t startOffset = 0;
@@ -1118,25 +1125,25 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
htmlBuilder << "<BR>";
}
for (UINT col = 0; col < rows.text[row].length(); col++)
for (size_t col = 0; col < rows.text.at(row).length(); col++)
{
// do not include \r nor \n as they don't have attributes
// and are not HTML friendly. For line break use '<BR>' instead.
bool isLastCharInRow =
col == rows.text[row].length() - 1 ||
rows.text[row][col + 1] == '\r' ||
rows.text[row][col + 1] == '\n';
const bool isLastCharInRow =
col == rows.text.at(row).length() - 1 ||
rows.text.at(row).at(col + 1) == '\r' ||
rows.text.at(row).at(col + 1) == '\n';
bool colorChanged = false;
if (!fgColor.has_value() || rows.FgAttr[row][col] != fgColor.value())
if (!fgColor.has_value() || rows.FgAttr.at(row).at(col) != fgColor.value())
{
fgColor = rows.FgAttr[row][col];
fgColor = rows.FgAttr.at(row).at(col);
colorChanged = true;
}
if (!bkColor.has_value() || rows.BkAttr[row][col] != bkColor.value())
if (!bkColor.has_value() || rows.BkAttr.at(row).at(col) != bkColor.value())
{
bkColor = rows.BkAttr[row][col];
bkColor = rows.BkAttr.at(row).at(col);
colorChanged = true;
}
@@ -1145,7 +1152,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
{
// note: this should be escaped (for '<', '>', and '&'),
// however MS Word doesn't appear to support HTML entities
htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text[row].data() + startOffset, col - startOffset + includeCurrent));
htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent));
startOffset = col;
}
};

View File

@@ -72,7 +72,7 @@ public:
~TextBuffer() = default;
// Used for duplicating properties to another text buffer
void CopyProperties(const TextBuffer& OtherBuffer);
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
// row manipulation
const ROW& GetRowByOffset(const size_t index) const;
@@ -107,16 +107,16 @@ public:
COORD GetLastNonSpaceCharacter() const;
COORD GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const;
Cursor& GetCursor();
const Cursor& GetCursor() const;
Cursor& GetCursor() noexcept;
const Cursor& GetCursor() const noexcept;
const SHORT GetFirstRowIndex() const;
const SHORT GetFirstRowIndex() const noexcept;
const Microsoft::Console::Types::Viewport GetSize() const;
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
UINT TotalRowCount() const;
UINT TotalRowCount() const noexcept;
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
@@ -124,12 +124,12 @@ public:
void Reset();
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize);
const UnicodeStorage& GetUnicodeStorage() const;
UnicodeStorage& GetUnicodeStorage();
const UnicodeStorage& GetUnicodeStorage() const noexcept;
UnicodeStorage& GetUnicodeStorage() noexcept;
Microsoft::Console::Render::IRenderTarget& GetRenderTarget();
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
class TextAndColor
{
@@ -165,7 +165,7 @@ private:
Microsoft::Console::Render::IRenderTarget& _renderTarget;
void _SetFirstRowIndex(const SHORT FirstRowIndex);
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
COORD _GetPreviousFromCursor() const;

View File

@@ -65,7 +65,7 @@ TextBufferCellIterator::operator bool() const noexcept
// - it - The other iterator to compare to this one.
// Return Value:
// - True if it's the same text buffer and same cell position. False otherwise.
bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const noexcept
bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const
{
return _pos == it._pos &&
&_buffer == &it._buffer &&
@@ -81,7 +81,7 @@ bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const
// - it - The other iterator to compare to this one.
// Return Value:
// - True if it's the same text buffer and different cell position or if they're different buffers. False otherwise.
bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const noexcept
bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const
{
return !(*this == it);
}
@@ -212,7 +212,7 @@ void TextBufferCellIterator::_SetPos(const COORD newPos)
if (newPos.X != _pos.X)
{
const ptrdiff_t diff = newPos.X - _pos.X;
const auto diff = gsl::narrow_cast<ptrdiff_t>(newPos.X) - gsl::narrow_cast<ptrdiff_t>(_pos.X);
_attrIter += diff;
}

View File

@@ -28,12 +28,10 @@ public:
TextBufferCellIterator(const TextBuffer& buffer, COORD pos);
TextBufferCellIterator(const TextBuffer& buffer, COORD pos, const Microsoft::Console::Types::Viewport limits);
~TextBufferCellIterator() = default;
operator bool() const noexcept;
bool operator==(const TextBufferCellIterator& it) const noexcept;
bool operator!=(const TextBufferCellIterator& it) const noexcept;
bool operator==(const TextBufferCellIterator& it) const;
bool operator!=(const TextBufferCellIterator& it) const;
TextBufferCellIterator& operator+=(const ptrdiff_t& movement);
TextBufferCellIterator& operator-=(const ptrdiff_t& movement);

View File

@@ -16,7 +16,7 @@ using namespace Microsoft::Console::Types;
// - Narrows the view of a cell iterator into a text only iterator.
// Arguments:
// - A cell iterator
TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) :
TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) noexcept :
TextBufferCellIterator(cellIt)
{
}
@@ -25,7 +25,8 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel
// - Returns the text information from the text buffer position addressed by this iterator.
// Return Value:
// - Read only UTF-16 text data
const std::wstring_view TextBufferTextIterator::operator*() const
// TODO GH 2682, fix design so this doesn't have to be suppressed.
[[gsl::suppress(26434)]] const std::wstring_view TextBufferTextIterator::operator*() const noexcept
{
return _view.Chars();
}
@@ -34,7 +35,8 @@ const std::wstring_view TextBufferTextIterator::operator*() const
// - Returns the text information from the text buffer position addressed by this iterator.
// Return Value:
// - Read only UTF-16 text data
const std::wstring_view* TextBufferTextIterator::operator->() const
// TODO GH 2682, fix design so this doesn't have to be suppressed.
[[gsl::suppress(26434)]] const std::wstring_view* TextBufferTextIterator::operator->() const noexcept
{
return &_view.Chars();
}

View File

@@ -22,10 +22,10 @@ class SCREEN_INFORMATION;
class TextBufferTextIterator final : public TextBufferCellIterator
{
public:
TextBufferTextIterator(const TextBufferCellIterator& cellIter);
TextBufferTextIterator(const TextBufferCellIterator& cellIter) noexcept;
const std::wstring_view operator*() const;
const std::wstring_view* operator->() const;
const std::wstring_view operator*() const noexcept;
const std::wstring_view* operator->() const noexcept;
protected:
#if UNIT_TESTING

View File

@@ -32,6 +32,9 @@ namespace TerminalAppLocalTests
TEST_METHOD(TryCreateWinRTType);
TEST_METHOD(ValidateProfilesExist);
TEST_METHOD(FindMissingProfile);
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
TEST_METHOD(ValidateDefaultProfileExists);
TEST_METHOD(ValidateDuplicateProfiles);
TEST_METHOD(ValidateManyWarnings);
@@ -127,6 +130,154 @@ namespace TerminalAppLocalTests
}
}
void SettingsTests::FindMissingProfile()
{
// Test that CascadiaSettings::FindProfile returns null for a GUID that
// doesn't exist
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
const Profile* const profile1 = settings->FindProfile(guid1);
const Profile* const profile2 = settings->FindProfile(guid2);
const Profile* const profile3 = settings->FindProfile(guid3);
VERIFY_IS_NOT_NULL(profile1);
VERIFY_IS_NOT_NULL(profile2);
VERIFY_IS_NULL(profile3);
VERIFY_ARE_EQUAL(L"profile0", profile1->GetName());
VERIFY_ARE_EQUAL(L"profile1", profile2->GetName());
}
void SettingsTests::MakeSettingsForProfileThatDoesntExist()
{
// Test that MakeSettings throws when the GUID doesn't exist
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
try
{
auto terminalSettings = settings->MakeSettings(guid1);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
}
try
{
auto terminalSettings = settings->MakeSettings(guid2);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
}
try
{
auto terminalSettings = settings->MakeSettings(guid3);
VERIFY_IS_TRUE(false, L"This call to MakeSettings should fail");
}
catch (...)
{
VERIFY_IS_TRUE(true, L"This call to MakeSettings successfully failed");
}
try
{
auto terminalSettings = settings->MakeSettings(std::nullopt);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
}
}
void SettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
{
// Test that MakeSettings _doesnt_ throw when we load settings with a
// defaultProfile that's not in the list, we validate the settings, and
// then call MakeSettings(nullopt). The validation should ensure that
// the default profile is something reasonable
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(1u, settings->_warnings.size());
VERIFY_ARE_EQUAL(2u, settings->_profiles.size());
VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid());
try
{
auto terminalSettings = settings->MakeSettings(std::nullopt);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
}
}
void SettingsTests::ValidateDefaultProfileExists()
{
const std::string goodProfiles{ R"(

View File

@@ -31,7 +31,8 @@ namespace TerminalAppLocalTests
// deploying the appx takes a bit, so use sparingly (though it will
// deploy once per class when used like this.)
BEGIN_TEST_CLASS(TabTests)
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
TEST_METHOD_PROPERTY(L"RunAs", L"UAP")
TEST_METHOD_PROPERTY(L"UAP:Host", L"PackagedCwa")
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TerminalApp.LocalTests.AppxManifest.xml")
END_TEST_CLASS()
@@ -43,6 +44,8 @@ namespace TerminalAppLocalTests
TEST_METHOD(TryCreateXamlObjects);
TEST_METHOD(TryCreateTab);
TEST_METHOD(TryDuplicateBadTab);
TEST_CLASS_SETUP(ClassSetup)
{
winrt::init_apartment(winrt::apartment_type::single_threaded);
@@ -107,4 +110,16 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(newTab);
}
void TabTests::TryDuplicateBadTab()
{
// Create a tab with a profile with GUID A
// Reload the settings so that GUID A is no longer in the list of profiles
// Try calling _DuplicateTabViewItem on tab A
// No new tab should be created (and more importantly, the app should not crash)
// This is a tests that was inspired by GH#2455, but at the time,
// GH#2472 was still not solved, so this test was not possible to be
// authored.
}
}

View File

@@ -30,6 +30,10 @@
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
<!-- If you encounter errors deploying the test because these aren't
installed, try deploying the actual app package first. Sideloading this test
package doesn't install these dependencies if they're not already installed.
-->
<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>

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,9 @@
#include "Tab.h"
#include "CascadiaSettings.h"
#include "TerminalPage.h"
#include "App.g.h"
#include "App.base.h"
#include "ScopedResourceLoader.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h>
@@ -25,8 +25,7 @@ namespace winrt::TerminalApp::implementation
{
public:
App();
Windows::UI::Xaml::UIElement GetRoot() noexcept;
~App() = default;
void Create();
void LoadSettings();
@@ -34,15 +33,15 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
bool GetShowTabsInTitlebar();
~App() = default;
Windows::UI::Xaml::UIElement GetRoot() noexcept;
hstring GetTitle();
hstring Title();
void TitlebarClicked();
// -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs);
DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
private:
@@ -50,96 +49,37 @@ namespace winrt::TerminalApp::implementation
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the app.
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in _ApplyTheme. The two roots currently are _root and _tabRow
// (which is a root when the tabs are in the titlebar.)
Windows::UI::Xaml::Controls::Control _root{ nullptr };
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
TerminalApp::TabRowControl _tabRow{ nullptr };
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
// updated in _ApplyTheme. The root currently is _root.
winrt::com_ptr<TerminalPage> _root{ nullptr };
std::vector<std::shared_ptr<Tab>> _tabs;
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
std::unique_ptr<::TerminalApp::CascadiaSettings> _settings;
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
HRESULT _settingsLoadedResult;
winrt::hstring _settingsLoadExceptionText{};
bool _loadedInitialSettings;
std::shared_mutex _dialogLock;
ScopedResourceLoader _resourceLoader;
wil::unique_folder_change_reader_nothrow _reader;
std::shared_mutex _dialogLock;
std::atomic<bool> _settingsReloadQueued{ false };
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& titleElement,
const winrt::Windows::Foundation::IInspectable& contentElement,
const winrt::hstring& closeButtonText);
void _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
void _ShowAboutDialog();
fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
void _ShowLoadWarningsDialog();
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
void _LoadSettings();
void _OpenSettings();
void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
void _RegisterSettingsChange();
fire_and_forget _DispatchReloadSettings();
void _ReloadSettings();
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _UpdateTabView();
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
void _UpdateTitle(std::shared_ptr<Tab> tab);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
void _OpenNewTab(std::optional<int> profileIndex);
void _DuplicateTabViewItem();
void _CloseFocusedTab();
void _CloseFocusedPane();
void _SelectNextTab(const bool bMoveRight);
bool _SelectTab(const int tabIndex);
void _SetFocusedTabIndex(int tabIndex);
int _GetFocusedTabIndex() const;
void _Scroll(int delta);
bool _CopyText(const bool trimTrailingWhitespace);
void _PasteText();
void _SplitVertical(const std::optional<GUID>& profileGuid);
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _ScrollPage(int delta);
void _ResizePane(const Direction& direction);
void _MoveFocus(const Direction& direction);
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs);
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnContentSizeChanged(const IInspectable& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _RemoveTabViewItem(const IInspectable& tabViewItem);
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
@@ -150,30 +90,6 @@ namespace winrt::TerminalApp::implementation
void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
#pragma endregion
};
}

View File

@@ -21,16 +21,15 @@ namespace TerminalApp
Windows.UI.Xaml.UIElement GetRoot();
String Title { get; };
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
Boolean GetShowTabsInTitlebar();
event Microsoft.Terminal.TerminalControl.TitleChangedEventArgs TitleChanged;
event LastTabClosedEventArgs LastTabClosed;
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
String GetTitle();
void TitlebarClicked();
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
}
}

View File

@@ -26,112 +26,112 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
void App::_HandleNewTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_OpenNewTab(std::nullopt);
args.Handled(true);
}
void App::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_OpenNewTabDropdown();
args.Handled(true);
}
void App::_HandleDuplicateTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_DuplicateTabViewItem();
args.Handled(true);
}
void App::_HandleCloseTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_CloseFocusedTab();
args.Handled(true);
}
void App::_HandleClosePane(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleClosePane(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_CloseFocusedPane();
args.Handled(true);
}
void App::_HandleScrollUp(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_Scroll(-1);
args.Handled(true);
}
void App::_HandleScrollDown(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleScrollDown(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_Scroll(1);
args.Handled(true);
}
void App::_HandleNextTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_SelectNextTab(true);
args.Handled(true);
}
void App::_HandlePrevTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_SelectNextTab(false);
args.Handled(true);
}
void App::_HandleSplitVertical(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleSplitVertical(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_SplitVertical(std::nullopt);
args.Handled(true);
}
void App::_HandleSplitHorizontal(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleSplitHorizontal(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_SplitHorizontal(std::nullopt);
args.Handled(true);
}
void App::_HandleScrollUpPage(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_ScrollPage(-1);
args.Handled(true);
}
void App::_HandleScrollDownPage(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleScrollDownPage(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_ScrollPage(1);
args.Handled(true);
}
void App::_HandleOpenSettings(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_OpenSettings();
args.Handled(true);
}
void App::_HandlePasteText(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_PasteText();
args.Handled(true);
}
void App::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabWithProfileArgs>())
{
@@ -140,8 +140,8 @@ namespace winrt::TerminalApp::implementation
}
}
void App::_HandleSwitchToTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SwitchToTabArgs>())
{
@@ -150,8 +150,8 @@ namespace winrt::TerminalApp::implementation
}
}
void App::_HandleResizePane(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::ResizePaneArgs>())
{
@@ -160,8 +160,8 @@ namespace winrt::TerminalApp::implementation
}
}
void App::_HandleMoveFocus(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::MoveFocusArgs>())
{
@@ -170,8 +170,8 @@ namespace winrt::TerminalApp::implementation
}
}
void App::_HandleCopyText(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
void TerminalPage::_HandleCopyText(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::CopyTextArgs>())
{

View File

@@ -451,7 +451,7 @@ TerminalSettings CascadiaSettings::MakeSettings(std::optional<GUID> profileGuidA
const Profile* const profile = FindProfile(profileGuid);
if (profile == nullptr)
{
throw E_INVALIDARG;
THROW_HR(E_INVALIDARG);
}
TerminalSettings result = profile->CreateTerminalSettings(_globals.GetColorSchemes());
@@ -756,7 +756,7 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
// Try collecting all the unique guids. If we ever encounter a guid that's
// already in the set, then we need to delete that profile.
for (int i = 0; i < _profiles.size(); i++)
for (size_t i = 0; i < _profiles.size(); i++)
{
if (!uniqueGuids.insert(_profiles.at(i).GetGuid()).second)
{

View File

@@ -164,26 +164,26 @@ bool Pane::ResizePane(const Direction& direction)
{
return _Resize(direction);
}
else
// If neither of our children were the focused leaf, then recurse into
// our children and see if they can handle the resize.
// For each child, if it has a focused descendant, try having that child
// handle the resize.
// If the child wasn't able to handle the resize, it's possible that
// there were no descendants with a separator the correct direction. If
// our separator _is_ the correct direction, then we should be the pane
// to resize. Otherwise, just return false, as we couldn't handle it
// either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
// If neither of our children were the focused leaf, then recurse into
// our children and see if they can handle the resize.
// For each child, if it has a focused descendant, try having that child
// handle the resize.
// If the child wasn't able to handle the resize, it's possible that
// there were no descendants with a separator the correct direction. If
// our separator _is_ the correct direction, then we should be the pane
// to resize. Otherwise, just return false, as we couldn't handle it
// either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
return _firstChild->ResizePane(direction) || _Resize(direction);
}
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->ResizePane(direction) || _Resize(direction);
}
return _firstChild->ResizePane(direction) || _Resize(direction);
}
if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->ResizePane(direction) || _Resize(direction);
}
return false;
}
@@ -253,26 +253,26 @@ bool Pane::NavigateFocus(const Direction& direction)
{
return _NavigateFocus(direction);
}
else
// If neither of our children were the focused leaf, then recurse into
// our children and see if they can handle the focus move.
// For each child, if it has a focused descendant, try having that child
// handle the focus move.
// If the child wasn't able to handle the focus move, it's possible that
// there were no descendants with a separator the correct direction. If
// our separator _is_ the correct direction, then we should be the pane
// to move focus into our other child. Otherwise, just return false, as
// we couldn't handle it either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
// If neither of our children were the focused leaf, then recurse into
// our children and see if they can handle the focus move.
// For each child, if it has a focused descendant, try having that child
// handle the focus move.
// If the child wasn't able to handle the focus move, it's possible that
// there were no descendants with a separator the correct direction. If
// our separator _is_ the correct direction, then we should be the pane
// to move focus into our other child. Otherwise, just return false, as
// we couldn't handle it either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction);
}
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction);
}
return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction);
}
if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction);
}
return false;
}
@@ -348,15 +348,13 @@ std::shared_ptr<Pane> Pane::GetFocusedPane()
{
return _lastFocused ? shared_from_this() : nullptr;
}
else
auto firstFocused = _firstChild->GetFocusedPane();
if (firstFocused != nullptr)
{
auto firstFocused = _firstChild->GetFocusedPane();
if (firstFocused != nullptr)
{
return firstFocused;
}
return _secondChild->GetFocusedPane();
return firstFocused;
}
return _secondChild->GetFocusedPane();
}
// Method Description:
@@ -778,110 +776,58 @@ void Pane::_ApplySplitDefinitions()
}
// Method Description:
// - Determines whether the pane can be split vertically
// - Determines whether the pane can be split
// Arguments:
// - splitType: what type of split we want to create.
// Return Value:
// - True if the pane can be split vertically. False otherwise.
bool Pane::CanSplitVertical()
// - True if the pane can be split. False otherwise.
bool Pane::CanSplit(SplitState splitType)
{
if (!_IsLeaf())
if (_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
return _firstChild->CanSplitVertical();
}
else if (_secondChild->_HasFocusedChild())
{
return _secondChild->CanSplitVertical();
}
return false;
return _CanSplit(splitType);
}
return _CanSplit(SplitState::Vertical);
if (_firstChild->_HasFocusedChild())
{
return _firstChild->CanSplit(splitType);
}
if (_secondChild->_HasFocusedChild())
{
return _secondChild->CanSplit(splitType);
}
return false;
}
// Method Description:
// - Vertically split the focused pane in our tree of panes, and place the given
// - Split the focused pane in our tree of panes, and place the given
// TermControl into the newly created pane. If we're the focused pane, then
// we'll create two new children, and place them side-by-side in our Grid.
// Arguments:
// - splitType: what type of split we want to create.
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::SplitVertical(const GUID& profile, const TermControl& control)
void Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control)
{
// If we're not the leaf, recurse into our children to split them.
if (!_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
_firstChild->SplitVertical(profile, control);
_firstChild->Split(splitType, profile, control);
}
else if (_secondChild->_HasFocusedChild())
{
_secondChild->SplitVertical(profile, control);
_secondChild->Split(splitType, profile, control);
}
return;
}
_Split(SplitState::Vertical, profile, control);
}
// Method Description:
// - Determines whether the pane can be split horizontally
// Arguments:
// - splitType: what type of split we want to create.
// Return Value:
// - True if the pane can be split horizontally. False otherwise.
bool Pane::CanSplitHorizontal()
{
if (!_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
return _firstChild->CanSplitHorizontal();
}
else if (_secondChild->_HasFocusedChild())
{
return _secondChild->CanSplitHorizontal();
}
return false;
}
return _CanSplit(SplitState::Horizontal);
}
// Method Description:
// - Horizontally split the focused pane in our tree of panes, and place the given
// TermControl into the newly created pane. If we're the focused pane, then
// we'll create two new children, and place them side-by-side in our Grid.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
{
if (!_IsLeaf())
{
if (_firstChild->_HasFocusedChild())
{
_firstChild->SplitHorizontal(profile, control);
}
else if (_secondChild->_HasFocusedChild())
{
_secondChild->SplitHorizontal(profile, control);
}
return;
}
_Split(SplitState::Horizontal, profile, control);
_Split(splitType, profile, control);
}
// Method Description:
@@ -892,8 +838,6 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
// - True if the pane can be split. False otherwise.
bool Pane::_CanSplit(SplitState splitType)
{
const bool changeWidth = _splitState == SplitState::Vertical;
const Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
gsl::narrow_cast<float>(_root.ActualHeight()) };
@@ -1006,14 +950,12 @@ Size Pane::_GetMinSize() const
{
return _control.MinimumSize();
}
else
{
const auto firstSize = _firstChild->_GetMinSize();
const auto secondSize = _secondChild->_GetMinSize();
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
return { newWidth, newHeight };
}
const auto firstSize = _firstChild->_GetMinSize();
const auto secondSize = _secondChild->_GetMinSize();
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
return { newWidth, newHeight };
}
DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs);

View File

@@ -49,11 +49,8 @@ public:
bool ResizePane(const winrt::TerminalApp::Direction& direction);
bool NavigateFocus(const winrt::TerminalApp::Direction& direction);
bool CanSplitHorizontal();
void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
bool CanSplitVertical();
void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
bool CanSplit(SplitState splitType);
void Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void Close();

View File

@@ -204,47 +204,28 @@ void Tab::Scroll(const int delta)
}
// Method Description:
// - Determines whether the focused pane has sufficient space to be split vertically.
// - Determines whether the focused pane has sufficient space to be split.
// Arguments:
// - splitType: The type of split we want to create.
// Return Value:
// - True if the focused pane can be split horizontally. False otherwise.
bool Tab::CanAddVerticalSplit()
// - True if the focused pane can be split. False otherwise.
bool Tab::CanSplitPane(Pane::SplitState splitType)
{
return _rootPane->CanSplitVertical();
return _rootPane->CanSplit(splitType);
}
// Method Description:
// - Vertically split the focused pane in our tree of panes, and place the
// - Split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.
// Arguments:
// - splitType: The type of split we want to create.
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Tab::AddVerticalSplit(const GUID& profile, TermControl& control)
void Tab::SplitPane(Pane::SplitState splitType, const GUID& profile, TermControl& control)
{
_rootPane->SplitVertical(profile, control);
}
// Method Description:
// - Determines whether the focused pane has sufficient space to be split horizontally.
// Return Value:
// - True if the focused pane can be split horizontally. False otherwise.
bool Tab::CanAddHorizontalSplit()
{
return _rootPane->CanSplitHorizontal();
}
// Method Description:
// - Horizontally split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Tab::AddHorizontalSplit(const GUID& profile, TermControl& control)
{
_rootPane->SplitHorizontal(profile, control);
_rootPane->Split(splitType, profile, control);
}
// Method Description:

View File

@@ -19,10 +19,9 @@ public:
void SetFocused(const bool focused);
void Scroll(const int delta);
bool CanAddVerticalSplit();
void AddVerticalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
bool CanAddHorizontalSplit();
void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
bool CanSplitPane(Pane::SplitState splitType);
void SplitPane(Pane::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void UpdateFocus();
void UpdateIcon(const winrt::hstring iconPath);

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,144 @@
#include "winrt/Microsoft.UI.Xaml.Controls.h"
#include "TerminalPage.g.h"
#include "Tab.h"
#include "CascadiaSettings.h"
#include "Profile.h"
#include "ScopedResourceLoader.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
namespace winrt::TerminalApp::implementation
{
struct TerminalPage : TerminalPageT<TerminalPage>
{
public:
TerminalPage();
TerminalPage(std::shared_ptr<ScopedResourceLoader> resourceLoader);
void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI);
void Create();
hstring Title();
void ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
void TitlebarClicked();
// -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog);
private:
// If you add controls here, but forget to null them either here or in
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the app.
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in App::_ApplyTheme. The roots currently is _tabRow
// (which is a root when the tabs are in the titlebar.)
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
TerminalApp::TabRowControl _tabRow{ nullptr };
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
std::vector<std::shared_ptr<Tab>> _tabs;
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
void _ShowAboutDialog();
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
void _OpenNewTab(std::optional<int> profileIndex);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
void _UpdateTitle(std::shared_ptr<Tab> tab);
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
void _UpdateTabView();
void _DuplicateTabViewItem();
void _RemoveTabViewItem(const IInspectable& tabViewItem);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
void _SelectNextTab(const bool bMoveRight);
bool _SelectTab(const int tabIndex);
void _MoveFocus(const Direction& direction);
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
int _GetFocusedTabIndex() const;
void _SetFocusedTabIndex(int tabIndex);
void _CloseFocusedTab();
void _CloseFocusedPane();
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _Scroll(int delta);
void _SplitVertical(const std::optional<GUID>& profileGuid);
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
void _ResizePane(const Direction& direction);
void _ScrollPage(int delta);
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData);
void _PasteFromClipboardHandler(const IInspectable& sender,
const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
bool _CopyText(const bool trimTrailingWhitespace);
void _PasteText();
static fire_and_forget PasteFromClipboard(winrt::Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs);
void _OpenSettings();
fire_and_forget LaunchSettings();
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
void _OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs);
void _RefreshUIForSettingsReload();
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
#pragma endregion
};
}

View File

@@ -1,10 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "..\App.idl";
namespace TerminalApp
{
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page
{
TerminalPage();
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.Controls.ContentDialog> ShowDialog;
}
}

View File

@@ -574,37 +574,41 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - S_OK otherwise
HRESULT AzureConnection::_TenantChoiceHelper()
{
const auto tenantListAsArray = _tenantList.as_array();
_maxSize = tenantListAsArray.size();
for (int i = 0; i < _maxSize; i++)
try
{
const auto& tenant = tenantListAsArray.at(i);
const auto [tenantId, tenantDisplayName] = _crackTenant(tenant);
_outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str()));
const auto tenantListAsArray = _tenantList.as_array();
_maxSize = gsl::narrow<int>(tenantListAsArray.size());
for (int i = 0; i < _maxSize; i++)
{
const auto& tenant = tenantListAsArray.at(i);
const auto [tenantId, tenantDisplayName] = _crackTenant(tenant);
_outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str()));
}
_outputHandlers(winrt::to_hstring(enterTenant));
// Use a lock to wait for the user to input a valid number
std::unique_lock<std::mutex> tenantNumberLock{ _commonMutex };
_canProceed.wait(tenantNumberLock, [=]() {
return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _closing.load();
});
// User might have closed the tab while we waited for input
if (_closing.load())
{
return E_FAIL;
}
const auto& chosenTenant = tenantListAsArray.at(_tenantNumber);
std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant);
// We have to refresh now that we have the tenantID
const auto refreshResponse = _RefreshTokens();
_accessToken = refreshResponse.at(L"access_token").as_string();
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
_state = State::StoreTokens;
return S_OK;
}
_outputHandlers(winrt::to_hstring(enterTenant));
// Use a lock to wait for the user to input a valid number
std::unique_lock<std::mutex> tenantNumberLock{ _commonMutex };
_canProceed.wait(tenantNumberLock, [=]() {
return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _closing.load();
});
// User might have closed the tab while we waited for input
if (_closing.load())
{
return E_FAIL;
}
const auto& chosenTenant = tenantListAsArray.at(_tenantNumber);
std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant);
// We have to refresh now that we have the tenantID
const auto refreshResponse = _RefreshTokens();
_accessToken = refreshResponse.at(L"access_token").as_string();
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
_state = State::StoreTokens;
return S_OK;
CATCH_RETURN();
}
// Method description:

View File

@@ -108,6 +108,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
si,
extraEnvVars));
_startTime = std::chrono::high_resolution_clock::now();
// Create our own output handling thread
// This must be done after the pipes are populated.
// Each connection needs to make sure to drain the output from its backing host.
@@ -209,6 +211,21 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return 0;
}
if (!_recievedFirstByte)
{
auto now = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> delta = now - _startTime;
TraceLoggingWrite(g_hTerminalConnectionProvider,
"RecievedFirstByte",
TraceLoggingDescription("An event emitted when the connection recieves the first byte"),
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingFloat64(delta.count(), "Duration"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
_recievedFirstByte = true;
}
// Convert buffer to hstring
auto hstr{ winrt::to_hstring(strView) };

View File

@@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
bool _connected{};
std::atomic<bool> _closing{ false };
bool _recievedFirstByte{ false };
std::chrono::high_resolution_clock::time_point _startTime{};
wil::unique_hfile _inPipe; // The pipe for writing input to
wil::unique_hfile _outPipe; // The pipe for reading output from

View File

@@ -35,6 +35,7 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="init.cpp"/>
<ClCompile Include="AzureConnection.cpp" Condition="'$(Platform)'!='ARM64'" />
<ClCompile Include="AzureConnection-ARM64.cpp" Condition="'$(Platform)'=='ARM64'" />
<ClCompile Include="pch.cpp">

View File

@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.
#include "pch.h"
// Note: Generate GUID using TlgGuid.exe tool
TRACELOGGING_DEFINE_PROVIDER(
g_hTerminalConnectionProvider,
"Microsoft.Windows.Terminal.Connection",
// {e912fe7b-eeb6-52a5-c628-abe388e5f792}
(0xe912fe7b, 0xeeb6, 0x52a5, 0xc6, 0x28, 0xab, 0xe3, 0x88, 0xe5, 0xf7, 0x92),
TraceLoggingOptionMicrosoftTelemetry());
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstDll);
TraceLoggingRegister(g_hTerminalConnectionProvider);
break;
case DLL_PROCESS_DETACH:
if (g_hTerminalConnectionProvider)
{
TraceLoggingUnregister(g_hTerminalConnectionProvider);
}
break;
}
return TRUE;
}

View File

@@ -19,3 +19,7 @@
#include "winrt/Windows.Security.Credentials.h"
#include "winrt/Windows.Foundation.Collections.h"
#include <Windows.h>
#include <TraceLoggingProvider.h>
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider);
#include <telemetry/ProjectTelemetry.h>

View File

@@ -732,7 +732,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(_root);
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
// Ignore mouse events while the terminal does not have focus.
// This prevents the user from selecting and copying text if they
@@ -818,7 +818,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(_root);
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
if (point.Properties().IsLeftButtonPressed())
{
@@ -897,7 +897,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ptr = args.Pointer();
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
@@ -1443,7 +1443,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
// send data up for clipboard
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData));
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), gsl::narrow<winrt::hstring::size_type>(textData.size())), winrt::to_hstring(htmlData));
_clipboardCopyHandlers(*this, *copyArgs);
return true;
}

View File

@@ -136,7 +136,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges);
int count = providers.size();
int count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);

View File

@@ -81,7 +81,7 @@ void AppHost::Initialize()
_app.TitleChanged({ this, &AppHost::AppTitleChanged });
_app.LastTabClosed({ this, &AppHost::LastTabClosed });
AppTitleChanged(_app.GetTitle());
_window->UpdateTitle(_app.Title());
// Set up the content of the application. If the app has a custom titlebar,
// set that content as well.
@@ -93,10 +93,11 @@ void AppHost::Initialize()
// - Called when the app's title changes. Fires off a window message so we can
// update the window's title on the main thread.
// Arguments:
// - sender: unused
// - newTitle: the string to use as the new window title
// Return Value:
// - <none>
void AppHost::AppTitleChanged(winrt::hstring newTitle)
void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle)
{
_window->UpdateTitle(newTitle.c_str());
}
@@ -104,10 +105,11 @@ void AppHost::AppTitleChanged(winrt::hstring newTitle)
// Method Description:
// - Called when no tab is remaining to close the window.
// Arguments:
// - <none>
// - sender: unused
// - LastTabClosedEventArgs: unused
// Return Value:
// - <none>
void AppHost::LastTabClosed()
void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/)
{
_window->Close();
}
@@ -199,6 +201,13 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
// If we can't resize the window, that's really okay. We can just go on with
// the originally proposed window size.
LOG_LAST_ERROR_IF(!succeeded);
TraceLoggingWrite(
g_hWindowsTerminalProvider,
"WindowCreated",
TraceLoggingDescription("Event emitted upon creating the application window"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
// Method Description:
@@ -209,7 +218,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
// - arg: the UIElement to use as the new Titlebar content.
// Return Value:
// - <none>
void AppHost::_UpdateTitleBarContent(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::UIElement& arg)
void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::UIElement& arg)
{
if (_useNonClientArea)
{

View File

@@ -14,8 +14,8 @@ public:
AppHost() noexcept;
virtual ~AppHost();
void AppTitleChanged(winrt::hstring newTitle);
void LastTabClosed();
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
void Initialize();
private:
@@ -25,7 +25,7 @@ private:
winrt::TerminalApp::App _app;
void _HandleCreateWindow(const HWND hwnd, const RECT proposedRect);
void _UpdateTitleBarContent(const winrt::TerminalApp::App& sender,
void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::UIElement& arg);
void _UpdateTheme(const winrt::TerminalApp::App&,
const winrt::Windows::UI::Xaml::ElementTheme& arg);

View File

@@ -36,7 +36,7 @@ public:
void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
#pragma region IUiaWindow
void ChangeViewport(const SMALL_RECT NewWindow)
void ChangeViewport(const SMALL_RECT /*NewWindow*/)
{
// TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider
// Relevant comment from zadjii-msft:
@@ -57,7 +57,7 @@ public:
return BaseWindow::GetHandle();
};
[[nodiscard]] HRESULT SignalUia(_In_ EVENTID id) override { return E_NOTIMPL; };
[[nodiscard]] HRESULT SignalUia(_In_ EVENTID /*id*/) override { return E_NOTIMPL; };
[[nodiscard]] HRESULT UiaSetTextAreaFocus() override { return E_NOTIMPL; };
RECT GetWindowRect() const noexcept override

View File

@@ -107,7 +107,7 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind
#pragma region IRawElementProviderFragment
IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection /*direction*/, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
*ppProvider = nullptr;
@@ -139,7 +139,7 @@ IFACEMETHODIMP WindowUiaProvider::SetFocus()
IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/,
_In_ double /*y*/,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
@@ -151,7 +151,7 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/,
return S_OK;
}
IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/)
{
RETURN_IF_FAILED(_EnsureValidHwnd());
// TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider

View File

@@ -11,6 +11,15 @@ using namespace Windows::UI::Composition;
using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::Foundation::Numerics;
// Note: Generate GUID using TlgGuid.exe tool - seriously, it won't work if you
// just generate an arbitrary GUID
TRACELOGGING_DEFINE_PROVIDER(
g_hWindowsTerminalProvider,
"Microsoft.Windows.Terminal.Win32Host",
// {56c06166-2e2e-5f4d-7ff3-74f4b78c87d6}
(0x56c06166, 0x2e2e, 0x5f4d, 0x7f, 0xf3, 0x74, 0xf4, 0xb7, 0x8c, 0x87, 0xd6),
TraceLoggingOptionMicrosoftTelemetry());
// Routine Description:
// - Retrieves the string resource from the current module with the given ID
// from the resources files. See resource.h and the .rc definitions for valid IDs.
@@ -94,6 +103,14 @@ static void EnsureNativeArchitecture()
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
TraceLoggingRegister(g_hWindowsTerminalProvider);
TraceLoggingWrite(
g_hWindowsTerminalProvider,
"ExecutableStarted",
TraceLoggingDescription("Event emitted immediately on startup"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
// Block the user from starting if they launched the incorrect architecture version of the project.
// This should only be applicable to developer versions. The package installation process
// should choose and install the correct one from the bundle.

View File

@@ -52,3 +52,10 @@ Abstract:
#include <wil/resource.h>
#include <wil/win32_helpers.h>
// Including TraceLogging essentials for the binary
#include <TraceLoggingProvider.h>
#include <winmeta.h>
TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider);
#include <telemetry\ProjectTelemetry.h>
#include <TraceLoggingActivity.h>

View File

@@ -79,7 +79,7 @@ namespace TerminalAppUnitTests
"\"name\" : \"Campbell\","
"\"purple\" : \"#881798\","
"\"red\" : \"#C50F1F\","
"\"white\" : \"#CCCCCC\","
"\"white\" : \"#CCC\","
"\"yellow\" : \"#C19C00\""
"}" };

View File

@@ -9,6 +9,7 @@
<application>
<!-- Windows 10 1903 -->
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
<maxversiontested Id="10.0.18362.0"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>

View File

@@ -94,6 +94,7 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /Zc:twoPhase- /std:c++17 </AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<PreprocessorDefinitions Condition="'$(ConfigurationType)'=='DynamicLibrary'">_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@@ -15,6 +15,7 @@ class PolicyTests
#ifdef __INSIDE_WINDOWS
BEGIN_TEST_METHOD(WrongWayVerbsUAP)
TEST_METHOD_PROPERTY(L"RunAs", L"UAP")
TEST_METHOD_PROPERTY(L"UAP:AppxManifest", L"MUA")
END_TEST_METHOD();
#endif

View File

@@ -36,6 +36,9 @@
<ProjectReference Include="..\..\types\lib\types.vcxproj">
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
</ProjectReference>
<ProjectReference Include="..\..\winconpty\winconpty.vcxproj">
<Project>{58a03bb2-df5a-4b66-91a0-7ef3ba01269a}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Host.Tests.Feature.rc" />
@@ -56,4 +59,10 @@
<Import Project="$(SolutionDir)src\common.build.dll.props" />
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
</Project>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>$(OutDir)\conpty.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@@ -1356,24 +1356,22 @@ void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool
if (screenInfo.IsCursorInMargins(oldCursorPosition))
{
// Cursor is at the top of the viewport
const COORD bufferSize = screenInfo.GetBufferSize().Dimensions();
// Rectangle to cut out of the existing buffer
// Rectangle to cut out of the existing buffer. This is inclusive.
// It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width.
SMALL_RECT srScroll;
srScroll.Left = 0;
srScroll.Right = bufferSize.X;
srScroll.Right = SHORT_MAX;
srScroll.Top = viewport.Top;
srScroll.Bottom = viewport.Bottom - 1;
srScroll.Bottom = viewport.Bottom;
// Paste coordinate for cut text above
COORD coordDestination;
coordDestination.X = 0;
coordDestination.Y = viewport.Top + 1;
SMALL_RECT srClip = viewport;
Status = NTSTATUS_FROM_HRESULT(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo,
srScroll,
coordDestination,
srClip,
srScroll,
UNICODE_SPACE,
screenInfo.GetAttributes().GetLegacyAttributes()));
}
@@ -2033,13 +2031,13 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert)
const auto cursorPosition = textBuffer.GetCursor().GetPosition();
if (screenInfo.IsCursorInMargins(cursorPosition))
{
const auto screenEdges = screenInfo.GetBufferSize().ToInclusive();
// Rectangle to cut out of the existing buffer
// Rectangle to cut out of the existing buffer. This is inclusive.
// It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width.
SMALL_RECT srScroll;
srScroll.Left = 0;
srScroll.Right = screenEdges.Right - screenEdges.Left;
srScroll.Right = SHORT_MAX;
srScroll.Top = cursorPosition.Y;
srScroll.Bottom = screenEdges.Bottom;
srScroll.Bottom = screenInfo.GetViewport().BottomInclusive();
// Paste coordinate for cut text above
COORD coordDestination;
coordDestination.X = 0;
@@ -2052,15 +2050,25 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert)
coordDestination.Y = (cursorPosition.Y) - gsl::narrow<short>(count);
}
SMALL_RECT srClip = screenEdges;
srClip.Top = cursorPosition.Y;
LOG_IF_FAILED(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo,
srScroll,
coordDestination,
srClip,
UNICODE_SPACE,
screenInfo.GetAttributes().GetLegacyAttributes()));
// Here we previously called to ScrollConsoleScreenBufferWImpl to
// perform the scrolling operation. However, that function only accepts
// a WORD for the fill attributes. That means we'd lose 256/RGB fidelity
// for fill attributes. So instead, we'll just call ScrollRegion
// ourselves, with the same params that ScrollConsoleScreenBufferWImpl
// would have.
// See microsoft/terminal#832 for more context.
try
{
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
ScrollRegion(screenInfo,
srScroll,
srScroll,
coordDestination,
UNICODE_SPACE,
screenInfo.GetAttributes());
}
CATCH_LOG();
}
}

View File

@@ -1231,10 +1231,13 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const COORD* const pcoordSize,
// See MSFT:19917443
// If we're in terminal scrolling mode, and we've changed the height of the
// viewport, the new viewport's bottom to the _virtualBottom
// viewport, the new viewport's bottom to the _virtualBottom.
// GH#1206 - Only do this if the viewport is _growing_ in height. This can
// cause unexpected behavior if we try to anchor the _virtualBottom to a
// position that will be greater than the height of the buffer.
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto newViewport = Viewport::FromInclusive(srNewViewport);
if (gci.IsTerminalScrolling() && newViewport.Height() != _viewport.Height())
if (gci.IsTerminalScrolling() && newViewport.Height() >= _viewport.Height())
{
const short newTop = static_cast<short>(std::max(0, _virtualBottom - (newViewport.Height() - 1)));

View File

@@ -161,13 +161,23 @@ class ScreenBufferTests
TEST_METHOD(DontResetColorsAboveVirtualBottom);
TEST_METHOD(ScrollOperations);
TEST_METHOD(InsertChars);
TEST_METHOD(DeleteChars);
TEST_METHOD(ScrollUpInMargins);
TEST_METHOD(ScrollDownInMargins);
TEST_METHOD(InsertLinesInMargins);
TEST_METHOD(DeleteLinesInMargins);
TEST_METHOD(ReverseLineFeedInMargins);
TEST_METHOD(InsertDeleteLines256Colors);
TEST_METHOD(SetOriginMode);
TEST_METHOD(HardResetBuffer);
TEST_METHOD(RestoreDownAltBufferWithTerminalScrolling);
};
void ScreenBufferTests::SingleAlternateBufferCreationTest()
@@ -3051,6 +3061,499 @@ void ScreenBufferTests::DontResetColorsAboveVirtualBottom()
}
}
template<class T>
void _FillLine(COORD position, T fillContent, TextAttribute fillAttr)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& row = si.GetTextBuffer().GetRowByOffset(position.Y);
row.WriteCells({ fillContent, fillAttr }, position.X, false);
}
template<class T>
void _FillLine(int line, T fillContent, TextAttribute fillAttr)
{
_FillLine({ 0, gsl::narrow<SHORT>(line) }, fillContent, fillAttr);
}
template<class T>
void _FillLines(int startLine, int endLine, T fillContent, TextAttribute fillAttr)
{
for (auto line = startLine; line < endLine; ++line)
{
_FillLine(line, fillContent, fillAttr);
}
}
template<class T>
bool _ValidateLineContains(COORD position, T expectedContent, TextAttribute expectedAttr)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto actual = si.GetCellLineDataAt(position);
auto expected = OutputCellIterator{ expectedContent, expectedAttr };
while (actual && expected)
{
if (actual->Chars() != expected->Chars() || actual->TextAttr() != expected->TextAttr())
{
return false;
}
++actual;
++expected;
}
return true;
};
template<class T>
bool _ValidateLineContains(int line, T expectedContent, TextAttribute expectedAttr)
{
return _ValidateLineContains({ 0, gsl::narrow<SHORT>(line) }, expectedContent, expectedAttr);
}
template<class T>
auto _ValidateLinesContain(int startLine, int endLine, T expectedContent, TextAttribute expectedAttr)
{
for (auto line = startLine; line < endLine; ++line)
{
if (!_ValidateLineContains(line, expectedContent, expectedAttr))
{
return false;
}
}
return true;
};
void ScreenBufferTests::ScrollOperations()
{
enum ScrollType : int
{
ScrollUp,
ScrollDown,
InsertLine,
DeleteLine,
ReverseIndex
};
enum ScrollDirection : int
{
Up,
Down
};
ScrollType scrollType;
ScrollDirection scrollDirection;
int scrollMagnitude;
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:scrollType", L"{0, 1, 2, 3, 4}")
TEST_METHOD_PROPERTY(L"Data:scrollMagnitude", L"{1, 2, 5}")
END_TEST_METHOD_PROPERTIES()
VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollType", (int&)scrollType));
VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollMagnitude", scrollMagnitude));
std::wstringstream escapeSequence;
switch (scrollType)
{
case ScrollUp:
Log::Comment(L"Testing scroll up (SU).");
escapeSequence << "\x1b[" << scrollMagnitude << "S";
scrollDirection = Up;
break;
case ScrollDown:
Log::Comment(L"Testing scroll down (SD).");
escapeSequence << "\x1b[" << scrollMagnitude << "T";
scrollDirection = Down;
break;
case InsertLine:
Log::Comment(L"Testing insert line (IL).");
escapeSequence << "\x1b[" << scrollMagnitude << "L";
scrollDirection = Down;
break;
case DeleteLine:
Log::Comment(L"Testing delete line (DL).");
escapeSequence << "\x1b[" << scrollMagnitude << "M";
scrollDirection = Up;
break;
case ReverseIndex:
Log::Comment(L"Testing reverse index (RI).");
for (auto i = 0; i < scrollMagnitude; ++i)
{
escapeSequence << "\x1bM";
}
scrollDirection = Down;
break;
default:
VERIFY_FAIL();
return;
}
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& stateMachine = si.GetStateMachine();
const auto& cursor = si.GetTextBuffer().GetCursor();
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
const auto bufferWidth = si.GetBufferSize().Width();
const auto bufferHeight = si.GetBufferSize().Height();
// Move the viewport down a few lines, and only cover part of the buffer width.
si.SetViewport(Viewport::FromDimensions({ 5, 10 }, { bufferWidth - 10, 10 }), true);
const auto viewportStart = si.GetViewport().Top();
const auto viewportEnd = si.GetViewport().BottomExclusive();
// Fill the entire buffer with Zs. Blue on Green.
const auto bufferChar = L'Z';
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
_FillLines(0, bufferHeight, bufferChar, bufferAttr);
// Fill the viewport with a range of letters to see if they move. Red on Blue.
const auto viewportAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
auto viewportChar = L'A';
auto viewportLine = viewportStart;
while (viewportLine < viewportEnd)
{
_FillLine(viewportLine++, viewportChar++, viewportAttr);
}
// Set the background color so that it will be used to fill the revealed area.
si.SetAttributes({ BACKGROUND_RED });
// Place the cursor in the center.
auto cursorPos = COORD{ bufferWidth / 2, (viewportStart + viewportEnd) / 2 };
// Unless this is reverse index, which has to be be at the top of the viewport.
if (scrollType == ReverseIndex)
{
cursorPos.Y = viewportStart;
}
Log::Comment(L"Set the cursor position and perform the operation.");
VERIFY_SUCCEEDED(si.SetCursorPosition(cursorPos, true));
stateMachine.ProcessString(escapeSequence.str());
Log::Comment(L"Verify cursor didn't move.");
VERIFY_ARE_EQUAL(cursorPos, cursor.GetPosition());
Log::Comment(L"Field of Zs outside viewport should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLinesContain(0, viewportStart, bufferChar, bufferAttr));
VERIFY_IS_TRUE(_ValidateLinesContain(viewportEnd, bufferHeight, bufferChar, bufferAttr));
// Depending on the direction of scrolling, lines are either deleted or inserted.
const auto deletedLines = scrollDirection == Up ? scrollMagnitude : 0;
const auto insertedLines = scrollDirection == Down ? scrollMagnitude : 0;
// Insert and delete operations only scroll the viewport below the cursor position.
const auto scrollStart = (scrollType == InsertLine || scrollType == DeleteLine) ? cursorPos.Y : viewportStart;
// Reset the viewport character and line number for the verification loop.
viewportChar = L'A';
viewportLine = viewportStart;
Log::Comment(L"Lines above the scrolled area should remain unchanged.");
while (viewportLine < scrollStart)
{
VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr));
}
Log::Comment(L"Scrolled area should have moved up/down by given magnitude.");
viewportChar += gsl::narrow<wchar_t>(deletedLines); // Characters dropped when deleting
viewportLine += gsl::narrow<SHORT>(insertedLines); // Lines skipped when inserting
while (viewportLine < viewportEnd - deletedLines)
{
VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr));
}
Log::Comment(L"The revealed area should now be blank, with default buffer attributes.");
const auto revealedStart = scrollDirection == Up ? viewportEnd - deletedLines : scrollStart;
const auto revealedEnd = revealedStart + scrollMagnitude;
VERIFY_IS_TRUE(_ValidateLinesContain(revealedStart, revealedEnd, L' ', si.GetAttributes()));
}
void ScreenBufferTests::InsertChars()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& stateMachine = si.GetStateMachine();
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
// Set the buffer width to 40, with a centered viewport of 20.
const auto bufferWidth = 40;
const auto bufferHeight = si.GetBufferSize().Height();
const auto viewportStart = 10;
const auto viewportEnd = viewportStart + 20;
VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false));
si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true);
Log::Comment(
L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. "
L"Then insert 5 spaces at the cursor. Watch spaces get inserted, text slides right "
L"out of the viewport, pushing some of the Qs out of the buffer.");
const auto insertLine = SHORT{ 10 };
auto insertPos = SHORT{ 20 };
// Place the cursor in the center of the line.
VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true));
// Save the cursor position. It shouldn't move for the rest of the test.
const auto& cursor = si.GetTextBuffer().GetCursor();
auto expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
const auto bufferChar = L'Q';
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
_FillLine(insertLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
const auto textChars = L"ABCDEFGHIJKLMNOPQRST";
const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
// Set the background color so that it will be used to fill the revealed area.
si.SetAttributes({ BACKGROUND_RED });
// Insert 5 spaces at the cursor position.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After: QQQQQQQQQQABCDEFGHIJ KLMNOPQRSTQQQQQ
Log::Comment(L"Inserting 5 spaces in the middle of the line.");
auto before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
stateMachine.ProcessString(L"\x1b[5@");
auto after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr),
L"Field of Qs left of the viewport should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJ", textAttr),
L"First half of the alphabet should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()),
L"Spaces should be inserted with the current attributes at the cursor position.");
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos + 5, insertLine }, L"KLMNOPQRST", textAttr),
L"Second half of the alphabet should have moved to the right by the number of spaces inserted.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd + 5, insertLine }, L"QQQQQ", bufferAttr),
L"Field of Qs right of the viewport should be moved right, half pushed outside the buffer.");
Log::Comment(
L"Test 2: Inserting at the exact end of the line. Same line structure. "
L"Move cursor to right edge of window and insert > 1 space. "
L"Only 1 should be inserted, everything else unchanged.");
// Move cursor to right edge.
insertPos = bufferWidth - 1;
VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true));
expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
_FillLine(insertLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
// Set the background color so that it will be used to fill the revealed area.
si.SetAttributes({ BACKGROUND_RED });
// Insert 5 spaces at the right edge. Only 1 should be inserted.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
Log::Comment(L"Inserting 5 spaces at the right edge of the buffer.");
before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
stateMachine.ProcessString(L"\x1b[5@");
after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr),
L"Field of Qs left of the viewport should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr),
L"Entire viewport range should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, insertLine }, L"QQQQQQQQQ", bufferAttr),
L"Field of Qs right of the viewport should remain unchanged except for the last spot.");
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()),
L"One space should be inserted with the current attributes at the cursor postion.");
Log::Comment(
L"Test 3: Inserting at the exact beginning of the line. Same line structure. "
L"Move cursor to left edge of buffer and insert > buffer width of space. "
L"The whole row should be replaced with spaces.");
// Move cursor to left edge.
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, insertLine }, true));
expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
_FillLine(insertLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
// Insert greater than the buffer width at the left edge. The entire line should be erased.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After:
Log::Comment(L"Inserting 100 spaces at the left edge of the buffer.");
before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
stateMachine.ProcessString(L"\x1b[100@");
after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains(insertLine, L' ', si.GetAttributes()),
L"A whole line of spaces was inserted at the start, erasing the line.");
}
void ScreenBufferTests::DeleteChars()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& stateMachine = si.GetStateMachine();
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
// Set the buffer width to 40, with a centered viewport of 20.
const auto bufferWidth = 40;
const auto bufferHeight = si.GetBufferSize().Height();
const auto viewportStart = 10;
const auto viewportEnd = viewportStart + 20;
VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false));
si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true);
Log::Comment(
L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. "
L"Then delete 5 characters at the cursor. Watch the rest of the line slide left, "
L"replacing the deleted characters, with spaces inserted at the end of the line.");
const auto deleteLine = SHORT{ 10 };
auto deletePos = SHORT{ 20 };
// Place the cursor in the center of the line.
VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true));
// Save the cursor position. It shouldn't move for the rest of the test.
const auto& cursor = si.GetTextBuffer().GetCursor();
auto expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
const auto bufferChar = L'Q';
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
_FillLine(deleteLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
const auto textChars = L"ABCDEFGHIJKLMNOPQRST";
const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
// Set the background color so that it will be used to fill the revealed area.
si.SetAttributes({ BACKGROUND_RED });
// Delete 5 characters at the cursor position.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After: QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
Log::Comment(L"Deleting 5 characters in the middle of the line.");
auto before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
stateMachine.ProcessString(L"\x1b[5P");
auto after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
L"Field of Qs left of the viewport should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJ", textAttr),
L"First half of the alphabet should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L"PQRST", textAttr),
L"Only half of the second part of the alphabet remains.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd - 5, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
L"Field of Qs right of the viewport should be moved left.");
VERIFY_IS_TRUE(_ValidateLineContains({ bufferWidth - 5, deleteLine }, L" ", si.GetAttributes()),
L"The rest of the line should be replaced with spaces with the current attributes.");
Log::Comment(
L"Test 2: Deleting at the exact end of the line. Same line structure. "
L"Move cursor to right edge of window and delete > 1 character. "
L"Only 1 should be deleted, everything else unchanged.");
// Move cursor to right edge.
deletePos = bufferWidth - 1;
VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true));
expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
_FillLine(deleteLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
// Set the background color so that it will be used to fill the revealed area.
si.SetAttributes({ BACKGROUND_RED });
// Delete 5 characters at the right edge. Only 1 should be deleted.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
Log::Comment(L"Deleting 5 characters at the right edge of the buffer.");
before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
stateMachine.ProcessString(L"\x1b[5P");
after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
L"Field of Qs left of the viewport should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr),
L"Entire viewport range should remain unchanged.");
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, deleteLine }, L"QQQQQQQQQ", bufferAttr),
L"Field of Qs right of the viewport should remain unchanged except for the last spot.");
VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L" ", si.GetAttributes()),
L"One character should be erased with the current attributes at the cursor postion.");
Log::Comment(
L"Test 3: Deleting at the exact beginning of the line. Same line structure. "
L"Move cursor to left edge of buffer and delete > buffer width of characters. "
L"The whole row should be replaced with spaces.");
// Move cursor to left edge.
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, deleteLine }, true));
expectedCursor = cursor.GetPosition();
// Fill the entire line with Qs. Blue on Green.
_FillLine(deleteLine, bufferChar, bufferAttr);
// Fill the viewport range with text. Red on Blue.
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
// Delete greater than the buffer width at the left edge. The entire line should be erased.
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
// After:
Log::Comment(L"Deleting 100 characters at the left edge of the buffer.");
before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
stateMachine.ProcessString(L"\x1b[100P");
after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
Log::Comment(before.c_str(), L"Before");
Log::Comment(after.c_str(), L" After");
// Verify cursor didn't move.
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
// Verify the updated structure of the line.
VERIFY_IS_TRUE(_ValidateLineContains(deleteLine, L' ', si.GetAttributes()),
L"A whole line of spaces was inserted from the right, erasing the line.");
}
void _CommonScrollingSetup()
{
// Used for testing MSFT:20204600
@@ -3449,6 +3952,103 @@ void ScreenBufferTests::ReverseLineFeedInMargins()
}
}
void ScreenBufferTests::InsertDeleteLines256Colors()
{
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:insert", L"{false, true}")
TEST_METHOD_PROPERTY(L"Data:colorStyle", L"{0, 1, 2}")
END_TEST_METHOD_PROPERTIES();
// colorStyle will be used to control whether we use a color from the 16
// color table, a color from the 256 color table, or a pure RGB color.
const int Use16Color = 0;
const int Use256Color = 1;
const int UseRGBColor = 2;
bool insert;
int colorStyle;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"insert", insert), L"whether to insert(true) or delete(false) lines");
VERIFY_SUCCEEDED(TestData::TryGetValue(L"colorStyle", colorStyle), L"controls whether to use the 16 color table, 256 table, or RGB colors");
// This test is largely taken from repro code from
// https://github.com/microsoft/terminal/issues/832#issuecomment-507447272
Log::Comment(
L"Sets the attributes to a 256/RGB color, then scrolls some lines with"
L" DL. Verifies the rows are cleared with the attributes we'd expect.");
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& tbi = si.GetTextBuffer();
auto& stateMachine = si.GetStateMachine();
auto& cursor = si.GetTextBuffer().GetCursor();
TextAttribute expectedAttr{ si.GetAttributes() };
std::wstring sgrSeq = L"\x1b[48;5;2m";
if (colorStyle == Use16Color)
{
expectedAttr.SetBackground(gci.GetColorTableEntry(2));
}
else if (colorStyle == Use256Color)
{
expectedAttr.SetBackground(gci.GetColorTableEntry(20));
sgrSeq = L"\x1b[48;5;20m";
}
else if (colorStyle == UseRGBColor)
{
expectedAttr.SetBackground(RGB(1, 2, 3));
sgrSeq = L"\x1b[48;2;1;2;3m";
}
// Set some scrolling margins
stateMachine.ProcessString(L"\x1b[1;3r");
// Set the BG color to the table index 2, as a 256-color sequence
stateMachine.ProcessString(sgrSeq);
VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes());
// Move to home
stateMachine.ProcessString(L"\x1b[H");
// Insert/Delete 10 lines
stateMachine.ProcessString(insert ? L"\x1b[10L" : L"\x1b[10M");
Log::Comment(NoThrowString().Format(
L"cursor=%s", VerifyOutputTraits<COORD>::ToString(cursor.GetPosition()).GetBuffer()));
Log::Comment(NoThrowString().Format(
L"viewport=%s", VerifyOutputTraits<SMALL_RECT>::ToString(si.GetViewport().ToInclusive()).GetBuffer()));
VERIFY_ARE_EQUAL(0, cursor.GetPosition().X);
VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y);
stateMachine.ProcessString(L"foo");
Log::Comment(NoThrowString().Format(
L"cursor=%s", VerifyOutputTraits<COORD>::ToString(cursor.GetPosition()).GetBuffer()));
VERIFY_ARE_EQUAL(3, cursor.GetPosition().X);
VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y);
{
auto iter00 = tbi.GetCellDataAt({ 0, 0 });
auto iter10 = tbi.GetCellDataAt({ 1, 0 });
auto iter20 = tbi.GetCellDataAt({ 2, 0 });
auto iter30 = tbi.GetCellDataAt({ 3, 0 });
auto iter01 = tbi.GetCellDataAt({ 0, 1 });
auto iter02 = tbi.GetCellDataAt({ 0, 2 });
VERIFY_ARE_EQUAL(L"f", iter00->Chars());
VERIFY_ARE_EQUAL(L"o", iter10->Chars());
VERIFY_ARE_EQUAL(L"o", iter20->Chars());
VERIFY_ARE_EQUAL(L"\x20", iter30->Chars());
VERIFY_ARE_EQUAL(L"\x20", iter01->Chars());
VERIFY_ARE_EQUAL(L"\x20", iter02->Chars());
VERIFY_ARE_EQUAL(expectedAttr, iter00->TextAttr());
VERIFY_ARE_EQUAL(expectedAttr, iter10->TextAttr());
VERIFY_ARE_EQUAL(expectedAttr, iter20->TextAttr());
VERIFY_ARE_EQUAL(expectedAttr, iter30->TextAttr());
VERIFY_ARE_EQUAL(expectedAttr, iter01->TextAttr());
VERIFY_ARE_EQUAL(expectedAttr, iter02->TextAttr());
}
}
void ScreenBufferTests::SetOriginMode()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
@@ -3516,3 +4116,130 @@ void ScreenBufferTests::SetOriginMode()
// Reset DECOM so we don't affect future tests
stateMachine.ProcessString(L"\x1B[?6l");
}
void ScreenBufferTests::HardResetBuffer()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& stateMachine = si.GetStateMachine();
const auto& viewport = si.GetViewport();
const auto& cursor = si.GetTextBuffer().GetCursor();
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
auto isBufferClear = [&]() {
auto offset = 0;
auto width = si.GetBufferSize().Width();
for (auto iter = si.GetCellDataAt({}); iter; ++iter, ++offset)
{
if (iter->Chars() != L" " || iter->TextAttr() != TextAttribute{})
{
Log::Comment(NoThrowString().Format(
L"Buffer not clear at (X:%d, Y:%d)",
offset % width,
offset / width));
return false;
}
}
return true;
};
const auto resetToInitialState = L"\033c";
Log::Comment(L"Start with a clear buffer, viewport and cursor at 0,0");
si.SetAttributes(TextAttribute());
si.ClearTextData();
VERIFY_SUCCEEDED(si.SetViewportOrigin(true, { 0, 0 }, true));
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, 0 }, true));
VERIFY_IS_TRUE(isBufferClear());
Log::Comment(L"Write a single line of text to the buffer");
stateMachine.ProcessString(L"Hello World!\n");
VERIFY_IS_FALSE(isBufferClear());
VERIFY_ARE_EQUAL(COORD({ 0, 1 }), cursor.GetPosition());
Log::Comment(L"After a reset, buffer should be clear, with cursor at 0,0");
stateMachine.ProcessString(resetToInitialState);
VERIFY_IS_TRUE(isBufferClear());
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition());
Log::Comment(L"Set the background color to red");
stateMachine.ProcessString(L"\x1b[41m");
Log::Comment(L"Write multiple pages of text to the buffer");
for (auto i = 0; i < viewport.Height() * 2; i++)
{
stateMachine.ProcessString(L"Hello World!\n");
}
VERIFY_IS_FALSE(isBufferClear());
VERIFY_IS_GREATER_THAN(viewport.Top(), viewport.Height());
VERIFY_IS_GREATER_THAN(cursor.GetPosition().Y, viewport.Height());
Log::Comment(L"After a reset, buffer should be clear, with viewport and cursor at 0,0");
stateMachine.ProcessString(resetToInitialState);
VERIFY_IS_TRUE(isBufferClear());
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), viewport.Origin());
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition());
}
void ScreenBufferTests::RestoreDownAltBufferWithTerminalScrolling()
{
// This is a test for microsoft/terminal#1206. Refer to that issue for more
// context
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.SetTerminalScrolling(true);
gci.LockConsole(); // Lock must be taken to manipulate buffer.
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
auto& siMain = gci.GetActiveOutputBuffer();
COORD const coordFontSize = siMain.GetScreenFontSize();
siMain._virtualBottom = siMain._viewport.BottomInclusive();
auto originalView = siMain._viewport;
VERIFY_IS_NULL(siMain._psiMainBuffer);
VERIFY_IS_NULL(siMain._psiAlternateBuffer);
Log::Comment(L"Create an alternate buffer");
if (VERIFY_IS_TRUE(NT_SUCCESS(siMain.UseAlternateScreenBuffer())))
{
VERIFY_IS_NOT_NULL(siMain._psiAlternateBuffer);
auto& altBuffer = *siMain._psiAlternateBuffer;
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
const COORD originalSize = originalView.Dimensions();
const COORD doubledSize = { originalSize.X * 2, originalSize.Y * 2 };
// Create some RECTs, which are dimensions in pixels, because
// ProcessResizeWindow needs to work on rects in screen _pixel_
// dimensions, not character sizes.
RECT originalClientRect{ 0 }, maximizedClientRect{ 0 };
originalClientRect.right = originalSize.X * coordFontSize.X;
originalClientRect.bottom = originalSize.Y * coordFontSize.Y;
maximizedClientRect.right = doubledSize.X * coordFontSize.X;
maximizedClientRect.bottom = doubledSize.Y * coordFontSize.Y;
Log::Comment(NoThrowString().Format(
L"Emulate a maximize"));
// Note that just calling _InternalSetViewportSize does not hit the
// exceptional case here. There's other logic farther down the stack
// that triggers it.
altBuffer.ProcessResizeWindow(&maximizedClientRect, &originalClientRect);
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
Log::Comment(NoThrowString().Format(
L"Emulate a restore down"));
altBuffer.ProcessResizeWindow(&originalClientRect, &maximizedClientRect);
// Before the bugfix, this would fail, with the top being roughly 80,
// halfway into the buffer, with the bottom being anchored to the old
// size.
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
}
}

View File

@@ -1044,18 +1044,8 @@ class ViewportTests
const auto original = Viewport::FromInclusive(srOriginal);
const auto remove = original;
std::vector<Viewport> expected;
expected.emplace_back(Viewport::FromDimensions(original.Origin(), { 0, 0 }));
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
VERIFY_ARE_EQUAL(0u, actual.size(), L"There should be no viewports returned");
}
};

View File

@@ -26,16 +26,21 @@ constexpr COLORREF DEFAULT_BACKGROUND_WITH_ALPHA = OPACITY_OPAQUE | DEFAULT_BACK
constexpr COLORREF POWERSHELL_BLUE = RGB(1, 36, 86);
constexpr short DEFAULT_HISTORY_SIZE = 9001;
#pragma warning(push)
#pragma warning(disable : 26426)
// TODO GH 2674, don't disable this warning, move to std::wstring_view or something like that.
const std::wstring DEFAULT_FONT_FACE{ L"Consolas" };
constexpr int DEFAULT_FONT_SIZE = 10;
constexpr int DEFAULT_FONT_SIZE = 12;
constexpr int DEFAULT_ROWS = 30;
constexpr int DEFAULT_COLS = 120;
const std::wstring DEFAULT_PADDING{ L"0, 0, 0, 0" };
const std::wstring DEFAULT_PADDING{ L"8, 8, 8, 8" };
const std::wstring DEFAULT_STARTING_DIRECTORY{ L"%USERPROFILE%" };
constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE;
constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25;
const std::wstring DEFAULT_WORD_DELIMITERS{ L" ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" };
#pragma warning(pop)

View File

@@ -3,6 +3,30 @@
#include "precomp.h"
void InitializeCursorSize(const HWND hOptionsDlg)
{
unsigned int newRadioValue = IDD_CURSOR_ADVANCED;
if (gpStateInfo->CursorType != 0)
{
// IDD_CURSOR_ADVANCED is used as a placeholder for when a
// non-legacy shape is selected.
newRadioValue = IDD_CURSOR_ADVANCED;
}
else if (gpStateInfo->CursorSize <= 25)
{
newRadioValue = IDD_CURSOR_SMALL;
}
else if (gpStateInfo->CursorSize <= 50)
{
newRadioValue = IDD_CURSOR_MEDIUM;
}
else
{
newRadioValue = IDD_CURSOR_LARGE;
}
CheckRadioButton(hOptionsDlg, IDD_CURSOR_SMALL, IDD_CURSOR_ADVANCED, newRadioValue);
}
bool OptionsCommandCallback(HWND hDlg, const unsigned int Item, const unsigned int Notification, HWND hControlWindow)
{
UINT Value;
@@ -147,6 +171,9 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara
switch (wMsg)
{
case WM_INITDIALOG:
// Initialize the global handle to this dialog
g_hOptionsDlg = hDlg;
CheckDlgButton(hDlg, IDD_HISTORY_NODUP, gpStateInfo->HistoryNoDup);
CheckDlgButton(hDlg, IDD_QUICKEDIT, gpStateInfo->QuickEdit);
CheckDlgButton(hDlg, IDD_INSERT, gpStateInfo->InsertMode);
@@ -167,19 +194,7 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara
CreateAndAssociateToolTipToControl(IDD_INTERCEPT_COPY_PASTE, hDlg, IDS_TOOLTIP_INTERCEPT_COPY_PASTE);
// initialize cursor radio buttons
if (gpStateInfo->CursorSize <= 25)
{
Item = IDD_CURSOR_SMALL;
}
else if (gpStateInfo->CursorSize <= 50)
{
Item = IDD_CURSOR_MEDIUM;
}
else
{
Item = IDD_CURSOR_LARGE;
}
CheckRadioButton(hDlg, IDD_CURSOR_SMALL, IDD_CURSOR_LARGE, Item);
InitializeCursorSize(hDlg);
SetDlgItemInt(hDlg, IDD_HISTORY_SIZE, gpStateInfo->HistoryBufferSize, FALSE);
SendDlgItemMessage(hDlg, IDD_HISTORY_SIZE, EM_LIMITTEXT, 3, 0);

View File

@@ -6,7 +6,7 @@ Module Name:
- OptionsPage.h
Abstract:
- This module contains the definitions for console options dialog.
- This module contains the definitions for console options dialog.
Author(s):
Mike Griese (migrie) Oct-2016
@@ -16,3 +16,4 @@ Author(s):
void ToggleV2OptionsControls(__in const HWND hDlg);
INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
void InitializeCursorSize(const HWND hOptionsDlg);

View File

@@ -3,6 +3,7 @@
#include "precomp.h"
#include "TerminalPage.h"
#include "OptionsPage.h" // For InitializeCursorSize
#include "ColorControl.h"
#include <functional>
@@ -323,10 +324,22 @@ bool TerminalDlgCommand(const HWND hDlg, const WORD item, const WORD command) no
case IDD_TERMINAL_UNDERSCORE:
case IDD_TERMINAL_EMPTYBOX:
case IDD_TERMINAL_SOLIDBOX:
{
gpStateInfo->CursorType = item - IDD_TERMINAL_LEGACY_CURSOR;
UpdateApplyButton(hDlg);
// See GH#1219 - When the cursor state is something other than legacy,
// we need to manually check the "IDD_CURSOR_ADVANCED" radio button on
// the Options page. This will prevent the Options page from manually
// resetting the cursor to legacy.
if (g_hOptionsDlg != INVALID_HANDLE_VALUE)
{
InitializeCursorSize(g_hOptionsDlg);
}
handled = true;
break;
}
case IDD_DISABLE_SCROLLFORWARD:
gpStateInfo->TerminalScrolling = IsDlgButtonChecked(hDlg, IDD_DISABLE_SCROLLFORWARD);
UpdateApplyButton(hDlg);

View File

@@ -42,6 +42,8 @@ BEGIN
WS_TABSTOP | WS_GROUP
AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10,
AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10,
// IDD_CURSOR_ADVANCED is a hidden control, see GH#1219
AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0,
GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP
LTEXT "&Buffer Size:", -1, 119, 25, 78, 9
@@ -106,6 +108,8 @@ BEGIN
WS_TABSTOP | WS_GROUP
AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10,
AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10,
// IDD_CURSOR_ADVANCED is a hidden control, see GH#1219
AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0,
GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP
LTEXT "&Buffer Size:", -1, 119, 25, 78, 9

View File

@@ -43,6 +43,7 @@ Revision History:
#define IDD_LANGUAGE_GROUPBOX 116
#define DID_SETTINGS_COMCTL5 117
#define DID_SETTINGS2_COMCTL5 118
#define IDD_CURSOR_ADVANCED 119
#define DID_FONTDLG 200
#define IDD_STATIC 201

View File

@@ -55,3 +55,4 @@ COLORREF g_fakeBackgroundColor = RGB(12, 12, 12); // Default black
COLORREF g_fakeCursorColor = RGB(242, 242, 242); // Default bright white
HWND g_hTerminalDlg = static_cast<HWND>(INVALID_HANDLE_VALUE);
HWND g_hOptionsDlg = static_cast<HWND>(INVALID_HANDLE_VALUE);

View File

@@ -53,3 +53,4 @@ extern COLORREF g_fakeBackgroundColor;
extern COLORREF g_fakeCursorColor;
extern HWND g_hTerminalDlg;
extern HWND g_hOptionsDlg;

View File

@@ -22,6 +22,7 @@
<ClInclude Include="..\..\inc\IRenderData.hpp" />
<ClInclude Include="..\..\inc\IRenderEngine.hpp" />
<ClInclude Include="..\..\inc\IRenderer.hpp" />
<ClInclude Include="..\..\inc\IRenderTarget.hpp" />
<ClInclude Include="..\..\inc\RenderEngineBase.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\renderer.hpp" />

View File

@@ -80,6 +80,9 @@
<ClInclude Include="..\..\inc\Cluster.hpp">
<Filter>Header Files\inc</Filter>
</ClInclude>
<ClInclude Include="..\..\inc\IRenderTarget.hpp">
<Filter>Header Files\inc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />

View File

@@ -20,16 +20,16 @@ using namespace Microsoft::Console::Render;
// - font - The DirectWrite font face to use while calculating layout (by default, will fallback if necessary)
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
// - width - The count of pixels available per column (the expected pixel width of every column)
CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
IDWriteTextAnalyzer1* const analyzer,
IDWriteTextFormat* const format,
IDWriteFontFace1* const font,
CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
gsl::not_null<IDWriteTextFormat*> const format,
gsl::not_null<IDWriteFontFace1*> const font,
std::basic_string_view<Cluster> const clusters,
size_t const width) :
_factory{ factory },
_analyzer{ analyzer },
_format{ format },
_font{ font },
_factory{ factory.get() },
_analyzer{ analyzer.get() },
_format{ format.get() },
_font{ font.get() },
_localeName{},
_numberSubstitution{},
_readingDirection{ DWRITE_READING_DIRECTION_LEFT_TO_RIGHT },
@@ -39,7 +39,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
_width{ width }
{
// Fetch the locale name out once now from the format
_localeName.resize(format->GetLocaleNameLength() + 1); // +1 for null
_localeName.resize(gsl::narrow_cast<size_t>(format->GetLocaleNameLength()) + 1); // +1 for null
THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow<UINT32>(_localeName.size())));
for (const auto& cluster : clusters)
@@ -58,6 +58,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - S_OK or suitable DirectX/DirectWrite/Direct2D result code.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetColumns(_Out_ UINT32* columns)
{
RETURN_HR_IF_NULL(E_INVALIDARG, columns);
*columns = 0;
RETURN_IF_FAILED(_AnalyzeRuns());
@@ -88,7 +89,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::Draw(_In_opt_ void* clientDrawingContext,
_In_ IDWriteTextRenderer* renderer,
FLOAT originX,
FLOAT originY)
FLOAT originY) noexcept
{
RETURN_IF_FAILED(_AnalyzeRuns());
RETURN_IF_FAILED(_ShapeGlyphRuns());
@@ -146,7 +147,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
}
// Resequence the resulting runs in order before returning to caller.
size_t totalRuns = _runs.size();
const size_t totalRuns = _runs.size();
std::vector<LinkedRun> runs;
runs.resize(totalRuns);
@@ -178,7 +179,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
const auto textLength = gsl::narrow<UINT32>(_text.size());
// Estimate the maximum number of glyph indices needed to hold a string.
UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength);
const UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength);
_glyphIndices.resize(estimatedGlyphCount);
_glyphOffsets.resize(estimatedGlyphCount);
@@ -230,9 +231,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// will shape as if the line is not broken.
Run& run = _runs.at(runIndex);
UINT32 textStart = run.textStart;
UINT32 textLength = run.textLength;
UINT32 maxGlyphCount = static_cast<UINT32>(_glyphIndices.size() - glyphStart);
const UINT32 textStart = run.textStart;
const UINT32 textLength = run.textLength;
UINT32 maxGlyphCount = gsl::narrow<UINT32>(_glyphIndices.size() - glyphStart);
UINT32 actualGlyphCount = 0;
run.glyphStart = glyphStart;
@@ -268,7 +269,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
do
{
hr = _analyzer->GetGlyphs(
&_text[textStart],
&_text.at(textStart),
textLength,
run.fontFace.Get(),
run.isSideways, // isSideways,
@@ -280,10 +281,10 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
nullptr, // featureLengths
0, // featureCount
maxGlyphCount, // maxGlyphCount
&_glyphClusters[textStart],
&textProps[0],
&_glyphIndices[glyphStart],
&glyphProps[0],
&_glyphClusters.at(textStart),
&textProps.at(0),
&_glyphIndices.at(glyphStart),
&glyphProps.at(0),
&actualGlyphCount);
tries++;
@@ -291,7 +292,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
{
// Try again using a larger buffer.
maxGlyphCount = _EstimateGlyphCount(maxGlyphCount);
UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount;
const UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount;
glyphProps.resize(maxGlyphCount);
_glyphIndices.resize(totalGlyphsArrayCount);
@@ -306,19 +307,19 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// Get the placement of the all the glyphs.
_glyphAdvances.resize(std::max(static_cast<size_t>(glyphStart + actualGlyphCount), _glyphAdvances.size()));
_glyphOffsets.resize(std::max(static_cast<size_t>(glyphStart + actualGlyphCount), _glyphOffsets.size()));
_glyphAdvances.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphAdvances.size()));
_glyphOffsets.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphOffsets.size()));
const auto fontSizeFormat = _format->GetFontSize();
const auto fontSize = fontSizeFormat * run.fontScale;
hr = _analyzer->GetGlyphPlacements(
&_text[textStart],
&_glyphClusters[textStart],
&textProps[0],
&_text.at(textStart),
&_glyphClusters.at(textStart),
&textProps.at(0),
textLength,
&_glyphIndices[glyphStart],
&glyphProps[0],
&_glyphIndices.at(glyphStart),
&glyphProps.at(0),
actualGlyphCount,
run.fontFace.Get(),
fontSize,
@@ -329,8 +330,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
NULL, // features
NULL, // featureRangeLengths
0, // featureRanges
&_glyphAdvances[glyphStart],
&_glyphOffsets[glyphStart]);
&_glyphAdvances.at(glyphStart),
&_glyphOffsets.at(glyphStart));
RETURN_IF_FAILED(hr);
@@ -391,13 +392,13 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
{
// Advance is how wide in pixels the glyph is
auto& advance = _glyphAdvances[i];
auto& advance = _glyphAdvances.at(i);
// Offsets is how far to move the origin (in pixels) from where it is
auto& offset = _glyphOffsets[i];
auto& offset = _glyphOffsets.at(i);
// Get how many columns we expected the glyph to have and mutiply into pixels.
const auto columns = _textClusterColumns[i];
const auto columns = _textClusterColumns.at(i);
const auto advanceExpected = static_cast<float>(columns * _width);
// If what we expect is bigger than what we have... pad it out.
@@ -419,7 +420,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
// height proportional to the width that we desire.
INT32 advanceInDesignUnits;
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices[i], &advanceInDesignUnits));
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices.at(i), &advanceInDesignUnits));
// When things are drawn, we want the font size (as specified in the base font in the original format)
// to be scaled by some factor.
@@ -467,6 +468,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
IDWriteTextRenderer* renderer,
const D2D_POINT_2F origin) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, renderer);
try
{
// We're going to start from the origin given and walk to the right for each
@@ -477,21 +480,21 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
{
// Get the run
Run& run = _runs.at(runIndex);
const Run& run = _runs.at(runIndex);
// Prepare the glyph run and description objects by converting our
// internal storage representation into something that matches DWrite's structures.
DWRITE_GLYPH_RUN glyphRun = { 0 };
DWRITE_GLYPH_RUN glyphRun;
glyphRun.bidiLevel = run.bidiLevel;
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
glyphRun.fontFace = run.fontFace.Get();
glyphRun.glyphAdvances = _glyphAdvances.data() + run.glyphStart;
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
glyphRun.glyphCount = run.glyphCount;
glyphRun.glyphIndices = _glyphIndices.data() + run.glyphStart;
glyphRun.glyphOffsets = _glyphOffsets.data() + run.glyphStart;
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
glyphRun.isSideways = false;
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription = { 0 };
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
glyphRunDescription.clusterMap = _glyphClusters.data();
glyphRunDescription.localeName = _localeName.data();
glyphRunDescription.string = _text.data();
@@ -539,7 +542,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// Return Value:
// - An estimate of how many glyph spaces may be required in the shaping arrays
// to hold the data from a string of the given length.
[[nodiscard]] UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept
[[nodiscard]] constexpr UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept
{
// This formula is from https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nf-dwrite-idwritetextanalyzer-getglyphs
// and is the recommended formula for estimating buffer size for glyph count.
@@ -561,12 +564,15 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength)
{
RETURN_HR_IF_NULL(E_INVALIDARG, textString);
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
*textString = nullptr;
*textLength = 0;
if (textPosition < _text.size())
{
*textString = _text.data() + textPosition;
*textString = &_text.at(textPosition);
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
}
@@ -585,8 +591,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - S_OK or appropriate STL/GSL failure code.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetTextBeforePosition(UINT32 textPosition,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength)
_Out_ UINT32* textLength) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, textString);
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
*textString = nullptr;
*textLength = 0;
@@ -606,7 +615,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - <none>
// Return Value:
// - The reading direction held for this layout from construction
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection()
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection() noexcept
{
return _readingDirection;
}
@@ -622,8 +631,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - S_OK or appropriate STL/GSL failure code.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetLocaleName(UINT32 textPosition,
_Out_ UINT32* textLength,
_Outptr_result_z_ WCHAR const** localeName)
_Outptr_result_z_ WCHAR const** localeName) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
RETURN_HR_IF_NULL(E_INVALIDARG, localeName);
*localeName = _localeName.data();
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
@@ -641,8 +653,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - S_OK or appropriate STL/GSL failure code.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetNumberSubstitution(UINT32 textPosition,
_Out_ UINT32* textLength,
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution)
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
RETURN_HR_IF_NULL(E_INVALIDARG, numberSubstitution);
*numberSubstitution = nullptr;
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
@@ -799,7 +814,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
RETURN_IF_FAILED(format1->GetFontCollection(&collection));
std::wstring familyName;
familyName.resize(format1->GetFontFamilyNameLength() + 1);
familyName.resize(gsl::narrow_cast<size_t>(format1->GetFontFamilyNameLength()) + 1);
RETURN_IF_FAILED(format1->GetFontFamilyName(familyName.data(), gsl::narrow<UINT32>(familyName.size())));
const auto weight = format1->GetFontWeight();
@@ -912,7 +927,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
if (textLength < runTextLength)
{
runTextLength = textLength; // Limit to what's actually left.
UINT32 runTextStart = run.textStart;
const UINT32 runTextStart = run.textStart;
_SplitCurrentRun(runTextStart + runTextLength);
}
@@ -940,12 +955,12 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
// - <none> - Updates internal state
void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
{
if (_runIndex < _runs.size() && _runs[_runIndex].ContainsTextPosition(textPosition))
if (_runIndex < _runs.size() && _runs.at(_runIndex).ContainsTextPosition(textPosition))
{
return;
}
_runIndex = static_cast<UINT32>(
_runIndex = gsl::narrow<UINT32>(
std::find(_runs.begin(), _runs.end(), textPosition) - _runs.begin());
}
@@ -957,13 +972,13 @@ void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
// - <none> - Updates internal state, the back half will be selected after running
void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
{
UINT32 runTextStart = _runs.at(_runIndex).textStart;
const UINT32 runTextStart = _runs.at(_runIndex).textStart;
if (splitPosition <= runTextStart)
return; // no change
// Grow runs by one.
size_t totalRuns = _runs.size();
const size_t totalRuns = _runs.size();
try
{
_runs.resize(totalRuns + 1);
@@ -974,16 +989,16 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
}
// Copy the old run to the end.
LinkedRun& frontHalf = _runs[_runIndex];
LinkedRun& frontHalf = _runs.at(_runIndex);
LinkedRun& backHalf = _runs.back();
backHalf = frontHalf;
// Adjust runs' text positions and lengths.
UINT32 splitPoint = splitPosition - runTextStart;
const UINT32 splitPoint = splitPosition - runTextStart;
backHalf.textStart += splitPoint;
backHalf.textLength -= splitPoint;
frontHalf.textLength = splitPoint;
frontHalf.nextRunIndex = static_cast<UINT32>(totalRuns);
_runIndex = static_cast<UINT32>(totalRuns);
frontHalf.nextRunIndex = gsl::narrow<UINT32>(totalRuns);
_runIndex = gsl::narrow<UINT32>(totalRuns);
}
#pragma endregion

View File

@@ -19,10 +19,10 @@ namespace Microsoft::Console::Render
public:
// Based on the Windows 7 SDK sample at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/DirectWrite/CustomLayout
CustomTextLayout(IDWriteFactory1* const factory,
IDWriteTextAnalyzer1* const analyzer,
IDWriteTextFormat* const format,
IDWriteFontFace1* const font,
CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
gsl::not_null<IDWriteTextFormat*> const format,
gsl::not_null<IDWriteFontFace1*> const font,
const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters,
size_t const width);
@@ -32,43 +32,43 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT STDMETHODCALLTYPE Draw(_In_opt_ void* clientDrawingContext,
_In_ IDWriteTextRenderer* renderer,
FLOAT originX,
FLOAT originY);
FLOAT originY) noexcept;
// IDWriteTextAnalysisSource methods
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength) override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength) override;
[[nodiscard]] virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition,
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength) override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition,
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
_Out_ UINT32* textLength) noexcept override;
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() noexcept override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition,
_Out_ UINT32* textLength,
_Outptr_result_z_ WCHAR const** localeName) noexcept override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition,
_Out_ UINT32* textLength,
_Outptr_result_z_ WCHAR const** localeName) override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition,
_Out_ UINT32* textLength,
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) override;
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept override;
// IDWriteTextAnalysisSink methods
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition,
UINT32 textLength,
_In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition,
UINT32 textLength,
_In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition,
UINT32 textLength,
UINT8 explicitLevel,
UINT8 resolvedLevel) override;
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition,
UINT32 textLength,
_In_ IDWriteNumberSubstitution* numberSubstitution) override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition,
UINT32 textLength,
_In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition,
UINT32 textLength,
_In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition,
UINT32 textLength,
UINT8 explicitLevel,
UINT8 resolvedLevel) override;
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition,
UINT32 textLength,
_In_ IDWriteNumberSubstitution* numberSubstitution) override;
protected:
// A single contiguous run of characters containing the same analysis results.
struct Run
{
Run() :
Run() noexcept :
textStart(),
textLength(),
glyphStart(),
@@ -93,12 +93,12 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<IDWriteFontFace1> fontFace;
FLOAT fontScale;
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const noexcept
{
return desiredTextPosition >= textStart && desiredTextPosition < textStart + textLength;
}
inline bool operator==(UINT32 desiredTextPosition) const
inline bool operator==(UINT32 desiredTextPosition) const noexcept
{
// Search by text position using std::find
return ContainsTextPosition(desiredTextPosition);
@@ -108,7 +108,7 @@ namespace Microsoft::Console::Render
// Single text analysis run, which points to the next run.
struct LinkedRun : Run
{
LinkedRun() :
LinkedRun() noexcept :
nextRunIndex(0)
{
}
@@ -132,7 +132,7 @@ namespace Microsoft::Console::Render
IDWriteTextRenderer* renderer,
const D2D_POINT_2F origin) noexcept;
[[nodiscard]] static UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
private:
const ::Microsoft::WRL::ComPtr<IDWriteFactory1> _factory;

View File

@@ -21,8 +21,10 @@ using namespace Microsoft::Console::Render;
// Return Value:
// - S_OK
[[nodiscard]] HRESULT CustomTextRenderer::IsPixelSnappingDisabled(void* /*clientDrawingContext*/,
_Out_ BOOL* isDisabled)
_Out_ BOOL* isDisabled) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled);
*isDisabled = false;
return S_OK;
}
@@ -38,9 +40,12 @@ using namespace Microsoft::Console::Render;
// Return Value:
// - S_OK
[[nodiscard]] HRESULT CustomTextRenderer::GetPixelsPerDip(void* clientDrawingContext,
_Out_ FLOAT* pixelsPerDip)
_Out_ FLOAT* pixelsPerDip) noexcept
{
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, pixelsPerDip);
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
float dpiX, dpiY;
drawingContext->renderTarget->GetDpi(&dpiX, &dpiY);
@@ -58,12 +63,24 @@ using namespace Microsoft::Console::Render;
// Return Value:
// - S_OK
[[nodiscard]] HRESULT CustomTextRenderer::GetCurrentTransform(void* clientDrawingContext,
DWRITE_MATRIX* transform)
DWRITE_MATRIX* transform) noexcept
{
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, transform);
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
// Retrieve as D2D1 matrix then copy into DWRITE matrix.
D2D1_MATRIX_3X2_F d2d1Matrix{ 0 };
drawingContext->renderTarget->GetTransform(&d2d1Matrix);
transform->dx = d2d1Matrix.dx;
transform->dy = d2d1Matrix.dy;
transform->m11 = d2d1Matrix.m11;
transform->m12 = d2d1Matrix.m12;
transform->m21 = d2d1Matrix.m21;
transform->m22 = d2d1Matrix.m22;
// Matrix structures are defined identically
drawingContext->renderTarget->GetTransform((D2D1_MATRIX_3X2_F*)transform);
return S_OK;
}
#pragma endregion
@@ -88,17 +105,16 @@ using namespace Microsoft::Console::Render;
FLOAT baselineOriginX,
FLOAT baselineOriginY,
_In_ const DWRITE_UNDERLINE* underline,
IUnknown* clientDrawingEffect)
IUnknown* clientDrawingEffect) noexcept
{
_FillRectangle(clientDrawingContext,
clientDrawingEffect,
baselineOriginX,
baselineOriginY + underline->offset,
underline->width,
underline->thickness,
underline->readingDirection,
underline->flowDirection);
return S_OK;
return _FillRectangle(clientDrawingContext,
clientDrawingEffect,
baselineOriginX,
baselineOriginY + underline->offset,
underline->width,
underline->thickness,
underline->readingDirection,
underline->flowDirection);
}
// Routine Description:
@@ -120,17 +136,16 @@ using namespace Microsoft::Console::Render;
FLOAT baselineOriginX,
FLOAT baselineOriginY,
_In_ const DWRITE_STRIKETHROUGH* strikethrough,
IUnknown* clientDrawingEffect)
IUnknown* clientDrawingEffect) noexcept
{
_FillRectangle(clientDrawingContext,
clientDrawingEffect,
baselineOriginX,
baselineOriginY + strikethrough->offset,
strikethrough->width,
strikethrough->thickness,
strikethrough->readingDirection,
strikethrough->flowDirection);
return S_OK;
return _FillRectangle(clientDrawingContext,
clientDrawingEffect,
baselineOriginX,
baselineOriginY + strikethrough->offset,
strikethrough->width,
strikethrough->thickness,
strikethrough->readingDirection,
strikethrough->flowDirection);
}
// Routine Description:
@@ -146,16 +161,17 @@ using namespace Microsoft::Console::Render;
// - flowDirection - textual flow information that could affect the rectangle
// Return Value:
// - S_OK
void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
IUnknown* clientDrawingEffect,
float x,
float y,
float width,
float thickness,
DWRITE_READING_DIRECTION /*readingDirection*/,
DWRITE_FLOW_DIRECTION /*flowDirection*/)
[[nodiscard]] HRESULT CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
IUnknown* clientDrawingEffect,
float x,
float y,
float width,
float thickness,
DWRITE_READING_DIRECTION /*readingDirection*/,
DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept
{
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
// Get brush
ID2D1Brush* brush = drawingContext->foregroundBrush;
@@ -165,8 +181,10 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
brush = static_cast<ID2D1Brush*>(clientDrawingEffect);
}
D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness);
const D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness);
drawingContext->renderTarget->FillRectangle(&rect, brush);
return S_OK;
}
// Routine Description:
@@ -189,8 +207,10 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
IDWriteInlineObject* inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect)
IUnknown* clientDrawingEffect) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, inlineObject);
return inlineObject->Draw(clientDrawingContext,
this,
originX,
@@ -233,12 +253,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
// Since we've delegated the drawing of the background of the text into this function, the origin passed in isn't actually the baseline.
// It's the top left corner. Save that off first.
D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY);
const D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY);
// Then make a copy for the baseline origin (which is part way down the left side of the text, not the top or bottom).
// We'll use this baseline Origin for drawing the actual text.
D2D1_POINT_2F baselineOrigin = origin;
baselineOrigin.y += drawingContext->spacing.baseline;
const D2D1_POINT_2F baselineOrigin{ origin.x, origin.y + drawingContext->spacing.baseline };
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
@@ -249,11 +268,9 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
rect.bottom = rect.top + drawingContext->cellSize.height;
rect.left = origin.x;
rect.right = rect.left;
const auto advancesSpan = gsl::make_span(glyphRun->glyphAdvances, glyphRun->glyphCount);
for (UINT32 i = 0; i < glyphRun->glyphCount; i++)
{
rect.right += glyphRun->glyphAdvances[i];
}
rect.right = std::accumulate(advancesSpan.cbegin(), advancesSpan.cend(), rect.right);
d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);
@@ -270,7 +287,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
RETURN_IF_FAILED(drawingContext->dwriteFactory->QueryInterface(dwriteFactory4.GetAddressOf()));
// The list of glyph image formats this renderer is prepared to support.
DWRITE_GLYPH_IMAGE_FORMATS supportedFormats =
const DWRITE_GLYPH_IMAGE_FORMATS supportedFormats =
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
@@ -283,14 +300,14 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
// Determine whether there are any color glyph runs within glyphRun. If
// there are, glyphRunEnumerator can be used to iterate through them.
::Microsoft::WRL::ComPtr<IDWriteColorGlyphRunEnumerator1> glyphRunEnumerator;
HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin,
glyphRun,
glyphRunDescription,
supportedFormats,
measuringMode,
nullptr,
0,
&glyphRunEnumerator);
const HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin,
glyphRun,
glyphRunDescription,
supportedFormats,
measuringMode,
nullptr,
0,
&glyphRunEnumerator);
// If the analysis found no color glyphs in the run, just draw normally.
if (hr == DWRITE_E_NOCOLOR)
@@ -320,7 +337,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
DWRITE_COLOR_GLYPH_RUN1 const* colorRun;
RETURN_IF_FAILED(glyphRunEnumerator->GetCurrentRun(&colorRun));
D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY);
const D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY);
switch (colorRun->glyphImageFormat)
{
@@ -357,7 +374,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
// This run is solid-color outlines, either from non-color
// glyphs or from COLR glyph layers. Use Direct2D to draw them.
ID2D1Brush* layerBrush;
ID2D1Brush* layerBrush{ nullptr };
// The rule is "if 0xffff, use current brush." See:
// https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_2/ns-dwrite_2-dwrite_color_glyph_run
if (colorRun->paletteIndex == 0xFFFF)
@@ -414,6 +431,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
ID2D1Brush* brush)
{
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRunDescription);
RETURN_HR_IF_NULL(E_INVALIDARG, brush);
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
RETURN_IF_FAILED(clientDrawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
@@ -433,8 +455,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
D2D1_POINT_2F baselineOrigin,
DWRITE_MEASURING_MODE /*measuringMode*/,
_In_ const DWRITE_GLYPH_RUN* glyphRun,
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/)
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
// This is regular text but manually
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());
@@ -473,8 +498,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
D2D1_POINT_2F baselineOrigin,
DWRITE_MEASURING_MODE /*measuringMode*/,
_In_ const DWRITE_GLYPH_RUN* glyphRun,
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/)
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
// This is glow text manually
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());

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