Compare commits

...

75 Commits

Author SHA1 Message Date
Dustin Howett
abfaa5dc5f Fix the Release build ProxyStubClsids from #13570 2022-10-14 18:56:30 -05:00
Dustin Howett
a2bced81a9 Stop freeing BSTRs that do not belong to us (oops)
These cause AppVerifier to explode.
2022-10-14 17:57:54 -05:00
Mike Griese
72fbcd6559 Wire up passing LNK/EXE data from OpenCon to ITerminalHandoff (#13570)
This PR by itself doesn't _really_ change much. Technically, now the Terminal will respect the Title of a `.lnk` when started for defterm, but we don't do anything else yet. Primarily, the goal of this PR is to just wire up startup info in OpenConsole to the connected Terminal.
* This required a bit of changes in `srvinit.cpp:ConsoleEstablishHandoff` to replicate other bits of startup, where we crack open the connect message to get the relevant bits of info.
* We pack that all into a `TERMINAL_STARTUP_INFO`, which we pass along to the registered terminal application.
* `ConptyConnection` accepts the handoff, and gathers that information out of the `TERMINAL_STARTUP_INFO`
* Some other updates to the scratch sln were made to make it build again (related, but unimportant).
* This is a precursor to:
  * #13111
  * #12154
* Closes #9458
* Tested manually
* I work here

(cherry picked from commit 7e47f6aab9)
Service-Card-Id: 86230565
Service-Version: 1.15
2022-10-14 17:48:17 -05:00
Dustin L. Howett
cae559376a Disallow a window waiting on system() to block DefTerm startup (#14195)
We'll just ping the window and give it a chance to respond before we
bunk with it.

Fixes #14131

(cherry picked from commit ab04067e49)
Service-Card-Id: 86230023
Service-Version: 1.15
2022-10-14 16:51:23 -05:00
Dustin L. Howett
c6ea72fb61 OpenHere: stop failing if there's no site... (#14211)
It cannot be known why there is no site.
We should at least not crash.

Fixes MSFT-41571451

(cherry picked from commit 18e4e22394)
Service-Card-Id: 86206545
Service-Version: 1.15
2022-10-14 16:12:13 -05:00
Bjorn Neergaard
b1306c5e79 Fix a missing entry for intenseTextStyle in the Profile schema (#14210)
Fix a missing entry in the JSON schema for `intenseTextStyle`.

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

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

Partially addresses #13387

(cherry picked from commit 33cb0eb05f)
Service-Card-Id: 86228742
Service-Version: 1.15
2022-10-14 16:12:11 -05:00
Dustin L. Howett
1735110040 Pipe DirectKey events into TerminalPage for Alt+Space (#14221)
This pull request operates on the same theory as #14217, but at a lower
level. Carlos and I discovered that TerminalPage *already* has an
action-dispatching key preview handler, and that my implementation of
`IDirectKeyListener` handles focus-tree bubbling mostly correctly.

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

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

Fixes #11970
Closes #14217
Fixes MSFT-41390832

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

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

Closes #14132
Pretty sure this also applies to #1810

## Validation Steps Performed
* Open 5 tabs and run MSYS2's `bash --login` in each of them
* `Enter-VsDevShell` in another tab
* Close window
* 5 tab processes are killed instantly, 1 after ~3s 
* Replace conhost with OpenConsole via sfpcopy
* Launch Dozens of Git Bash tabs in VS Code
* Close them randomly
* Remaining ones still work, processes are gone 

(cherry picked from commit 1774cfd843)
Service-Card-Id: 86178271
Service-Version: 1.15
2022-10-13 18:19:00 -05:00
Leonard Hecker
0f947043c9 Fix potential lags/deadlocks during tab close (#14041)
`ConptyClosePseudoConsole` blocks until OpenConsole exits.
This is problematic for the changes in 666c446, which stopped calling that
function on a background thread to solve a race condition. This commit fixes
the potential lags/deadlocks from waiting on OpenConsole's exit, by adding
`ConptyClosePseudoConsoleNoWait` which only closes the IO handles and allows
OpenConsole to exit naturally. This uncovered another potential deadlock
in `ServiceLocator::RundownAndExit` which might call itself recursively.

Closes #14032

## Validation Steps Performed
* Print tons of text and concurrently close the tab.
  Tab closes, OpenConsole/pwsh exits instantly 
* Use `Enter-VsDevShell` and close the tab.
  Tab closes instantly, OpenConsole/pwsh exits after ~5 seconds 

(cherry picked from commit 274bdb31da)
Service-Card-Id: 86209670
Service-Version: 1.15
2022-10-13 18:18:58 -05:00
Dustin L. Howett
0f6d29adaf Add a maximum OSC 8 URI length of 2MB following iTerm2 (#14198)
c0b2f488c1

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

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

Fixes #14200

(cherry picked from commit 07201d6cd1)
Service-Card-Id: 86182617
Service-Version: 1.15
2022-10-13 17:02:05 -05:00
Jeroen B
24a8463818 Fix duplication issue for unfocused tabs (#13964)
Instead of using the currently focused tab when an unfocused tab is duplicated, the `_MakePane(...)` function now uses an optional source tab argument that points to the correct tab being duplicated.

## Validation Steps Performed

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

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

Closes #13942

(cherry picked from commit 8f08bb04d0)
Service-Card-Id: 86159036
Service-Version: 1.15
2022-10-12 15:00:20 -05:00
Carlos Zamora
c25cd5c9c7 Make SUI breadcrumb readable by screen readers (#14180)
The breadcrumbs in the SUI were not readable by screen readers because they are represented as a button with a text block inside of it. Turns out, if you make the DataTemplate's item `IStringable` (meaning it has a `ToString()`), it all magically works! Allowing the screen reader to read the button as text.

Closes #13826

(cherry picked from commit 30046dd4a7)
Service-Card-Id: 86158951
Service-Version: 1.15
2022-10-12 15:00:19 -05:00
Leonard Hecker
78f1bdff69 Fix a ControlCore race condition on connection close (#13882)
As noted by the `winrt::event` documentation:
> [...] But for asynchronous events, even after revoking [...], an in-flight
> event might reach your object after it has started destructing.

This is because while adding/removing/calling event handlers might be
thread-safe, there's no guarantee that they run mutually exclusive.

This commit fixes the issue by reverting 6f0f245. Since we never checked
the result of closing a terminal connection anyways, this commit simply drops
the wait on the connection being teared down to ensure #1996 doesn't regress.

Closes #13880

## Validation Steps Performed
* Open tab, close tab, open tab, close tab, open tab, close tab
  * ConPTY 
  * Azure 
* Closing a tab with a huge amount of panes 
* Opening a bunch of tabs and then closing the window 
* Closing a tab while it's busy with VT 
* `wtd -w 0 nt cmd /c exit` 
* `wtd -w -1 cmd /c exit`
  * No WerFault spawns 

(cherry picked from commit 666c446bc3)
Service-Card-Id: 86178273
Service-Version: 1.15
2022-10-12 15:00:18 -05:00
Carlos Zamora
dda7410408 [wpf] Add UIA events (#14097)
Adds UIA events to the WPF control for the following items:
- selection changed
- text changed (and output)
- cursor changed

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

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

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

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

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

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

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

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

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

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

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

Closes #12642

(cherry picked from commit 5608cf15a3)
Service-Card-Id: 86081128
Service-Version: 1.15
2022-10-06 18:36:03 -05:00
Dustin Howett
07cfad1e35 Delay-load dsound.dll in MidiAudio
Closes #14104

(cherry-picked from commit fba4e227f0)
2022-10-04 13:15:23 -05:00
Dustin Howett
69789eae14 Fix build for a1879878f 2022-09-28 17:14:44 -05:00
Dustin Howett
daec33babc Fix build for d1c7ee2c1 2022-09-28 12:15:42 -05:00
Dustin L. Howett
a1879878f0 Reject illegal paths in OSC 9;9 (#14093)
Paths that contain illegal path components will be dropped
in the dispatcher.

(cherry picked from commit fc0ef37977)
Service-Card-Id: 85900829
Service-Version: 1.15
2022-09-28 11:47:30 -05:00
Dustin L. Howett
faa4c1ac8b OpenHere: Replace explorer window lookup code w/ site lookup (#14048)
When we first introduced the shell extension, it didn't work properly
for some folders (such as the Desktop, or perhaps any "background"
click) due to a bug in Windows. We worked around that bug with the help
of an awesome community member, who contributed code that would pull up
the topmost Explorer window and query its location.

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

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

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

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

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

References #13206
References #13523
Closes #12578

Co-authored-by: John Lueders <johnlue@microsoft.com>
(cherry picked from commit 5027c8031d)
Service-Card-Id: 85788408
Service-Version: 1.15
2022-09-27 17:35:35 -05:00
EliaSchiavon
d1c7ee2c17 Disallow certain non-filename characters in Export Text (#13693)
Added control in code for not allowed characters in the filename when saving a
tab.

Closes #13664

(cherry picked from commit 8721573957)
Service-Card-Id: 85487287
Service-Version: 1.15
2022-09-27 17:33:24 -05:00
Dustin L. Howett
377cc05fb7 vPack: fix submit branch, add github token from consvc (#13959)
Our Windows branch name changed, and I took this opportunity to resolve
an issue where vpack builds would occasionally fail due to GitHub rate
limiting the Azure DevOps IP addresses.

(cherry picked from commit 3958c938af)
Service-Card-Id: 85567588
Service-Version: 1.15
2022-09-09 13:30:28 -05:00
Dustin Howett
06648e3212 Revert "Disallow certain non-filename characters in Export Text (#13693)"
This reverts commit 64c774e2a7.
2022-09-09 12:43:28 -05:00
Dustin Howett
acc019b3cb PGO: train 1.15 separately 2022-09-09 12:29:19 -05:00
Leonard Hecker
8d37b213e2 Reduce amount of telemetry events (#13951)
This commit reduces the amount of telemetry during general usage by about half.
8 events that weren't really used anymore were removed.
1 new event was added ("AppInitialized") which will help us investigate #5907.
During review 9 events were found that were incorrectly tagged as perf. data.

* Launch Windows Terminal
* The "latency" field "AppInitialized" matches the approx. launch time 

(cherry picked from commit 37c159abba)
Service-Card-Id: 85548958
Service-Version: 1.15
2022-09-09 12:19:24 -05:00
Dustin L. Howett
efb7f30717 Disable RTTI on TerminalApp and friends (#13947)
When Leonard updated CLI11 in #12658, he imported a version that no
longer requires RTTI. Since CLI11 was the only reason we had enabled
RTTI in any of our projects, we're finally able to turn it off.

|        | TerminalApp.dll | MSIX Package |
| ------ | ---------------:| ------------:|
| Before |         3180KiB |      6545KiB |
| After  |         1970KiB |      6426KiB |
| Delta  |    **-1210KiB** |  **-119KiB** |

(cherry picked from commit e682cdad5e)
Service-Card-Id: 85546966
Service-Version: 1.15
2022-09-09 12:17:51 -05:00
Sergey
decfc3dcee Use the tab's active title for Export Text (#13915)
Takes title from the tab instead of the TermControl

Closes #13909

(cherry picked from commit 2c341c8deb)
Service-Card-Id: 85427880
Service-Version: 1.15
2022-09-09 12:16:36 -05:00
Carlos Zamora
c288df551e fix: if selection exists, Mark Mode should promote it
(cherry picked from commit d8f3e56b3b)
2022-09-07 11:09:58 -05:00
dansmor7
fc0ad5ab75 Update caption buttons to use glyphs (#13341)
The min/max/close buttons now use the same font glyphs used in Windows instead of paths.
They will also look different depending on whether you use Windows 10 or 11.

With certain scaling levels, the new fluent icons are always blurry on titlebars (win32, UWP, terminal).
I didn't check, but I assume that the glyphs will not be blurry on Windows 10 because they use the old font.
However, the glyps seem to have some alignment issues, making them even more blurry, and I'm not sure if I can fix that.

I looked into the minimize button problem:
* The UWP titlebar has 4px/5px, just like Terminal, but win32 titlebars have 5px/4px
* The new fluent icons have rounded caps, but the win32 minimize one doesn't (it's very subtle)
* The win32 minimize button is not blurry when the other buttons are

From this I presume that the minimize button is still using the old icon. So perhaps the Terminal/UWP vertical alignment is correct.

I was able to improve the rendering by wrapping the icon inside a Viewbox.

However, it won't perfectly match UWP, because scaling is calculated differently.
The icon width is 10px, which on 1.25x scaling would become 12.5px.
UWP titlebars truncate that to 12px, while Xaml renders that as 13px.

This is probably the best that can be done for now.

I found these icons [here](https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font).

(cherry picked from commit 67f6b29d63)
Service-Card-Id: 85488527
Service-Version: 1.15
2022-09-06 15:47:42 -05:00
Leonard Hecker
20b0447774 Stop throwing exceptions for FontIcons (#13858)
While having a debugger attached, opening the settings tab generates an
uncomfortable amount of exceptions. This change reduces this by a lot.

## Validation Steps Performed
* Icons still appear 

(cherry picked from commit 76a5ff143e)
Service-Card-Id: 85487971
Service-Version: 1.15
2022-09-06 15:22:33 -05:00
Leonard Hecker
c4130367f3 Fix two race conditions around pseudo window visibility (#13832)
This commit fixes two race conditions:
* `SetPseudoWindowCallback` set the `_pseudoWindowMessageCallback`
  callback after the Win32 message thread was already spawned.
  This issue was fixed by instead using the `ServiceLocator` to get
  a hold of the global `VtIo` instance which is created statically.
* `XtermEngine::SetWindowVisibility` was called without holding the
  console lock. This issue was fixed by simply acquiring it first.

Closes MSFT:40913882

## Validation Steps Performed
* Add `IsConsoleLocked()` assertion in `VtEngine::_Write`
* Run Windows Terminal
* No assertion failures 

(cherry picked from commit 4f3afee728)
Service-Card-Id: 85487461
Service-Version: 1.15
2022-09-06 15:22:31 -05:00
EliaSchiavon
64c774e2a7 Disallow certain non-filename characters in Export Text (#13693)
Added control in code for not allowed characters in the filename when saving a
tab.

Closes #13664

(cherry picked from commit 8721573957)
Service-Card-Id: 85487287
Service-Version: 1.15
2022-09-06 15:22:29 -05:00
Dustin L. Howett
462192e88b Stop our release pipeline from downloading the entire SDK *twice* (#13491)
The debugging tools folks helpfully published PdbStr and SrcTool as
NuGet packages for us to take a dependency on! Now we can end this
wasteful practice once and for all...

NOTE: This change introduces a harmless error message at the end of
symbol indexing. Instead of printing out the list of mapped sources,
`srctool` will complain that `srcsrv.dll` is missing. This is only cosmetic.

(cherry picked from commit 5f3222b993)
Service-Card-Id: 85487966
Service-Version: 1.15
2022-09-06 15:22:26 -05:00
Dustin L. Howett
062e2b3f20 RelEng: Teach ServicingPipeline to infer the version if it's null (#13439)
```
% .\tools\ReleaseEngineering\ServicingPipeline.ps1
Inferred servicing version 1.14
PICK f025c53dba: Remove the fallback to wsl.exe when HKCU\...\Lxss doesn't exist (#13436)
 OK
```

(cherry picked from commit 16028dee8b)
Service-Card-Id: 85487931
Service-Version: 1.15
2022-09-06 15:21:59 -05:00
Carlos Zamora
5dcde95bfb Make Mark Mode keybindings precede custom keybindings (#13659)
This PR moves the key handling for mark mode into a helper method that is then called before an action/key binding is attempted.

Epic: #4993
Closes #13533

- Add custom keybinding to "down" arrow key
- in mark mode --> selection updates appropriately
- out of mark mode --> keybinding executed

Amended by DHowett to remove URL targeting features.

(cherry picked from commit 70313db246)
Service-Card-Id: 84711521
Service-Version: 1.15
2022-09-06 14:43:55 -05:00
Carlos Zamora
ecfe4b5d6a Revert "Remove most uses of CompareInBounds (#13244)" (#13907)
This reverts commit f785168aac (PR #13244)

The error logged to NVDA was caused by the following line of code in `_getTextValue()`:
`THROW_HR_IF(E_FAIL, !bufferSize.IsInBounds(_start) || !bufferSize.IsInBounds(_end));`
NVDA would expand a text range to encompass the document in the alt buffer. This means that the "end" would be set to the dangling "endExclusive" point (x = left, y = one past the end of the buffer). This is a valid range!
However, upon extracting the text, we would hit the code above. The exclusive end doesn't actually point to anything in the buffer, so we would falsly throw `E_FAIL`.

Though this could be fixed by adding a special check for the `endExclusive` in the line above, I suspect there are other places throughout the UIA code that hit this problem too. The safest course of action is to revert this commit entirely since it was a code health commit (it doesn't actually close an issue).

Closes #13866

(cherry picked from commit bfd5248a2e)
Service-Card-Id: 85405833
Service-Version: 1.15
2022-09-06 14:20:57 -05:00
Mike Griese
6027a367b2 An attempted fix for the SignalTextChanged crash (#13876)
This is conjecture - I was totally unable to repro the original crash here.
Based on the stacks in MSFT:39994969, it looks like we try to fire off a
`RaiseAutomationEvent`, which calls through UIA core, eventually to the point of
calling `ComPtr<WUX::Automation::Peers::IAutomationPeer>::{dtor}`. I'm guessing
based on the stacks that the TermControl has already been released and cleaned
up. However, the lambda in the `RunAsync` calls here only takes a ref to the
TCAP. The TCAP has an outstanding reference (maybe on the other side of the UIA
fence), and gets successfully resolved as strong, but when calling to
`RaiseAutomationEvent`, the `owner` we passed in is gonezo.

This explicitly passes a `weak_ref` to `TermControlAutomationPeer`, rather than
a raw ptr, so we can actually check if the control is still alive before _we_
dereference it. If it is, great, we've got a strong ref to it now and it won't
get torn down.

Again, this is hearsay. Without a repro, the only way we can confirm this is
gone is by just hoping the crashes go away. 🤞

* Might close #13357 (we'll reopen if it doesn't?)
* narrator still works

(cherry picked from commit fe0d57071e)
Service-Card-Id: 85401131
Service-Version: 1.15
2022-09-06 14:20:55 -05:00
Leonard Hecker
2f6b546f02 Fix a race condition in UpdatePatternLocations (#13859)
Since locking the console can take a non-trivial amount of time,
the main thread might have already released the ControlCore instance, while the
`UpdatePatternLocations` background thread is holding the last active reference.
If the function call goes out of scope, we destroy the instance, which might
not be safe, considering its members are usually only used by the main thread.
This commit fixes the issue by only holding a reference of the `Terminal`.

## Validation Steps Performed
* Patterns are recognized 

(cherry picked from commit 12122b2bf9)
Service-Card-Id: 85321064
Service-Version: 1.15
2022-09-06 14:20:54 -05:00
Mike Griese
eada4d2c9e Attempt to fix the _refreshSizeUnderLock crash (#13857)
See https://github.com/microsoft/terminal/issues/12176#issuecomment-1199488906, and MSFT:39723014.

I have literally no idea how to repro this one, or debug it. The dump I looked at looked like there was a `SwapChainScaleChanged` that was being dispatched as the app was tearing down. The `ControlCore` had started closing, but the `TermControl` hadn't yet. Apparently, just none of the `_refreshSizeUnderLock` callers checked if we were already closing.

All the callers appear to be on the main thread.

Closes #12176

Since there's no real way for me to repro this manually, I'm thinking we fire this fix off to the OS terminal build, where we'll pretty quickly be able to see if this fixed it or not.

(cherry picked from commit a85d9e69ed)
Service-Card-Id: 85321070
Service-Version: 1.15
2022-09-06 14:20:52 -05:00
Carlos Zamora
4829e9218e [a11y] Make CommandPalette announce selected item (#13519)
## Summary of the Pull Request
The command palette (and tab search by extension) doesn't ever tell screen readers what is selected. Here, we simply hook up the selection changed event to a function that tells the screen reader what is selected. With this, the user no longer has to tab into the list view to know what is selected!

Will resolve the following bug upon validation from a11y team: #12065

## Validation Steps Performed
Performed repro steps from #12065.

NOTE: we do NOT read the selected item when the command palette is first opened. I think that's ok.
(cherry picked from commit deb5e7c650)
Service-Card-Id: 85254984
Service-Version: 1.15
2022-09-06 14:20:51 -05:00
Dustin L. Howett
edef86b654 release yml: name the package correctly, don't duplicate appx (#13852)
I got tired of renaming the packages that came out of our build
pipeline. I also got tired of the fact that every appxbundle artifact we
upload comes with another whole copy of Terminal for every architecture,
plus all their symbols. Those are reflected in other artifacts, so
there's no reason to duplicate them.

(cherry picked from commit 623a59ecaa)
Service-Card-Id: 85252798
Service-Version: 1.15
2022-09-06 14:20:49 -05:00
Carlos Zamora
cd9a114359 [UIA] Prevent erroneous L'\0' padding in GetText(INT_MAX) (#13779)
Apparently, calling `GetText(INT_MAX)` causes a HUGE memory spike for a few seconds each time this is called. The [UIA docs](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nf-uiautomationcore-itextrangeprovider-gettext#parameters) say to put `-1` if no limit is required, but I assume a few people have been hit by this before.

This addresses this issue (and similar ones) in two ways:
1. as we iterate over the lines of text, if we're already past the max length, just break out of the loop
2. _only_ resize if the max length is actually less than the current length. This prevents us padding the string with `L'\0'` erroneously (which is probably what causes the memory spike).

(cherry picked from commit b4ada09776)
Service-Card-Id: 85252534
Service-Version: 1.15
2022-09-06 14:20:47 -05:00
Dustin L. Howett
0b63a33301 Use the viewport-relative cursor pos for CCore.CursorPosition (#13785)
In #13024, we removed `Terminal::GetCursorPosition` from TerminalCore.
This has been widely regarded as a good move.

Now, you might rightly be wondering: why didn't compilation immediately
fail? Well. It turns out that there were _two_ copies of
`GetCursorPosition`. One for `const Terminal`, and one for `Terminal`.
This is important.

`Terminal::GetCursorPosition()` returned the cursor position relative to
the viewport. `Terminal::GetCursorPosition() const`, however, returns
the cursor position in absolute.

We removed the non-`const` one. Fortunately, thanks to the lookup rules
for `const`-qualified members, this didn't matter. Code that called
`GetCursorPosition()` still called `GetCursorPosition()`, and everything
was fine.

Except that part about the relative coordinates. That was not fine.

The TSF control is the _only_ consumer of `ControlCore.CursorPosition`,
and that was the _only_ consumer of relative-`GetCursorPosition()`.

This commit restores equilibrium by introducing a new
`GetViewportRelativeCursorPosition()` member to `Terminal` and switching
over the only consumer of relative cursor position to use it.

Closes #13769.

(cherry picked from commit 2dedc9af7f)
Service-Card-Id: 85131461
Service-Version: 1.15
2022-09-06 14:20:42 -05:00
Mike Griese
a0eaea84f0 Fix a crash on exit with the command palette open (#13778)
Fixes MSFT:38775539
Might also fix MSFT:38614563

Looking at this code should be pretty clear what's going on. On exit, the XAML root is already nulled out. But here, we're just yolo'ing and assuming it exists (why wouldn't it). So yea. This is like weirdly high percent of crashes internally, but that's not from real users. Real users, I suspect hit this as like .3% of our crashes. Not zero, but _low_.

* [x] tested manually

<hr>

May also be related to...
* MSFT:40602905
* MSFT:40602904
* MSFT:40412800
* MSFT:35213459 <---has links

(cherry picked from commit 64bcc0bd25)
Service-Card-Id: 85486788
Service-Version: 1.15
2022-09-06 14:20:40 -05:00
Mike Griese
038ad3b509 Fix a crash in _WritePseudoWindowCallback (#13777)
Fixes MSFT:40853556

There's a small race here. The renderer thread in ConPTY might notice the terminal is gone, call CloseOutput, and release the vt renderer, and then the window proc fires and decides to minimize/restore the window, triggering an A/V.

I'm 100% confident that this has NEVER happened to a real user. But the test labs hit it so much that it makes up ~26% of our crashes.

I haven't tested this cause again, _it doesn't hit in the wild_

(cherry picked from commit f58240c9c0)
Service-Card-Id: 85103518
Service-Version: 1.15
2022-09-06 14:20:39 -05:00
Mike Griese
f2a691863b [1.14] Update the MUX package to 2.7.3 (#13761)
Does two things:

* the first two commits: shakes up the way we reference MUX in our projects so we can actually just
  ```xml
  <PropertyGroup Label="NuGet Dependencies">
    <TerminalMUX>true</TerminalMUX>
  </PropertyGroup>
  ```
  Like every other dependency we have
* the last commit: update to MUX.2.7.3

This is the 1.14 PR, which should be appropriately cherry-picked through to `release-1.15` and `main`

(cherry picked from commit a277b56f6a)
Service-Card-Id: 85036583
Service-Version: 1.15
2022-08-16 16:46:18 -05:00
Dustin Howett
76daf71cf9 Revert "Hide "Open in Terminal" context menu option appropriately (#13206)"
This reverts commit 93b5dff5f8.
2022-08-16 15:47:21 -05:00
Leonard Hecker
22edbc1b37 Fix issues with Japanese & Vietnamese IME (#13678)
This commit builds directly on the changes made in #13677 and fixes:
* TSF resetting to AlphaNumeric ("ASCII") input mode when pressing enter
* Vietnamese IME not composing a new word after pressing whitespace, etc.

Closes #11479
Closes #13398

## Validation Steps Performed
* Japanese IME (Full-Width Katakana)
  Typing "saitama" produces "サイタマ" 
* Korean IME
  Typing "gksrmf" produces "한글" 
* Vietnamese IME
  Typing "xin chaof" produces "xin chào" 
* Emoji Picker (Win+.)
  

(cherry picked from commit ed800dc72d)
Service-Card-Id: 84832470
Service-Version: 1.15
2022-08-16 13:59:13 -05:00
Leonard Hecker
479f625c36 Clean up TSFInputControl a bit (#13677)
While working on #13398 I felt that `TSFInputControl` wasn't up to sniff.
This commit is a minor cleanup of the class:
* default member initializers
* Simplified use of STL classes which already perform boundary checks
* Correctly check text buffer emptiness in `_SendAndClearText`
* Track selection range as mandated by the API

## Validation Steps Performed
* Japanese IME (Full-Width Katakana)
  Typing "saitama" produces "サイタマ" 
* Korean IME
  Typing "gksrmf" produces "한글" 
* Vietnamese IME
  Typing "xin chaof" continues to produce broken "xin xinchaof"
  (It's supposed to produce "xin chào")
* Emoji Picker (Win+.)
  

(cherry picked from commit 2c922e105c)
Service-Card-Id: 85034073
Service-Version: 1.15
2022-08-16 13:59:12 -05:00
Leonard Hecker
f5aa18a389 Call UpdatePatternLocations from a background thread (#13758)
We have a number of theories why #12607 is happening, one of which is that
some GPU drivers somehow rely on Win32 messages or similar which we process
on the main thread. If we then try to acquire the console lock on the main
thread, while the GPU-driver thread itself is holding that lock, we've got
ourselves a deadlock. This PR makes this less likely by running the repeat
offender `UpdatePatternLocations` on a background thread instead.
We have a number of other locations which acquire the console lock on the
main thread and a thorough bug fix must be done in a different way.

## Validation Steps Performed
* After pasting an URL it gets underlined on hover 

(cherry picked from commit 23e4d313d5)
Service-Card-Id: 85033019
Service-Version: 1.15
2022-08-16 13:40:55 -05:00
Dustin L. Howett
6933429721 [1.14/5] Propagate the color scheme resw changes from df671377 (#13712)
Due to the way our localization pipeline works, we cannot delete
resources in main until the resources in question are totally flushed
out of the active-servicing release branches. Unfortunately, in #13179,
we _did_ remove resource keys. Because the Color Schemes page in 1.14
and 1.15 uses the resources directly (rather than by way of x:Uid), it
is easier for us to backport the resource changes now than to
reintroduce the old keys on main.

Closes #13606
2022-08-10 15:13:45 -05:00
Leonard Hecker
7453b159c1 Replace result codes with exceptions in JumpList (#13688)
This commit simplifies `Jumplist::UpdateJumplist` by using exceptions
instead of returning error codes. Otherwise the code is identical to before.

(cherry picked from commit 768d4b59ca)
Service-Card-Id: 84836267
Service-Version: 1.15
2022-08-08 13:21:23 -05:00
Mike Griese
dab0c3a010 Fix conpty not emitting colored spaces on the VERY FIRST frame (#13665)
This bug arose from a "race condition" in the first frame handling of conpty. We'd try to optimize out spaces if we've cleared the entire frame (which always happens on the first frame). However, doing that even for colored spaces meant that things like powerline prompts could be emitted to conhost during the first frame, and we'd optimize the spaces out. That's silly.

This is hard to repro naturally, but this comment has another repro I used
https://github.com/microsoft/terminal/issues/8341#issuecomment-731310022

Modified to facilitate simpler testing, it looks like:

#### Before
![image](https://user-images.githubusercontent.com/18356694/182680119-bb22179c-a328-43f3-b64a-0d1d5773b813.png)

#### After
![image](https://user-images.githubusercontent.com/18356694/182680159-805964c5-c4cc-411a-8865-3866fca8d6e9.png)

* [x] Closes #8341
* [x] Tests added

Co-authored by @DHowett

(cherry picked from commit 210a98e449)
Service-Card-Id: 84836597
Service-Version: 1.15
2022-08-08 13:21:22 -05:00
PankajBhojwani
8b9504b536 Use only one tab color picker for all tabs, delay load it (#13674)
- We only ever have 1 color picker now, instead of each tab having its own
- `TerminalPage` constructs this color picker (upon first request for it)
- `TerminalPage` attaches the color picker to the tab that requested for it
- `TerminalTab` detaches the color picker when it is done with it, so that `TerminalPage` can attach it to another tab later on

User-end behaviour is the same

(cherry picked from commit ba08dd2174)
2022-08-08 13:21:09 -05:00
PankajBhojwani
c4e60ad5d6 Fix _isDefTermSession not propagating upon pane split/close (#13649)
## Summary of the Pull Request
In #13560 we added a member to `Pane` that lets it know if it was spawned as a default terminal session, but did not propagate that value when the pane gets split or when the pane closes. This commit fixes that.

## Validation Steps Performed
A session spawned by a def term invocation remembers it even as it goes through splits

(cherry picked from commit 1a7783449c)
Service-Card-Id: 84836635
Service-Version: 1.15
2022-08-08 13:16:36 -05:00
Mike Griese
fc50edd5b4 Hopefully fix the HandleCommandlineArgs crashes (#13604)
This is an experiment, as discussed in https://github.com/microsoft/terminal/issues/11790#issuecomment-1179143049. We don't know what for sure causes these crashes, but it seems that blindly throwing, so that it gets picked up by Watson, is probably not the move. Instead, we're just gonna do our fallback, REGARDLESS of what the exception was.

See #11790, MSFT:38542548, MSFT:38572983, MSFT:38542574 et. al.

(cherry picked from commit 5c35a64bb3)
Service-Card-Id: 84836297
Service-Version: 1.15
2022-08-08 13:16:34 -05:00
Mike Griese
531901505c Manually quit when the OS tells us to update (#13614)
Refer to https://docs.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications

The OS will send us a WM_QUERYENDSESSION when it's preparing an
update for our app. It will then send us a WM_ENDSESSION, which gives
us a small timeout (~30s) to actually shut down gracefully. After
that timeout, it will send us a WM_CLOSE. If we still don't close
after the WM_CLOSE, it'll force-kill us (causing a crash which will be
bucketed to moapphang).

We will manually start a quit, so that we can persist the state. If we refuse to
gracefully shut down, the OS will crash us to focefully terminate us. We
choose to quit here, rather than just close, to skip over any warning dialogs
(e.g. "Are you sure you want to close all tabs?") which might prevent a WM_CLOSE
from cleanly closing the window.

This will cause a appHost._RequestQuitAll, which will notify the
monarch to collect up all the window state and save it.

This "crash" caused by the OS force killing us constitutes 80% of all our crashes. 80%. See MSFT:38947155, MSFT:38877540, MSFT:21058878, MSFT:31710054, MSFT:39764652, MSFT:26883776.

Closes #13569

It also fixes the issue where if you've got Terminal Dev running (outside VS), and you try to Deploy, you have to make sure to close the "Are you sure you want to close all tabs" dialog before the deployment can proceed. A deploy in VS sends the same sequence of messages as a real update.

(cherry picked from commit b3604ba0eb)
Service-Card-Id: 84836638
Service-Version: 1.15
2022-08-08 13:16:33 -05:00
PankajBhojwani
b68de3e5c5 [DxD] Add 'Automatic' as a mode for CloseOnExit (#13560)
## Summary of the Pull Request
Adds a new mode to `CloseOnExit`: `Automatic`. In this mode, if a process handed off by defterm terminates for whatever reason, we always close (i.e. we treat the mode as `Always`), but for processes launched by Terminal we terminate as with the `Graceful` behaviour.

## PR Checklist
* [x] Closes #13325
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Detailed Description of the Pull Request / Additional comments

- Adds a new enum value to `CloseOnExit`
- Adds a new function to `Pane`: `FinalizeConfigurationGivenDefault`: this is a function that should be called when the pane is created via default terminal handoff, and can contain any special configurations we should set given that the pane was created via handoff

## Validation Steps Performed

(cherry picked from commit 89d57e827e)
Service-Card-Id: 84836029
Service-Version: 1.15
2022-08-08 11:55:48 -05:00
Mike Griese
fff3372ed2 Fix the ConPTY extended attributes optimization (#13661)
... which should have never worked in the first place

Quick filing a PR for review. This is the bulk of the actual code changes. Figured it was best to review the conpty changes sooner than later and I can add tests in the morning.

Test cases:
```
printf "\e[7m         test         \e[m\n"
```

```
printf "\e[7m"; printf ' %.0s' $(seq 1 $COLUMNS); printf "\e[m\n"
```

After:
![image](https://user-images.githubusercontent.com/18356694/182478185-6e65ab99-5c27-4772-af3b-2baa22387ec1.png)

Closes #13229

Definitely fixes:
* [x] #13643
* [x] https://github.com/PowerShell/PowerShell/issues/17812

(cherry picked from commit ffe9a0f09b)
Service-Card-Id: 84736231
Service-Version: 1.15
2022-08-08 11:46:34 -05:00
Leonard Hecker
f3f9eba5c9 Fix input corruption for high code points (#13667)
We must use 65535 as `MAX_PARAMETER_VALUE` in order for us to properly parse
win32-input-mode sequences, which transmit UTF-16 characters as parameters.

Closes #12977

## Validation Steps Performed
* Call `SendInput` with 🙁 (`L'\xD83D'`, `L'\xDE41'`)
* 🙁 appears on the input line 

(cherry picked from commit 74cdffe921)
Service-Card-Id: 84772549
Service-Version: 1.15
2022-08-08 11:46:32 -05:00
Mike Griese
b670800460 Restore the ability for alt+tab to restore the Terminal after minimizing with taskbar (#13624)
Curiously, at least on Windows 10 (and rarely on Windows 11), if you minimize the Terminal by clicking on the taskbar, then alt-tab to try and restore the window, the Taskbar will decide to call `SwitchToWindow` on the invisible, owned ConPTY window instead of the main window. When that happens, ConPTY'll get a `WM_SIZE(SIZE_RESTORED, lParam=0)`. The main window will NOT get a `SwitchToWindow` called. If ConPTY doesn't actually inform the hosting process about this, then the main HWND might stay hidden.

* Refer to #13158 where we disabled this.
* Closes #13589
* Closes #13248
* Tested manually on a Windows 10 VM.
* Confirmed that opening tabs while maximized/snapped doesn't restore down.
* `[Native]::ShowWindow([Native]::GetConsoleWindow(), 6)` still works

(cherry picked from commit d1fc11248c)
Service-Card-Id: 84673887
Service-Version: 1.15
2022-08-08 11:46:30 -05:00
Dustin L. Howett
210145a709 Lock the app names in the pseudoloc locales (#13563)
On occasion, when we submit to the store we get a package rejection
because the app name has changed for the `qps-*` locales. Instead of
constantly reserving new pseudolocalized app names every time the
pseudolocalization seed changes, we should just lock our app name so
that it does not get pseudolocalized.

(cherry picked from commit bb40efc00b)
Service-Card-Id: 84417902
Service-Version: 1.15
2022-08-08 11:46:29 -05:00
Carlos Zamora
961925b04f [1.15] Scroll to sln marker for Mark Mode & SelectAll (#13660)
Adds `ScrollToPoint()` from #13405 to be able to scroll to the selection marker when (1) mark mode is entered and (2) `selectAll` is called.

This change is a combination of #13656 and a minor part of #13405.
Epic: #4993
2022-08-03 11:01:02 -07:00
James Holderness
b8847a3407 Reimplement DECPS using DirectSound in place of MIDI (#13471)
## Summary of the Pull Request

The original `DECPS` implementation made use of the Windows MIDI APIs to
generate the sound, but that required a 3MB package dependency for the
GS wavetable DLS. This PR reimplements the `MidiAudio` class using
`DirectSound`, so we can avoid that dependency.

## References

The original `DECPS` implementation was added in PR #13208, but was
hidden behind a velocity flag in #13258.

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

## Detailed Description of the Pull Request / Additional comments

The way it works is by creating a sound buffer with a single triangle
wave that is played in a loop. We generate different notes simply by
adjusting the frequency at which that buffer is played.

When we need a note to end, we just set the volume to its minimum value
rather than stopping the buffer. If we don't do that, the repeated
starting and stopping tends to produce a lot of static in the output. We
also use two buffers, which we alternate between notes, as another way
to reduce that static.

One other thing worth mentioning is the handling of the buffer position.
At the end of each note we save the current position, and then use an
offset from that position when starting the following note. This helps
produce a clearer separation between tones when repeating sequences of
the same note.

In an ideal world, we should really have something like an attack-decay-
sustain-release envelope for each note, but the above hack seems to work
reasonably well, and keeps the implementation simple.

## Validation Steps Performed

I've manually tested both conhost and Terminal with the sample tunes
listed in issue #8687, as well as a couple of games that I have which
make use of `DECPS` sound effects.

(cherry picked from commit bc79867b38)
Service-Card-Id: 84270205
Service-Version: 1.15
2022-07-19 14:24:36 -05:00
Mike Griese
a4a12ef190 Send focus events even in ReadOnly mode (#13483)
Does what it says on the tin. When we get focused, temporarily turn off readonly mode, as to not pop the dialog when the focus sequence is eventually sent to the connection.

* closes #13461

(cherry picked from commit b4a52c847f)
Service-Card-Id: 84111371
Service-Version: 1.15
2022-07-19 14:24:34 -05:00
James Holderness
91a60caf41 Add line breaks in the debug tap output (#13475)
## Summary of the Pull Request

When the debug tap converts control characters into visible glyphs, it
ends up losing the structure of the output, and that can sometimes make
things difficult to read. This PR attempts to alleviate that problem by
reinjecting an actual line break in the debug stream whenever an `LF`
control is received.

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

## Validation Steps Performed

I've tested the updated debug tab with a number of different shells, and
also a couple of different apps. When there aren't many linefeeds in the
output, it's obviously not going to make much of a difference, but when
there are, I think it definitely improves the readability.

(cherry picked from commit 04478d1df0)
Service-Card-Id: 84116395
Service-Version: 1.15
2022-07-19 14:24:32 -05:00
Davide Giacometti
88b527f606 Don't set full screen to quake window (#13473)
If launch mode is set to full screen quake window is opened in full
screen.

## Validation Steps Performed
- Set startup > launch mode > full screen
- Launch quake window
- Quake window shouldn't be opened in full screen

Closes #12894

(cherry picked from commit 589286a357)
Service-Card-Id: 84116410
Service-Version: 1.15
2022-07-19 14:24:31 -05:00
Mike Griese
976cddbe7c Wrap the tooltips on the new tab button (#13463)
In non-en-us locales, these tooltips can get really long and get clipped.

Closes MSFT:39603031
See Also #9913

(cherry picked from commit 32379c29f0)
Service-Card-Id: 84008282
Service-Version: 1.15
2022-07-19 14:24:29 -05:00
James Holderness
93c08bd459 Allow leading spaces to bypass console aliases (#13476)
## Summary of the Pull Request

When you create a console alias that overrides an existing command, it
should still be possible to execute the original command by prefixing it
with a space. However, at some point in the past, there was an attempt
to improve the usability by trimming leading spaces, and that ended up
breaking this functionality. This PR reverts that change, so leading
spaces can once again be used to bypass an alias.

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

## Validation Steps Performed

I've updated the existing alias unit test for leading spaces to match
the new behavior, i.e. it now confirms that a command with leading
spaces will not match the alias.

I've also manually confirmed that the `doskey` test case reported in
issue #4189 is now working as expected.

(cherry picked from commit cd2166aedf)
Service-Card-Id: 84072860
Service-Version: 1.15
2022-07-19 14:24:28 -05:00
Mike Griese
ba8171e0a4 Also hide the scroll marks when hiding the scrollbar (#13454)
This one's pretty obvious. I added another scrollbar sized grid to the terminal,
but I forgot to collapse it when the user requests `"scrollbarState": "hidden"`.

* [x] Closes #13446
* [x] I work here

(cherry picked from commit aa4e9f5414)
Service-Card-Id: 83969639
Service-Version: 1.15
2022-07-19 14:24:26 -05:00
dansmor7
83e7e14978 Update tab hover colors (#13434)
The hover tab color used to be generated from the selected tab color, which would end up lighter or darker, and white-gray colors would end up pink.
It is now simply the selected tab color with 60% opacity. This is also how brushes are created for accent buttons and color buttons (although with different opacity levels).

(cherry picked from commit c6b67aad4b)
Service-Card-Id: 83894208
Service-Version: 1.15
2022-07-05 17:06:29 -05:00
Dustin L. Howett
74e3985ba8 Remove the fallback to wsl.exe when HKCU\...\Lxss doesn't exist (#13436)
The main result of this fallback is that we attempt to launch wsl.exe
when the user hasn't installed or interacted with WSL. On our test
machines, that results in the creation of a wsl.exe process that tells
us precisely nothing; on WDAC managed machines it results in an Event
Log entry about spawning another (possibly blocked) process.

The registry is more reliable, and if the "API" it provides changes we
can just rev terminal.

Closes #11716

(cherry picked from commit f025c53dba)
Service-Card-Id: 83892844
Service-Version: 1.15
2022-07-05 17:06:28 -05:00
Carlos Zamora
9e05dcd4f3 Update renderer on SwitchSelectionEndpoint (#13435)
## Summary of the Pull Request
In #13370, we should be notifying the renderer that the selection changed. Minor oversight and simple fix.

## References
#4993
#13370
Closes #13413

(cherry picked from commit 66ecb0bd63)
Service-Card-Id: 83892665
Service-Version: 1.15
2022-07-05 15:38:37 -05:00
Leonard Hecker
81f2d3a834 Fix a race condition in CTerminalHandoff::s_StopListening (#13410)
This commit fixes a minor race condition covered as part of #13368.
The member `_pfnHandoff` was read without the mutex `_mtx` being locked first.
The issue was solved by acquiring the lock early and running the entire
`s_StopListening` function with that lock held.

(cherry picked from commit 95a19624a4)
Service-Card-Id: 83893126
Service-Version: 1.15
2022-07-05 15:38:14 -05:00
Mike Griese
0080b81149 Fix the bellsound schema, for real (#13433)
As noted in https://github.com/microsoft/terminal/pull/11511#issuecomment-1173541384.

Missed in #13035

(cherry picked from commit 86dfefa690)
Service-Card-Id: 83891743
Service-Version: 1.15
2022-07-05 15:38:13 -05:00
PankajBhojwani
2fdcd4e151 Update schema with scroll marks actions and settings (#13414)
Update schema with the settings/actions added in #12948

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

(cherry picked from commit 478c2c3613)
Service-Card-Id: 83790285
Service-Version: 1.15
2022-07-05 15:38:12 -05:00
146 changed files with 2443 additions and 1864 deletions

View File

@@ -14,6 +14,7 @@ BYCOMMAND
BYPOSITION
charconv
CLASSNOTAVAILABLE
CLOSEAPP
cmdletbinding
COLORPROPERTY
colspan
@@ -28,9 +29,11 @@ dataobject
dcomp
DERR
dlldata
DNE
DONTADDTORECENT
DWORDLONG
endfor
ENDSESSION
enumset
environstrings
EXPCMDFLAGS
@@ -141,6 +144,7 @@ PEXPLICIT
PICKFOLDERS
pmr
ptstr
QUERYENDSESSION
rcx
REGCLS
RETURNCMD

View File

@@ -233,6 +233,7 @@ CFuzz
cgscrn
chafa
changelist
chaof
charinfo
charlespetzold
charset
@@ -438,7 +439,9 @@ ctlseqs
Ctlv
ctor
CTRLEVENT
CTRLFREQUENCY
CTRLKEYSHORTCUTS
CTRLVOLUME
Ctx
Ctxt
ctype
@@ -661,7 +664,14 @@ dropdown
DROPDOWNLIST
DROPFILES
drv
DSBCAPS
DSBLOCK
DSBPLAY
DSBUFFERDESC
DSBVOLUME
dsm
dsound
DSSCL
dst
DSwap
DTest
@@ -682,6 +692,7 @@ dwrite
dwriteglyphrundescriptionclustermap
dxgi
dxgidwm
dxguid
dxinterop
dxp
dxsm
@@ -718,6 +729,7 @@ endptr
endregion
ENQ
enqueuing
ENTIREBUFFER
entrypoint
ENU
enum
@@ -934,6 +946,7 @@ gitfilters
github
gitlab
gle
GLOBALFOCUS
globals
GLYPHENTRY
gmail
@@ -1369,6 +1382,7 @@ lpv
LPVOID
LPW
LPWCH
lpwfx
LPWINDOWPOS
lpwpos
lpwstr
@@ -2187,8 +2201,6 @@ SETTITLE
setw
Setwindow
SETWINDOWINFO
SFGAO
SFGAOF
sfi
SFINAE
SFUI
@@ -2678,6 +2690,7 @@ WANTARROWS
WANTTAB
wapproj
wav
WAVEFORMATEX
wbuilder
wch
wchar
@@ -2878,6 +2891,9 @@ xff
xfg
XFile
XFORM
xin
xinchaof
xinxinchaof
XManifest
XMath
XMFLOAT

View File

@@ -11,9 +11,9 @@ Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "CascadiaPackage", "src\casc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.EXE", "src\host\exe\Host.EXE.vcxproj", "{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}"
ProjectSection(ProjectDependencies) = postProject
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
{5D23E8E1-3C64-4CC1-A8F7-6861677F7239} = {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PropertiesLibrary", "src\propslib\propslib.vcxproj", "{345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}"
@@ -64,9 +64,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererGdi", "src\renderer
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host", "src\host\lib\hostlib.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954746}"
ProjectSection(ProjectDependencies) = postProject
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.unittest", "src\host\ut_lib\host.unittest.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954747}"
@@ -77,8 +77,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.unittest", "src\host\u
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Unit", "src\host\ut_host\Host.UnitTests.vcxproj", "{531C23E7-4B76-4C08-8AAD-04164CB628C9}"
ProjectSection(ProjectDependencies) = postProject
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
{06EC74CB-9A12-429C-B551-8562EC954747} = {06EC74CB-9A12-429C-B551-8562EC954747}
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {0CF235BD-2DA0-407E-90EE-C467E8BBC714}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextBuffer.Unit.Tests", "src\buffer\out\ut_textbuffer\TextBuffer.Unit.Tests.vcxproj", "{531C23E7-4B76-4C08-8BBD-04164CB628C9}"
@@ -89,9 +89,9 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Feature", "src\host\ft_host\Host.FeatureTests.vcxproj", "{8CDB8850-7484-4EC7-B45B-181F85B2EE54}"
ProjectSection(ProjectDependencies) = postProject
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
{FC802440-AD6A-4919-8F2C-7701F2B38D79} = {FC802440-AD6A-4919-8F2C-7701F2B38D79}
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
{FC802440-AD6A-4919-8F2C-7701F2B38D79} = {FC802440-AD6A-4919-8F2C-7701F2B38D79}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.UnitTests", "src\terminal\parser\ut_parser\Parser.UnitTests.vcxproj", "{12144E07-FE63-4D33-9231-748B8D8C3792}"
@@ -114,8 +114,8 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Host.Tests.UIA", "src\host\ft_uia\Host.Tests.UIA.csproj", "{C17E1BF3-9D34-4779-9458-A8EF98CC5662}"
ProjectSection(ProjectDependencies) = postProject
{099193A0-1E43-4BBC-BA7F-7B351E1342DF} = {099193A0-1E43-4BBC-BA7F-7B351E1342DF}
{C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB} = {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
{C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB} = {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VTApp", "src\tools\vtapp\VTApp.csproj", "{099193A0-1E43-4BBC-BA7F-7B351E1342DF}"
@@ -172,12 +172,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalCore", "src\cascadi
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.Lib", "src\cascadia\TerminalControl\TerminalControlLib.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB}
{48D21369-3D7B-4431-9967-24E81292CF62} = {48D21369-3D7B-4431-9967-24E81292CF62}
{48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63}
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {8222900C-8B6C-452A-91AC-BE95DB04B95F}
{AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control", "src\cascadia\TerminalControl\dll\TerminalControl.vcxproj", "{CA5CAD1A-F542-4635-A069-7CAEFB930070}"
@@ -187,21 +188,21 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminalShellExt", "src\cascadia\ShellExtension\WindowsTerminalShellExt.vcxproj", "{F2ED628A-DB22-446F-A081-4CC845B51A2B}"
@@ -251,15 +252,15 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
EndProjectSection
EndProject
@@ -273,9 +274,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.DLL", "src\wincon
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia\LocalTests_TerminalApp\TestHostApp\TestHostApp.vcxproj", "{A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42}
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}"
@@ -330,8 +331,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "src\dep\fmt\fmt.vcxp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "src\cascadia\WpfTerminalTestNetCore\WpfTerminalTestNetCore.csproj", "{1588FD7C-241E-4E7D-9113-43735F3E6BAD}"
ProjectSection(ProjectDependencies) = postProject
{A22EC5F6-7851-4B88-AC52-47249D437A52} = {A22EC5F6-7851-4B88-AC52-47249D437A52}
{84848BFA-931D-42CE-9ADF-01EE54DE7890} = {84848BFA-931D-42CE-9ADF-01EE54DE7890}
{A22EC5F6-7851-4B88-AC52-47249D437A52} = {A22EC5F6-7851-4B88-AC52-47249D437A52}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
@@ -356,9 +357,9 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", "src\cascadia\LocalTests_SettingsModel\SettingsModel.LocalTests.vcxproj", "{CA5CAD1A-9B68-456A-B13E-C8218070DC42}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
{CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MonarchPeasantSample", "src\tools\MonarchPeasantSample\MonarchPeasantSample.vcxproj", "{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}"
@@ -377,8 +378,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\cascadia\UnitTests_Remoting\Remoting.UnitTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}"
ProjectSection(ProjectDependencies) = postProject
{43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26}
{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}
{43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}"
@@ -644,7 +645,8 @@ Global
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.Build.0 = Debug|x64
@@ -662,7 +664,8 @@ Global
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.ActiveCfg = Release|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|ARM64.Build.0 = Release|ARM64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x64Test.Build.0 = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.ActiveCfg = Release|x64
{DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x64.Build.0 = Release|x64
@@ -2309,7 +2312,8 @@ Global
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.ActiveCfg = Debug|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.Build.0 = Debug|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.ActiveCfg = Debug|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.Build.0 = Debug|x64
@@ -2326,7 +2330,8 @@ Global
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.ActiveCfg = Release|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.Build.0 = Release|ARM64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x64Test.Build.0 = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.ActiveCfg = Release|x64
{48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.Build.0 = Release|x64
@@ -3357,7 +3362,8 @@ Global
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.Build.0 = Debug|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.Build.0 = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.ActiveCfg = Debug|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.Build.0 = Debug|x64
@@ -3377,7 +3383,8 @@ Global
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.ActiveCfg = Release|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.Build.0 = Release|ARM64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.Build.0 = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.ActiveCfg = Release|x64
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.Build.0 = Release|x64

View File

@@ -4,7 +4,7 @@
"collection": "microsoft",
"project": "OS",
"repo": "os.2020",
"name": "official/rs_wdx_dxp_windev",
"name": "official/rs_we_adept_e4d2",
"workitem": "38106206",
"CheckinFiles": [
{
@@ -21,4 +21,4 @@
"sendOnErrorOnly": "False"
}
]
}
}

View File

@@ -4,5 +4,7 @@
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
<!-- This cannot be included in another project that depends on XAML (as it would be a duplicate package ID) -->
<package id="Microsoft.UI.Xaml" version="2.7.0" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />
</packages>

View File

@@ -9,7 +9,7 @@
<PropertyGroup>
<!-- Optional, defaults to main. Name of the branch which will be used for calculating branch point. -->
<PGOBranch>main</PGOBranch>
<PGOBranch>release-1.15</PGOBranch>
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>

View File

@@ -134,8 +134,6 @@ jobs:
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
- template: .\templates\restore-nuget-steps.yml
# Pull the Windows SDK for the developer tools like the debuggers so we can index sources later
- template: .\templates\install-winsdk-steps.yml
- task: UniversalPackages@0
displayName: Download terminal-internal Universal Package
inputs:
@@ -241,6 +239,7 @@ jobs:
filePath: build\scripts\Index-Pdbs.ps1
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
errorActionPreference: silentlyContinue
pwsh: true
- task: PowerShell@2
displayName: Run Unit Tests
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
@@ -336,6 +335,13 @@ jobs:
${{ windowsVersion }}:
TerminalTargetWindowsVersion: ${{ windowsVersion }}
displayName: Create and sign AppX/MSIX bundles
variables:
${{ if eq(parameters.branding, 'Release') }}:
BundleStemName: Microsoft.WindowsTerminal
${{ elseif eq(parameters.branding, 'Preview') }}:
BundleStemName: Microsoft.WindowsTerminalPreview
${{ else }}:
BundleStemName: WindowsTerminalDev
dependsOn: Build
steps:
- checkout: self
@@ -360,14 +366,15 @@ jobs:
$Components = "$(XES_APPXMANIFESTVERSION)" -Split "\."
$Components[0] = ([int]$Components[0] + $VersionEpoch)
$BundleVersion = $Components -Join "."
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
New-Item -Type Directory "$(System.ArtifactsDirectory)\bundle"
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
displayName: Create WindowsTerminal*.msixbundle
- task: EsrpCodeSigning@1
displayName: Submit *.msixbundle to ESRP for code signing
inputs:
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
FolderPath: $(System.ArtifactsDirectory)
Pattern: Microsoft.WindowsTerminal*.msixbundle
FolderPath: $(System.ArtifactsDirectory)\bundle
Pattern: $(BundleStemName)*.msixbundle
UseMinimatch: true
signConfigType: inlineSignParams
inlineOperation: >-
@@ -400,7 +407,7 @@ jobs:
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: appxbundle-signed'
inputs:
PathtoPublish: $(System.ArtifactsDirectory)
PathtoPublish: $(System.ArtifactsDirectory)\bundle
ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion)
- ${{ if eq(parameters.buildConPTY, true) }}:
@@ -612,6 +619,8 @@ jobs:
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
- template: .\templates\restore-nuget-steps.yml
# Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
- ${{ each platform in parameters.buildPlatforms }}:
- ${{ each windowsVersion in parameters.buildWindowsVersions }}:
@@ -634,13 +643,12 @@ jobs:
}
displayName: Extract symbols for public consumption
# Pull the Windows SDK for the developer tools like the debuggers so we can index sources later
- template: .\templates\install-winsdk-steps.yml
- task: PowerShell@2
displayName: Source Index PDBs (the public ones)
inputs:
filePath: build\scripts\Index-Pdbs.ps1
arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
pwsh: true
# Publish the app symbols to the public MSDL symbol server
# accessible via https://msdl.microsoft.com/download/symbols
@@ -706,6 +714,7 @@ jobs:
description: VPack for the Windows Terminal Application
pushPkgName: WindowsTerminal.app
owner: conhost
githubToken: $(GitHubTokenForVpackProvenance)
- task: PublishPipelineArtifact@1
displayName: 'Copy VPack Manifest to Drop'
inputs:

View File

@@ -1,9 +0,0 @@
parameters:
sdkVersion: 18362
steps:
- task: powershell@2
inputs:
targetType: filePath
filePath: build\scripts\Install-WindowsSdkISO.ps1
arguments: ${{ parameters.sdkVersion }}
displayName: 'Install Windows SDK (${{ parameters.sdkVersion }})'

View File

@@ -8,10 +8,11 @@ Param(
[switch]$recursive
)
$debuggerPath = (Get-ItemProperty -path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" -name WindowsDebuggersRoot10).WindowsDebuggersRoot10
$srcsrvPath = Join-Path $debuggerPath "x64\srcsrv"
$srctoolExe = Join-Path $srcsrvPath "srctool.exe"
$pdbstrExe = Join-Path $srcsrvPath "pdbstr.exe"
$pdbStrPackage = ([xml](Get-Content "$SourceRoot\build\packages.config")).packages.package | Where-Object id -like "*PdbStr*"
# This assumes that we rev PdbStr and SrcTool at the same time.
$debugPackageVersions = $pdbStrPackage.version
$srctoolExe = Join-Path $SourceRoot "packages" "Microsoft.Debugging.Tools.SrcTool.$debugPackageVersions" "content" "amd64" "srctool.exe"
$pdbstrExe = Join-Path $SourceRoot "packages" "Microsoft.Debugging.Tools.PdbStr.$debugPackageVersions" "content" "amd64" "pdbstr.exe"
$fileTable = @{}
foreach ($gitFile in & git ls-files)

View File

@@ -1,346 +0,0 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true, Position=0)]
[string]$buildNumber)
# Ensure the error action preference is set to the default for PowerShell3, 'Stop'
$ErrorActionPreference = 'Stop'
# Constants
$WindowsSDKOptions = @("OptionId.UWPCpp", "OptionId.DesktopCPPx64", "OptionId.DesktopCPPx86", "OptionId.DesktopCPPARM64", "OptionId.DesktopCPPARM", "OptionId.WindowsDesktopDebuggers")
$WindowsSDKRegPath = "HKLM:\Software\WOW6432Node\Microsoft\Windows Kits\Installed Roots"
$WindowsSDKRegRootKey = "KitsRoot10"
$WindowsSDKVersion = "10.0.$buildNumber.0"
$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options"
$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification"
$PublicKeyTokens = @("31bf3856ad364e35")
if ($buildNumber -notmatch "^\d{5,}$")
{
Write-Host "ERROR: '$buildNumber' doesn't look like a windows build number"
Write-Host
Exit 1
}
function Download-File
{
param ([string] $outDir,
[string] $downloadUrl,
[string] $downloadName)
$downloadPath = Join-Path $outDir "$downloadName.download"
$downloadDest = Join-Path $outDir $downloadName
$downloadDestTemp = Join-Path $outDir "$downloadName.tmp"
Write-Host -NoNewline "Downloading $downloadName..."
$retries = 10
$downloaded = $false
while (-not $downloaded)
{
try
{
$webclient = new-object System.Net.WebClient
$webclient.DownloadFile($downloadUrl, $downloadPath)
$downloaded = $true
}
catch [System.Net.WebException]
{
Write-Host
Write-Warning "Failed to fetch updated file from $downloadUrl : $($error[0])"
if (!(Test-Path $downloadDest))
{
if ($retries -gt 0)
{
Write-Host "$retries retries left, trying download again"
$retries--
start-sleep -Seconds 10
}
else
{
throw "$downloadName was not found at $downloadDest"
}
}
else
{
Write-Warning "$downloadName may be out of date"
}
}
}
Unblock-File $downloadPath
$downloadDestTemp = $downloadPath;
# Delete and rename to final dest
Write-Host "testing $downloadDest"
if (Test-Path $downloadDest)
{
Write-Host "Deleting: $downloadDest"
Remove-Item $downloadDest -Force
}
Move-Item -Force $downloadDestTemp $downloadDest
Write-Host "Done"
return $downloadDest
}
function Get-ISODriveLetter
{
param ([string] $isoPath)
$diskImage = Get-DiskImage -ImagePath $isoPath
if ($diskImage)
{
$volume = Get-Volume -DiskImage $diskImage
if ($volume)
{
$driveLetter = $volume.DriveLetter
if ($driveLetter)
{
$driveLetter += ":"
return $driveLetter
}
}
}
return $null
}
function Mount-ISO
{
param ([string] $isoPath)
# Check if image is already mounted
$isoDrive = Get-ISODriveLetter $isoPath
if (!$isoDrive)
{
Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null
}
$isoDrive = Get-ISODriveLetter $isoPath
Write-Verbose "$isoPath mounted to ${isoDrive}:"
}
function Dismount-ISO
{
param ([string] $isoPath)
$isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter
if ($isoDrive)
{
Write-Verbose "$isoPath dismounted"
Dismount-DiskImage -ImagePath $isoPath | Out-Null
}
}
function Disable-StrongName
{
param ([string] $publicKeyToken = "*")
reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
{
reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
}
}
function Test-Admin
{
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal $identity
$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Test-RegistryPathAndValue
{
param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $path,
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $value)
try
{
if (Test-Path $path)
{
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
return $true
}
}
catch
{
}
return $false
}
function Test-InstallWindowsSDK
{
$retval = $true
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
{
# A Windows SDK is installed
# Is an SDK of our version installed with the options we need?
$allRequiredSdkOptionsInstalled = $true
foreach($sdkOption in $WindowsSDKOptions)
{
if (!(Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value $sdkOption))
{
$allRequiredSdkOptionsInstalled = $false
}
}
if($allRequiredSdkOptionsInstalled)
{
# It appears we have what we need. Double check the disk
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
if ($sdkRoot)
{
if (Test-Path $sdkRoot)
{
$refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
if (Test-Path $refPath)
{
$umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
if (Test-Path $umdPath)
{
# Pretty sure we have what we need
$retval = $false
}
}
}
}
}
}
return $retval
}
function Test-InstallStrongNameHijack
{
foreach($publicKeyToken in $PublicKeyTokens)
{
$key = "$StrongNameRegPath\*,$publicKeyToken"
if (!(Test-Path $key))
{
return $true
}
}
return $false
}
Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..."
$InstallWindowsSDK = Test-InstallWindowsSDK
if ($InstallWindowsSDK)
{
Write-Host "Installation required"
}
else
{
Write-Host "INSTALLED"
}
$StrongNameHijack = Test-InstallStrongNameHijack
Write-Host -NoNewline "Checking if StrongName bypass required..."
if ($StrongNameHijack)
{
Write-Host "REQUIRED"
}
else
{
Write-Host "Done"
}
if ($StrongNameHijack -or $InstallWindowsSDK)
{
if (!(Test-Admin))
{
Write-Host
throw "ERROR: Elevation required"
}
}
if ($InstallWindowsSDK)
{
# Static(ish) link for Windows SDK
# Note: there is a delay from Windows SDK announcements to availability via the static link
$uri = "https://software-download.microsoft.com/download/sg/Windows_InsiderPreview_SDK_en-us_$($buildNumber)_1.iso";
if ($env:TEMP -eq $null)
{
$env:TEMP = Join-Path $env:SystemDrive 'temp'
}
$winsdkTempDir = Join-Path (Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName())) "WindowsSDK"
if (![System.IO.Directory]::Exists($winsdkTempDir))
{
[void][System.IO.Directory]::CreateDirectory($winsdkTempDir)
}
$file = "winsdk_$buildNumber.iso"
Write-Verbose "Getting WinSDK from $uri"
$downloadFile = Download-File $winsdkTempDir $uri $file
Write-Verbose "File is at $downloadFile"
$downloadFileItem = Get-Item $downloadFile
# Check to make sure the file is at least 10 MB.
if ($downloadFileItem.Length -lt 10*1024*1024)
{
Write-Host
Write-Host "ERROR: Downloaded file doesn't look large enough to be an ISO. The requested version may not be on microsoft.com yet."
Write-Host
Exit 1
}
# TODO Check if zip, exe, iso, etc.
try
{
Write-Host -NoNewline "Mounting ISO $file..."
Mount-ISO $downloadFile
Write-Host "Done"
$isoDrive = Get-ISODriveLetter $downloadFile
if (Test-Path $isoDrive)
{
Write-Host -NoNewLine "Installing WinSDK..."
$setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe"
Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q"
Write-Host "Done"
}
else
{
throw "Could not find mounted ISO at ${isoDrive}"
}
}
finally
{
Write-Host -NoNewline "Dismounting ISO $file..."
Dismount-ISO $downloadFile
Write-Host "Done"
}
}
if ($StrongNameHijack)
{
Write-Host -NoNewline "Disabling StrongName for Windows SDK..."
foreach($key in $PublicKeyTokens)
{
Disable-StrongName $key
}
Write-Host "Done"
}

View File

@@ -18,14 +18,14 @@
This version should be tracked in all project packages.config files for projects that depend on Xaml.
-->
<TerminalMUXVersion>2.7.2-prerelease.220406002</TerminalMUXVersion>
<TerminalMUXVersion>2.7.3-prerelease.220816001</TerminalMUXVersion>
<!--
For the Windows 11-specific build, we're targeting the public version of Microsoft.UI.Xaml.
This version emits a package dependency instead of embedding the dependency in our own package.
This version should be tracked in build/packages.config.
-->
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.1</TerminalMUXVersion>
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.3</TerminalMUXVersion>
</PropertyGroup>
</Project>

View File

@@ -1,13 +1,15 @@
{"Registrations":[
{
"$schema": "https://json.schemastore.org/component-detection-manifest.json",
"Registrations": [
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/CLIUtils/CLI11",
"commitHash": "dd0d8e4fe729e5b1110232c7a5c9566dad884686"
}
}
"type": "git",
"git": {
"repositoryUrl": "https://github.com/CLIUtils/CLI11",
"commitHash": "5cb3efabce007c3a0230e4cc2e27da491c646b6c"
}
}
}
],
"Version": 1
],
"Version": 1
}

View File

@@ -10,7 +10,7 @@
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.6.220404001" targetFramework="native" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.UI.Xaml" version="2.7.2-prerelease.220406002" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3-prerelease.220816001" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220201.1" targetFramework="native" developmentDependency="true" />
<!-- Managed packages -->

View File

@@ -197,7 +197,7 @@
},
"intenseTextStyle": {
"default": "bright",
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"description": "Controls how 'intense' text is rendered when unfocused. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"enum": [
"none",
"bold",
@@ -366,6 +366,10 @@
"quit",
"adjustOpacity",
"restoreLastClosed",
"addMark",
"scrollToMark",
"clearMark",
"clearAllMarks",
"unbound"
],
"type": "string"
@@ -385,6 +389,15 @@
],
"type": "string"
},
"ScrollToMarkDirection": {
"enum": [
"previous",
"next",
"first",
"last"
],
"type": "string"
},
"ResizeDirection": {
"enum": [
"left",
@@ -734,6 +747,30 @@
"direction"
]
},
"ScrollToMarkAction": {
"description": "Arguments corresponding to a Scroll to Mark Action",
"allOf": [
{
"$ref": "#/$defs/ShortcutAction"
},
{
"properties": {
"action": {
"type": "string",
"const": "scrollToMark"
},
"direction": {
"$ref": "#/$defs/ScrollToMarkDirection",
"default": "previous",
"description": "The direction to scroll to a mark."
}
}
}
],
"required": [
"direction"
]
},
"SendInputAction": {
"description": "Arguments corresponding to a Send Input Action",
"allOf": [
@@ -841,6 +878,27 @@
}
]
},
"AddMarkAction": {
"description": "Arguments corresponding to an Add Mark Action",
"allOf": [
{
"$ref": "#/$defs/ShortcutAction"
},
{
"properties": {
"action": {
"type": "string",
"const": "addMark"
},
"color": {
"$ref": "#/$defs/Color",
"default": null,
"description": "If provided, will set the mark's color to the given value."
}
}
}
]
},
"SetColorSchemeAction": {
"description": "Arguments corresponding to a Set Color Scheme Action",
"allOf": [
@@ -1669,6 +1727,16 @@
"description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.",
"type": "boolean"
},
"experimental.autoMarkPrompts": {
"default": false,
"description": "When set to true, prompts will automatically be marked.",
"type": "boolean"
},
"experimental.showMarksOnScrollbar": {
"default": false,
"description": "When set to true, marks added to the buffer via the addMark action will appear on the scrollbar.",
"type": "boolean"
},
"disableAnimations": {
"default": false,
"description": "When set to `true`, visual animations will be disabled across the application.",
@@ -2013,15 +2081,20 @@
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
"$ref": "#/$defs/BellStyle"
},
"bellSound": {
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
"$ref": "#/$defs/BellSound"
},
"closeOnExit": {
"default": "graceful",
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
"default": "automatic",
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"automatic\" (behave as \"graceful\" only for processes launched by terminal, behave as \"always\" otherwise)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
"oneOf": [
{
"enum": [
"never",
"graceful",
"always"
"always",
"automatic"
],
"type": "string"
},
@@ -2136,6 +2209,17 @@
],
"deprecated": true
},
"intenseTextStyle": {
"default": "bright",
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"enum": [
"none",
"bold",
"bright",
"all"
],
"type": "string"
},
"foreground": {
"$ref": "#/$defs/Color",
"default": "#cccccc",

View File

@@ -5,55 +5,34 @@
#include "MidiAudio.hpp"
#include "../terminal/parser/stateMachine.hpp"
namespace
{
class MidiOut
{
public:
static constexpr auto NOTE_OFF = 0x80;
static constexpr auto NOTE_ON = 0x90;
static constexpr auto PROGRAM_CHANGE = 0xC0;
#include <dsound.h>
// We're using a square wave as an approximation of the sound that the
// original VT525 terminals might have produced. This is probably not
// quite right, but it works reasonably well.
static constexpr auto SQUARE_WAVE_SYNTH = 80;
MidiOut() noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutOpen(&handle, MIDI_MAPPER, NULL, NULL, CALLBACK_NULL);
OutputMessage(PROGRAM_CHANGE, SQUARE_WAVE_SYNTH);
}
}
~MidiOut() noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutClose(handle);
}
}
void OutputMessage(const int b1, const int b2, const int b3 = 0, const int b4 = 0) noexcept
{
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
{
midiOutShortMsg(handle, MAKELONG(MAKEWORD(b1, b2), MAKEWORD(b3, b4)));
}
}
MidiOut(const MidiOut&) = delete;
MidiOut(MidiOut&&) = delete;
MidiOut& operator=(const MidiOut&) = delete;
MidiOut& operator=(MidiOut&&) = delete;
private:
HMIDIOUT handle = nullptr;
};
}
#pragma comment(lib, "dxguid.lib")
using Microsoft::WRL::ComPtr;
using namespace std::chrono_literals;
// The WAVE_DATA below is an 8-bit PCM encoding of a triangle wave form.
// We just play this on repeat at varying frequencies to produce our notes.
constexpr auto WAVE_SIZE = 16u;
constexpr auto WAVE_DATA = std::array<byte, WAVE_SIZE>{ 128, 159, 191, 223, 255, 223, 191, 159, 128, 96, 64, 32, 0, 32, 64, 96 };
MidiAudio::MidiAudio(HWND windowHandle)
{
_directSoundModule.reset(LoadLibraryExW(L"dsound.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (_directSoundModule)
{
auto createFunction = GetProcAddressByFunctionDeclaration(_directSoundModule.get(), DirectSoundCreate8);
if (SUCCEEDED(createFunction(nullptr, &_directSound, nullptr)))
{
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
{
_createBuffers();
}
}
}
}
MidiAudio::~MidiAudio() noexcept
{
try
@@ -61,7 +40,7 @@ MidiAudio::~MidiAudio() noexcept
#pragma warning(suppress : 26447)
// We acquire the lock here so the class isn't destroyed while in use.
// If this throws, we'll catch it, so the C26447 warning is bogus.
_inUseMutex.lock();
const auto lock = std::unique_lock{ _inUseMutex };
}
catch (...)
{
@@ -103,13 +82,26 @@ void MidiAudio::Unlock()
void MidiAudio::PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept
try
{
// The MidiOut is a local static because we can only have one instance,
// and we only want to construct it when it's actually needed.
static MidiOut midiOut;
if (velocity)
const auto& buffer = _buffers.at(_activeBufferIndex);
if (velocity && buffer)
{
midiOut.OutputMessage(MidiOut::NOTE_ON, noteNumber, velocity);
// The formula for frequency is 2^(n/12) * 440Hz, where n is zero for
// the A above middle C (A4). In MIDI terms, A4 is note number 69,
// which is why we subtract 69. We also need to multiply by the size
// of the wave form to determine the frequency that the sound buffer
// has to be played to achieve the equivalent note frequency.
const auto frequency = std::pow(2.0, (noteNumber - 69.0) / 12.0) * 440.0 * WAVE_SIZE;
buffer->SetFrequency(gsl::narrow_cast<DWORD>(frequency));
// For the volume, we're using the formula defined in the the General
// MIDI Level 2 specification: Gain in dB = 40 * log10(v/127). We need
// to multiply by 4000, though, because the SetVolume method expects
// the volume to be in hundredths of a decibel.
const auto volume = 4000.0 * std::log10(velocity / 127.0);
buffer->SetVolume(gsl::narrow_cast<LONG>(volume));
// Resetting the buffer to a position that is slightly off from the
// last position will help to produce a clearer separation between
// tones when repeating sequences of the same note.
buffer->SetCurrentPosition((_lastBufferPosition + 12) % WAVE_SIZE);
}
// By waiting on the shutdown future with the duration of the note, we'll
@@ -117,9 +109,48 @@ try
// of the wait early if we've been shutdown.
_shutdownFuture.wait_for(duration);
if (velocity)
if (velocity && buffer)
{
midiOut.OutputMessage(MidiOut::NOTE_OFF, noteNumber, velocity);
// When the note ends, we just turn the volume down instead of stopping
// the sound buffer. This helps reduce unwanted static between notes.
buffer->SetVolume(DSBVOLUME_MIN);
buffer->GetCurrentPosition(&_lastBufferPosition, nullptr);
}
// Cycling between multiple buffers can also help reduce the static.
_activeBufferIndex = (_activeBufferIndex + 1) % _buffers.size();
}
CATCH_LOG()
void MidiAudio::_createBuffers() noexcept
{
auto waveFormat = WAVEFORMATEX{};
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = 1;
waveFormat.nSamplesPerSec = 8000;
waveFormat.wBitsPerSample = 8;
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
auto bufferDescription = DSBUFFERDESC{};
bufferDescription.dwSize = sizeof(DSBUFFERDESC);
bufferDescription.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;
bufferDescription.dwBufferBytes = WAVE_SIZE;
bufferDescription.lpwfxFormat = &waveFormat;
for (auto& buffer : _buffers)
{
if (SUCCEEDED(_directSound->CreateSoundBuffer(&bufferDescription, &buffer, nullptr)))
{
LPVOID bufferPtr;
DWORD bufferSize;
if (SUCCEEDED(buffer->Lock(0, 0, &bufferPtr, &bufferSize, nullptr, nullptr, DSBLOCK_ENTIREBUFFER)))
{
std::memcpy(bufferPtr, WAVE_DATA.data(), WAVE_DATA.size());
buffer->Unlock(bufferPtr, bufferSize, nullptr, 0);
}
buffer->SetVolume(DSBVOLUME_MIN);
buffer->Play(0, 0, DSBPLAY_LOOPING);
}
}
}

View File

@@ -11,13 +11,17 @@ Abstract:
#pragma once
#include <array>
#include <future>
#include <mutex>
struct IDirectSound8;
struct IDirectSoundBuffer;
class MidiAudio
{
public:
MidiAudio() = default;
MidiAudio(HWND windowHandle);
MidiAudio(const MidiAudio&) = delete;
MidiAudio(MidiAudio&&) = delete;
MidiAudio& operator=(const MidiAudio&) = delete;
@@ -30,6 +34,13 @@ public:
void PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept;
private:
void _createBuffers() noexcept;
wil::unique_hmodule _directSoundModule;
Microsoft::WRL::ComPtr<IDirectSound8> _directSound;
std::array<Microsoft::WRL::ComPtr<IDirectSoundBuffer>, 2> _buffers;
size_t _activeBufferIndex = 0;
DWORD _lastBufferPosition = 0;
std::promise<void> _shutdownPromise;
std::future<void> _shutdownFuture;
std::mutex _inUseMutex;

View File

@@ -357,11 +357,6 @@ void TextAttribute::SetReverseVideo(bool isReversed) noexcept
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO, isReversed);
}
ExtendedAttributes TextAttribute::GetExtendedAttributes() const noexcept
{
return _extendedAttrs;
}
// Routine Description:
// - swaps foreground and background color
void TextAttribute::Invert() noexcept

View File

@@ -109,7 +109,10 @@ public:
void SetOverlined(bool isOverlined) noexcept;
void SetReverseVideo(bool isReversed) noexcept;
ExtendedAttributes GetExtendedAttributes() const noexcept;
constexpr ExtendedAttributes GetExtendedAttributes() const noexcept
{
return _extendedAttrs;
}
bool IsHyperlink() const noexcept;
@@ -161,6 +164,13 @@ public:
{
return WI_IsAnyFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE);
}
constexpr bool HasAnyExtendedAttributes() const noexcept
{
return GetExtendedAttributes() != ExtendedAttributes::Normal ||
IsAnyGridLineEnabled() ||
GetHyperlinkId() != 0 ||
IsReverseVideo();
}
private:
static std::array<TextColor, 16> s_legacyForegroundColorMap;

View File

@@ -1138,7 +1138,7 @@ til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_
// that it actually points to a space in the buffer
copy = bufferSize.BottomRightInclusive();
}
else if (target >= limit)
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
// if at/past the limit --> clamp to limit
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
@@ -1254,7 +1254,7 @@ til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_vi
// Already at/past the limit. Can't move forward.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (target >= limit)
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
return target;
}
@@ -1282,7 +1282,7 @@ til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, cons
const auto bufferSize{ GetSize() };
auto result{ target };
if (target >= limit)
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
// if we're already on/past the last RegularChar,
// clamp result to that position
@@ -1419,7 +1419,7 @@ bool TextBuffer::MoveToNextWord(til::point& pos, const std::wstring_view wordDel
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
if (copy >= limit)
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
{
return false;
}
@@ -1466,7 +1466,7 @@ til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::po
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
// Clamp pos to limit
if (resultPos > limit)
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
resultPos = limit;
}
@@ -1494,7 +1494,7 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
// Clamp pos to limit
if (resultPos > limit)
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
{
resultPos = limit;
}
@@ -1524,19 +1524,9 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
{
const auto bufferSize = GetSize();
bool pastEndInclusive;
til::point limit;
{
// if the limit is past the end of the buffer,
// 1) clamp limit to end of buffer
// 2) set pastEndInclusive
const auto endInclusive{ bufferSize.BottomRightInclusive() };
const auto val = limitOptional.value_or(endInclusive);
pastEndInclusive = val > endInclusive;
limit = pastEndInclusive ? endInclusive : val;
}
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit) + (pastEndInclusive ? 1 : 0) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
if (distanceToLimit >= 0)
{
// Corner Case: we're on/past the limit
@@ -1579,7 +1569,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (pos > limit)
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
{
// we're past the end
// clamp us to the limit
@@ -1621,7 +1611,7 @@ const std::vector<til::inclusive_rect> TextBuffer::GetTextRects(til::point start
// (0,0) is the top-left of the screen
// the physically "higher" coordinate is closer to the top-left
// the physically "lower" coordinate is closer to the bottom-right
const auto [higherCoord, lowerCoord] = start <= end ?
const auto [higherCoord, lowerCoord] = bufferSize.CompareInBounds(start, end) <= 0 ?
std::make_tuple(start, end) :
std::make_tuple(end, start);

View File

@@ -1,7 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<PropertyGroup Label="NuGet Dependencies">
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<PropertyGroup Label="Configuration">
<!--
These two properties are very important!
@@ -164,13 +169,7 @@
</Target>
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View File

@@ -99,7 +99,8 @@
<com:ComInterface>
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/> <!-- ITerminalHandoff -->
<com:Interface Id="AA6B364F-4A50-4176-9002-0AE755E7B5EF" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/> <!-- ITerminalHandoff2 -->
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
</com:ComInterface>
</com:Extension>

View File

@@ -188,7 +188,8 @@
<com:ComInterface>
<com:ProxyStub Id="1833E661-CC81-4DD0-87C6-C2F74BD39EFA" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/> <!-- ITerminalHandoff -->
<com:Interface Id="AA6B364F-4A50-4176-9002-0AE755E7B5EF" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/> <!-- ITerminalHandoff2 -->
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
</com:ComInterface>
</com:Extension>

View File

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

View File

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

View File

@@ -30,14 +30,6 @@
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)\src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="pch.h" />

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,11 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="HwndTerminal.cpp" />
<ClCompile Include="HwndTerminalAutomationPeer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="HwndTerminal.hpp" />
<ClInclude Include="HwndTerminalAutomationPeer.hpp" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
@@ -42,6 +44,9 @@
<ProjectReference Include="$(SolutionDir)src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj">
<Project>{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\uia\lib\uia.vcxproj">
<Project>{48d21369-3d7b-4431-9967-24e81292cf63}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>

View File

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

View File

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

View File

@@ -103,6 +103,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
auto proposedCommandline = false;
Remoting::ProposeCommandlineResult result{ nullptr };
auto attempts = 0;
while (!proposedCommandline)
{
try
@@ -118,78 +119,78 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
proposedCommandline = true;
}
catch (const winrt::hresult_error& e)
catch (...)
{
// We did not successfully ask the king what to do. They
// hopefully just died here. That's okay, let's just go ask the
// next in the line of succession. At the very worst, we'll find
// _us_, (likely last in the line).
// We did not successfully ask the king what to do. This could
// be for many reasons. Most commonly, the monarch died as we
// were talking to it. That could be a RPC_SERVER_UNAVAILABLE_HR
// or RPC_CALL_FAILED_HR (GH#12666). We also saw a
// RPC_S_CALL_FAILED_DNE in GH#11790. Ultimately, if this is
// gonna fail, we want to just try again, regardless of the
// cause. That's why we're no longer checking what the exception
// was, we're just always gonna try again regardless.
//
// If the king returned some _other_ error here, than lets
// bubble that up because that's a real issue.
//
// I'm checking both these here. I had previously got a
// RPC_S_CALL_FAILED about here once.
if (e.code() == RPC_SERVER_UNAVAILABLE_HR || e.code() == RPC_CALL_FAILED_HR)
// They hopefully just died here. That's okay, let's just go
// ask the next in the line of succession. At the very worst,
// we'll find _us_, (likely last in the line).
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_unexpectedExceptionFromKing",
TraceLoggingInt32(attempts, "attempts", "How many times we've tried"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
LOG_CAUGHT_EXCEPTION();
attempts++;
if (attempts >= 10)
{
// We've tried 10 times to find the monarch, failing each
// time. Since we have no idea why, we're guessing that in
// this case, there's just a Monarch registered that's
// misbehaving. In this case, just fall back to
// "IsolatedMonarchMode" - we can't trust the currently
// registered one.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_kingDied",
"WindowManager_TooManyAttempts_NullMonarchIsolateMode",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_monarch = winrt::make<winrt::Microsoft::Terminal::Remoting::implementation::Monarch>();
_createCallbacks();
}
else
{
// We failed to ask the monarch. It must have died. Try and
// find the real monarch. Don't perform an election, that
// assumes we have a peasant, which we don't yet.
_createMonarchAndCallbacks();
// _createMonarchAndCallbacks will initialize _isKing
if (_isKing)
{
// We became the king. We don't need to ProposeCommandline to ourself, we're just
// going to do it.
//
// Return early, because there's nothing else for us to do here.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_becameKing",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// In WindowManager::ProposeCommandline, had we been the
// king originally, we would have started by setting
// this to true. We became the monarch here, so set it
// here as well.
_shouldCreateWindow = true;
return;
}
// Here, we created the new monarch, it wasn't us, so we're
// gonna go through the while loop again and ask the new
// king.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_tryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
if (_isKing)
{
// We became the king. We don't need to ProposeCommandline to ourself, we're just
// going to do it.
//
// Return early, because there's nothing else for us to do here.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_unexpectedResultFromKing",
"WindowManager_proposeToMonarch_becameKing",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
LOG_CAUGHT_EXCEPTION();
throw;
// In WindowManager::ProposeCommandline, had we been the
// king originally, we would have started by setting
// this to true. We became the monarch here, so set it
// here as well.
_shouldCreateWindow = true;
return;
}
}
catch (...)
{
// If the monarch (maybe us) failed for _any other reason_ than
// them dying. This IS quite unexpected. Let this bubble out.
// Here, we created the new monarch, it wasn't us, so we're
// gonna go through the while loop again and ask the new
// king.
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_proposeToMonarch_unexpectedExceptionFromKing",
"WindowManager_proposeToMonarch_tryAgain",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
LOG_CAUGHT_EXCEPTION();
throw;
}
}
@@ -345,15 +346,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
CLSCTX_LOCAL_SERVER);
}
// NOTE: This can throw! Callers include:
// - the constructor, who performs this in a loop until it successfully
// find a a monarch
// - the performElection method, which is called in the waitOnMonarch
// thread. All the calls in that thread are wrapped in try/catch's
// already.
// - _createOurPeasant, who might do this in a loop to establish us with the
// monarch.
void WindowManager::_createMonarchAndCallbacks()
// Tries to instantiate a monarch, tries again, and eventually either throws
// (so that the caller will try again) or falls back to the isolated
// monarch.
void WindowManager::_redundantCreateMonarch()
{
_createMonarch();
@@ -399,9 +395,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::hresult_error(E_UNEXPECTED, L"Did not expect the Monarch to ever be null");
}
}
}
// NOTE: This can throw! Callers include:
// - the constructor, who performs this in a loop until it successfully
// find a a monarch
// - the performElection method, which is called in the waitOnMonarch
// thread. All the calls in that thread are wrapped in try/catch's
// already.
// - _createOurPeasant, who might do this in a loop to establish us with the
// monarch.
void WindowManager::_createMonarchAndCallbacks()
{
_redundantCreateMonarch();
// We're pretty confident that we have a Monarch here.
_createCallbacks();
}
// Check if we became the king, and if we are, wire up callbacks.
void WindowManager::_createCallbacks()
{
// Save the result of checking if we're the king. We want to avoid
// unnecessary calls back and forth if we can.
_isKing = _areWeTheKing();

View File

@@ -72,7 +72,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void _registerAsMonarch();
void _createMonarch();
void _redundantCreateMonarch();
void _createMonarchAndCallbacks();
void _createCallbacks();
bool _areWeTheKing();
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID,
const winrt::hstring& givenName);

View File

@@ -25,37 +25,25 @@ static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" };
// failure from an earlier HRESULT.
HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
IBindCtx* /*pBindContext*/)
try
{
wil::com_ptr_nothrow<IShellItem> psi;
RETURN_IF_FAILED(GetBestLocationFromSelectionOrSite(psiItemArray, psi.put()));
if (!psi)
{
return S_FALSE;
}
wil::unique_cotaskmem_string pszName;
if (psiItemArray == nullptr)
{
// get the current path from explorer.exe
const auto path = this->_GetPathFromExplorer();
// no go, unable to get a reasonable path
if (path.empty())
{
return S_FALSE;
}
pszName = wil::make_cotaskmem_string(path.c_str(), path.length());
}
else
{
DWORD count;
psiItemArray->GetCount(&count);
winrt::com_ptr<IShellItem> psi;
RETURN_IF_FAILED(psiItemArray->GetItemAt(0, psi.put()));
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName));
}
RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName));
{
wil::unique_process_information _piClient;
STARTUPINFOEX siEx{ 0 };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()) };
std::wstring cmdline;
RETURN_IF_FAILED(wil::str_printf_nothrow(cmdline, LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()));
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr, // lpApplicationName
cmdline.data(),
@@ -72,6 +60,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
return S_OK;
}
CATCH_RETURN()
HRESULT OpenTerminalHere::GetToolTip(IShellItemArray* /*psiItemArray*/,
LPWSTR* ppszInfoTip)
@@ -109,22 +98,14 @@ HRESULT OpenTerminalHere::GetState(IShellItemArray* psiItemArray,
// We however don't need to bother with any of that.
// If no item was selected when the context menu was opened and Explorer
// is not at a valid path (e.g. This PC or Quick Access), we should hide
// is not at a valid location (e.g. This PC or Quick Access), we should hide
// the verb from the context menu.
if (psiItemArray == nullptr)
{
const auto path = this->_GetPathFromExplorer();
*pCmdState = path.empty() ? ECS_HIDDEN : ECS_ENABLED;
}
else
{
winrt::com_ptr<IShellItem> psi;
psiItemArray->GetItemAt(0, psi.put());
SFGAOF attributes;
const bool isFileSystemItem = (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
}
wil::com_ptr_nothrow<IShellItem> psi;
RETURN_IF_FAILED(GetBestLocationFromSelectionOrSite(psiItemArray, psi.put()));
SFGAOF attributes;
const bool isFileSystemItem = psi && (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
return S_OK;
}
@@ -160,102 +141,54 @@ HRESULT OpenTerminalHere::EnumSubCommands(IEnumExplorerCommand** ppEnum)
return E_NOTIMPL;
}
std::wstring OpenTerminalHere::_GetPathFromExplorer() const
IFACEMETHODIMP OpenTerminalHere::SetSite(IUnknown* site) noexcept
{
using namespace std;
using namespace winrt;
wstring path;
HRESULT hr = NOERROR;
auto hwnd = ::GetForegroundWindow();
if (hwnd == nullptr)
{
return path;
}
TCHAR szName[MAX_PATH] = { 0 };
::GetClassName(hwnd, szName, MAX_PATH);
if (0 == StrCmp(szName, L"WorkerW") ||
0 == StrCmp(szName, L"Progman"))
{
//special folder: desktop
hr = ::SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, szName);
if (FAILED(hr))
{
return path;
}
path = szName;
return path;
}
if (0 != StrCmp(szName, L"CabinetWClass"))
{
return path;
}
com_ptr<IShellWindows> shell;
try
{
shell = create_instance<IShellWindows>(CLSID_ShellWindows, CLSCTX_ALL);
}
catch (...)
{
//look like try_create_instance is not available no more
}
if (shell == nullptr)
{
return path;
}
com_ptr<IDispatch> disp;
wil::unique_variant variant;
variant.vt = VT_I4;
com_ptr<IWebBrowserApp> browser;
// look for correct explorer window
for (variant.intVal = 0;
shell->Item(variant, disp.put()) == S_OK;
variant.intVal++)
{
com_ptr<IWebBrowserApp> tmp;
if (FAILED(disp->QueryInterface(tmp.put())))
{
disp = nullptr; // get rid of DEBUG non-nullptr warning
continue;
}
HWND tmpHWND = NULL;
hr = tmp->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&tmpHWND));
if (hwnd == tmpHWND)
{
browser = tmp;
disp = nullptr; // get rid of DEBUG non-nullptr warning
break; //found
}
disp = nullptr; // get rid of DEBUG non-nullptr warning
}
if (browser != nullptr)
{
wil::unique_bstr url;
hr = browser->get_LocationURL(&url);
if (FAILED(hr))
{
return path;
}
wstring sUrl(url.get(), SysStringLen(url.get()));
DWORD size = MAX_PATH;
hr = ::PathCreateFromUrl(sUrl.c_str(), szName, &size, NULL);
if (SUCCEEDED(hr))
{
path = szName;
}
}
return path;
site_ = site;
return S_OK;
}
IFACEMETHODIMP OpenTerminalHere::GetSite(REFIID riid, void** site) noexcept
{
RETURN_IF_FAILED(site_.query_to(riid, site));
return S_OK;
}
HRESULT OpenTerminalHere::GetLocationFromSite(IShellItem** location) const noexcept
{
wil::assign_null_to_opt_param(location);
if (!site_)
{
return S_FALSE;
}
wil::com_ptr_nothrow<IServiceProvider> serviceProvider;
RETURN_IF_FAILED(site_.query_to(serviceProvider.put()));
wil::com_ptr_nothrow<IFolderView> folderView;
RETURN_IF_FAILED(serviceProvider->QueryService(SID_SFolderView, IID_PPV_ARGS(folderView.put())));
RETURN_IF_FAILED(folderView->GetFolder(IID_PPV_ARGS(location)));
return S_OK;
}
HRESULT OpenTerminalHere::GetBestLocationFromSelectionOrSite(IShellItemArray* psiArray, IShellItem** location) const noexcept
{
wil::com_ptr_nothrow<IShellItem> psi;
if (psiArray)
{
DWORD count{};
RETURN_IF_FAILED(psiArray->GetCount(&count));
if (count) // Sometimes we get an array with a count of 0. Fall back to the site chain.
{
RETURN_IF_FAILED(psiArray->GetItemAt(0, psi.put()));
}
}
if (!psi)
{
RETURN_IF_FAILED(GetLocationFromSite(psi.put()));
}
RETURN_HR_IF(S_FALSE, !psi);
RETURN_IF_FAILED(psi.copy_to(location));
return S_OK;
}

View File

@@ -22,8 +22,6 @@ Author(s):
--*/
#pragma once
#include <conattrs.hpp>
using namespace Microsoft::WRL;
struct
@@ -34,7 +32,7 @@ struct
#else // DEV
__declspec(uuid("52065414-e077-47ec-a3ac-1cc5455e1b54"))
#endif
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand>
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand, IObjectWithSite>
{
#pragma region IExplorerCommand
STDMETHODIMP Invoke(IShellItemArray* psiItemArray,
@@ -52,9 +50,16 @@ struct
STDMETHODIMP GetCanonicalName(GUID* pguidCommandName);
STDMETHODIMP EnumSubCommands(IEnumExplorerCommand** ppEnum);
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP SetSite(IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(REFIID riid, void** site) noexcept;
#pragma endregion
private:
std::wstring _GetPathFromExplorer() const;
HRESULT GetLocationFromSite(IShellItem** location) const noexcept;
HRESULT GetBestLocationFromSelectionOrSite(IShellItemArray* psiArray, IShellItem** location) const noexcept;
wil::com_ptr_nothrow<IUnknown> site_;
};
CoCreatableClass(OpenTerminalHere);

View File

@@ -201,10 +201,11 @@ namespace winrt::TerminalApp::implementation
}
}
const auto& duplicateFromTab{ realArgs.SplitMode() == SplitType::Duplicate ? _GetFocusedTab() : nullptr };
_SplitPane(realArgs.SplitDirection(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),
_MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
_MakePane(realArgs.TerminalArgs(), duplicateFromTab));
args.Handled(true);
}
}
@@ -606,7 +607,7 @@ namespace winrt::TerminalApp::implementation
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->ActivateColorPicker();
activeTab->RequestColorPicker();
}
args.Handled(true);
}

View File

@@ -10,7 +10,7 @@
#include <LibraryResources.h>
#include <WtExeUtils.h>
#include <wil/token_helpers.h >
#include <wil/token_helpers.h>
#include "../../types/inc/utils.hpp"
@@ -292,10 +292,36 @@ namespace winrt::TerminalApp::implementation
_root->Maximized(true);
}
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode))
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow())
{
_root->SetFullscreen(true);
}
FILETIME creationTime, exitTime, kernelTime, userTime, now;
if (GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime))
{
static constexpr auto asInteger = [](const FILETIME& f) {
ULARGE_INTEGER i;
i.LowPart = f.dwLowDateTime;
i.HighPart = f.dwHighDateTime;
return i.QuadPart;
};
static constexpr auto asSeconds = [](uint64_t v) {
return v * 1e-7f;
};
GetSystemTimeAsFileTime(&now);
const auto latency = asSeconds(asInteger(now) - asInteger(creationTime));
TraceLoggingWrite(
g_hTerminalAppProvider,
"AppInitialized",
TraceLoggingDescription("Event emitted once the app is initialized"),
TraceLoggingFloat32(latency, "latency"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
});
_root->Create();
@@ -312,7 +338,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingDescription("Event emitted when the application is started"),
TraceLoggingBool(_settings.GlobalSettings().ShowTabsInTitlebar(), "TabsInTitlebar"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
void AppLogic::Quit()
@@ -504,26 +530,8 @@ namespace winrt::TerminalApp::implementation
if (keyboardServiceIsDisabled)
{
_root->ShowKeyboardServiceWarning();
TraceLoggingWrite(
g_hTerminalAppProvider,
"KeyboardServiceWasDisabled",
TraceLoggingDescription("Event emitted when the keyboard service is disabled, and we warned them about it"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
}
else
{
// For when the warning was disabled in the settings
TraceLoggingWrite(
g_hTerminalAppProvider,
"KeyboardServiceWarningWasDisabledBySetting",
TraceLoggingDescription("Event emitted when the user has disabled the KB service warning"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
if (FAILED(_settingsLoadedResult))
{
@@ -557,7 +565,7 @@ namespace winrt::TerminalApp::implementation
// not be noisy with this dialog if we failed for some reason.
// Open the service manager. This will return 0 if it failed.
wil::unique_schandle hManager{ OpenSCManager(nullptr, nullptr, 0) };
wil::unique_schandle hManager{ OpenSCManagerW(nullptr, nullptr, 0) };
if (LOG_LAST_ERROR_IF(!hManager.is_valid()))
{
@@ -565,8 +573,12 @@ namespace winrt::TerminalApp::implementation
}
// Get a handle to the keyboard service
wil::unique_schandle hService{ OpenService(hManager.get(), TabletInputServiceKey.data(), SERVICE_QUERY_STATUS) };
if (LOG_LAST_ERROR_IF(!hService.is_valid()))
wil::unique_schandle hService{ OpenServiceW(hManager.get(), TabletInputServiceKey.data(), SERVICE_QUERY_STATUS) };
// Windows 11 doesn't have a TabletInputService.
// (It was renamed to TextInputManagementService, because people kept thinking that a
// service called "tablet-something" is system-irrelevant on PCs and can be disabled.)
if (!hService.is_valid())
{
return true;
}
@@ -851,15 +863,6 @@ namespace winrt::TerminalApp::implementation
// happening during startup, it'll need to happen on a background thread.
void AppLogic::LoadSettings()
{
auto start = std::chrono::high_resolution_clock::now();
TraceLoggingWrite(
g_hTerminalAppProvider,
"SettingsLoadStarted",
TraceLoggingDescription("Event emitted before loading the settings"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
// Attempt to load the settings.
// If it fails,
// - use Default settings,
@@ -875,17 +878,6 @@ namespace winrt::TerminalApp::implementation
_settings = CascadiaSettings::LoadDefaults();
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> delta = end - start;
TraceLoggingWrite(
g_hTerminalAppProvider,
"SettingsLoadComplete",
TraceLoggingDescription("Event emitted when loading the settings is finished"),
TraceLoggingFloat64(delta.count(), "Duration"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
_loadedInitialSettings = true;
// Register for directory change notification.

View File

@@ -70,7 +70,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingDescription("Event emitted when the Command Palette is opened"),
TraceLoggingWideString(L"Action", "Mode", "which mode the palette was opened in"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
else
{
@@ -474,7 +474,13 @@ namespace winrt::TerminalApp::implementation
return;
}
auto focusedElementOrAncestor = Input::FocusManager::GetFocusedElement(this->XamlRoot()).try_as<DependencyObject>();
auto root = this->XamlRoot();
if (!root)
{
return;
}
auto focusedElementOrAncestor = Input::FocusManager::GetFocusedElement(root).try_as<DependencyObject>();
while (focusedElementOrAncestor)
{
if (focusedElementOrAncestor == *this)
@@ -524,6 +530,34 @@ namespace winrt::TerminalApp::implementation
}
}
void CommandPalette::_listItemSelectionChanged(const Windows::Foundation::IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e)
{
// We don't care about...
// - CommandlineMode: it doesn't have any selectable items in the list view
// - TabSwitchMode: focus and selected item are in sync
if (_currentMode == CommandPaletteMode::ActionMode || _currentMode == CommandPaletteMode::TabSearchMode)
{
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) })
{
if (const auto selectedList = e.AddedItems(); selectedList.Size() > 0)
{
const auto selectedCommand = selectedList.GetAt(0);
if (const auto filteredCmd = selectedCommand.try_as<TerminalApp::FilteredCommand>())
{
if (const auto paletteItem = filteredCmd.Item().try_as<TerminalApp::PaletteItem>())
{
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ItemAdded,
Automation::Peers::AutomationNotificationProcessing::MostRecent,
paletteItem.Name() + L" " + paletteItem.KeyChordText(),
L"CommandPaletteSelectedItemChanged" /* unique name for this notification category */);
}
}
}
}
}
}
// Method Description:
// This event is called when the user clicks on an ChevronLeft button right
// next to the ParentCommandName (e.g. New Tab...) above the subcommands list.
@@ -677,7 +711,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingUInt32(searchTextLength, "SearchTextLength", "Number of characters in the search string"),
TraceLoggingUInt32(nestedCommandDepth, "NestedCommandDepth", "the depth in the tree of commands for the dispatched action"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
}
}
@@ -745,7 +779,7 @@ namespace winrt::TerminalApp::implementation
"CommandPaletteDispatchedCommandline",
TraceLoggingDescription("Event emitted when the user runs a commandline in the Command Palette"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
if (const auto commandLinePaletteItem{ filteredCommand.value().Item().try_as<winrt::TerminalApp::CommandLinePaletteItem>() })
{
@@ -783,7 +817,7 @@ namespace winrt::TerminalApp::implementation
"CommandPaletteDismissed",
TraceLoggingDescription("Event emitted when the user dismisses the Command Palette without selecting an action"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
// Method Description:

View File

@@ -92,6 +92,8 @@ namespace winrt::TerminalApp::implementation
void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
void _updateFilteredActions();

View File

@@ -402,6 +402,7 @@
IsItemClickEnabled="True"
ItemClick="_listItemClicked"
ItemsSource="{x:Bind FilteredActions}"
SelectionChanged="_listItemSelectionChanged"
SelectionMode="Single" />
</Grid>

View File

@@ -111,7 +111,15 @@ namespace winrt::Microsoft::TerminalApp::implementation
void DebugTapConnection::_OutputHandler(const hstring str)
{
_TerminalOutputHandlers(til::visualize_control_codes(str));
auto output = til::visualize_control_codes(str);
// To make the output easier to read, we introduce a line break whenever
// an LF control is encountered. But at this point, the LF would have
// been converted to U+240A (␊), so that's what we need to search for.
for (size_t lfPos = 0; (lfPos = output.find(L'\u240A', lfPos)) != std::wstring::npos;)
{
output.insert(++lfPos, L"\r\n");
}
_TerminalOutputHandlers(output);
}
// Called by the DebugInputTapConnection to print user input

View File

@@ -89,12 +89,8 @@ winrt::fire_and_forget Jumplist::UpdateJumplist(const CascadiaSettings& settings
winrt::com_ptr<IObjectCollection> jumplistItems;
jumplistItems.capture(jumplistInstance, &ICustomDestinationList::BeginList, &slots);
// It's easier to clear the list and re-add everything. The settings aren't
// updated often, and there likely isn't a huge amount of items to add.
THROW_IF_FAILED(jumplistItems->Clear());
// Update the list of profiles.
THROW_IF_FAILED(_updateProfiles(jumplistItems.get(), strongSettings.ActiveProfiles().GetView()));
_updateProfiles(jumplistItems.get(), strongSettings.ActiveProfiles().GetView());
// TODO GH#1571: Add items from the future customizable new tab dropdown as well.
// This could either replace the default profiles, or be added alongside them.
@@ -116,26 +112,22 @@ winrt::fire_and_forget Jumplist::UpdateJumplist(const CascadiaSettings& settings
// - profiles - The profiles to add to the jumplist
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Jumplist::_updateProfiles(IObjectCollection* jumplistItems, winrt::Windows::Foundation::Collections::IVectorView<Profile> profiles) noexcept
void Jumplist::_updateProfiles(IObjectCollection* jumplistItems, winrt::Windows::Foundation::Collections::IVectorView<Profile> profiles)
{
try
// It's easier to clear the list and re-add everything. The settings aren't
// updated often, and there likely isn't a huge amount of items to add.
THROW_IF_FAILED(jumplistItems->Clear());
for (const auto& profile : profiles)
{
for (const auto& profile : profiles)
{
// Craft the arguments following "wt.exe"
auto args = fmt::format(L"-p {}", to_hstring(profile.Guid()));
// Craft the arguments following "wt.exe"
auto args = fmt::format(L"-p {}", to_hstring(profile.Guid()));
// Create the shell link object for the profile
winrt::com_ptr<IShellLinkW> shLink;
const auto normalizedIconPath{ _normalizeIconPath(profile.Icon()) };
RETURN_IF_FAILED(_createShellLink(profile.Name(), normalizedIconPath, args, shLink.put()));
RETURN_IF_FAILED(jumplistItems->AddObject(shLink.get()));
}
return S_OK;
// Create the shell link object for the profile
const auto normalizedIconPath{ _normalizeIconPath(profile.Icon()) };
const auto shLink = _createShellLink(profile.Name(), normalizedIconPath, args);
THROW_IF_FAILED(jumplistItems->AddObject(shLink.get()));
}
CATCH_RETURN();
}
// Method Description:
@@ -150,36 +142,27 @@ winrt::fire_and_forget Jumplist::UpdateJumplist(const CascadiaSettings& settings
// - shLink: The shell link object to return.
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Jumplist::_createShellLink(const std::wstring_view name,
const std::wstring_view path,
const std::wstring_view args,
IShellLinkW** shLink) noexcept
winrt::com_ptr<IShellLinkW> Jumplist::_createShellLink(const std::wstring_view name, const std::wstring_view path, const std::wstring_view args)
{
try
{
auto sh = winrt::create_instance<IShellLinkW>(CLSID_ShellLink, CLSCTX_ALL);
auto sh = winrt::create_instance<IShellLinkW>(CLSID_ShellLink, CLSCTX_ALL);
const auto module{ GetWtExePath() };
RETURN_IF_FAILED(sh->SetPath(module.data()));
RETURN_IF_FAILED(sh->SetArguments(args.data()));
const auto module{ GetWtExePath() };
THROW_IF_FAILED(sh->SetPath(module.data()));
THROW_IF_FAILED(sh->SetArguments(args.data()));
PROPVARIANT titleProp;
titleProp.vt = VT_LPWSTR;
titleProp.pwszVal = const_cast<wchar_t*>(name.data());
PROPVARIANT titleProp;
titleProp.vt = VT_LPWSTR;
titleProp.pwszVal = const_cast<wchar_t*>(name.data());
PROPVARIANT iconProp;
iconProp.vt = VT_LPWSTR;
iconProp.pwszVal = const_cast<wchar_t*>(path.data());
PROPVARIANT iconProp;
iconProp.vt = VT_LPWSTR;
iconProp.pwszVal = const_cast<wchar_t*>(path.data());
auto propStore{ sh.as<IPropertyStore>() };
RETURN_IF_FAILED(propStore->SetValue(PKEY_Title, titleProp));
RETURN_IF_FAILED(propStore->SetValue(PKEY_AppUserModel_DestListLogoUri, iconProp));
auto propStore{ sh.as<IPropertyStore>() };
THROW_IF_FAILED(propStore->SetValue(PKEY_Title, titleProp));
THROW_IF_FAILED(propStore->SetValue(PKEY_AppUserModel_DestListLogoUri, iconProp));
RETURN_IF_FAILED(propStore->Commit());
THROW_IF_FAILED(propStore->Commit());
*shLink = sh.detach();
return S_OK;
}
CATCH_RETURN();
return sh;
}

View File

@@ -21,6 +21,6 @@ public:
static winrt::fire_and_forget UpdateJumplist(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) noexcept;
private:
[[nodiscard]] static HRESULT _updateProfiles(IObjectCollection* jumplistItems, winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::Profile> profiles) noexcept;
[[nodiscard]] static HRESULT _createShellLink(const std::wstring_view name, const std::wstring_view path, const std::wstring_view args, IShellLinkW** shLink) noexcept;
static void _updateProfiles(IObjectCollection* jumplistItems, winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::Profile> profiles);
static winrt::com_ptr<IShellLinkW> _createShellLink(const std::wstring_view name, const std::wstring_view path, const std::wstring_view args);
};

View File

@@ -20,70 +20,73 @@
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="CloseButtonColor">#C42B1C</Color>
<x:Double x:Key="CaptionButtonStrokeWidth">1.0</x:Double>
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="CaptionButtonBackgroundPressed"
ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="CaptionButtonStroke"
<StaticResource x:Key="CaptionButtonForeground"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CaptionButtonStrokeColor"
<StaticResource x:Key="CaptionButtonForegroundColor"
ResourceKey="SystemBaseHighColor" />
<StaticResource x:Key="CaptionButtonStrokePointerOver"
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CaptionButtonStrokePressed"
<StaticResource x:Key="CaptionButtonForegroundPressed"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<SolidColorBrush x:Key="CaptionButtonBackground"
Color="Transparent" />
<Color x:Key="CaptionButtonBackgroundColor">Transparent</Color>
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
Color="{ThemeResource CloseButtonColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
Opacity="0.9"
Color="{ThemeResource CloseButtonColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePressed"
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
Opacity="0.7"
Color="White" />
<SolidColorBrush x:Key="CloseButtonBackground"
Color="#00e81123" />
<Color x:Key="CloseButtonBackgroundColor">#00e81123</Color>
Color="#00C42B1C" />
<Color x:Key="CloseButtonBackgroundColor">#00C42B1C</Color>
<StaticResource x:Key="RestoreButtonGlyph"
ResourceKey="RestoreGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="CloseButtonColor">#C42B1C</Color>
<x:Double x:Key="CaptionButtonStrokeWidth">1.0</x:Double>
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="CaptionButtonBackgroundPressed"
ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="CaptionButtonStroke"
<StaticResource x:Key="CaptionButtonForeground"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CaptionButtonStrokeColor"
<StaticResource x:Key="CaptionButtonForegroundColor"
ResourceKey="SystemBaseHighColor" />
<StaticResource x:Key="CaptionButtonStrokePointerOver"
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="CaptionButtonStrokePressed"
<StaticResource x:Key="CaptionButtonForegroundPressed"
ResourceKey="SystemControlForegroundBaseHighBrush" />
<SolidColorBrush x:Key="CaptionButtonBackground"
Color="Transparent" />
<Color x:Key="CaptionButtonBackgroundColor">Transparent</Color>
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
Color="{ThemeResource CloseButtonColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
Opacity="0.9"
Color="{ThemeResource CloseButtonColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePressed"
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
Opacity="0.7"
Color="White" />
<SolidColorBrush x:Key="CloseButtonBackground"
Color="#00e81123" />
<Color x:Key="CloseButtonBackgroundColor">#00e81123</Color>
Color="#00C42B1C" />
<Color x:Key="CloseButtonBackgroundColor">#00C42B1C</Color>
<StaticResource x:Key="RestoreButtonGlyph"
ResourceKey="RestoreGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<x:Double x:Key="CaptionButtonStrokeWidth">2.0</x:Double>
<SolidColorBrush x:Key="CaptionButtonBackground"
Color="{ThemeResource SystemColorButtonFaceColor}" />
<StaticResource x:Key="CaptionButtonBackgroundColor"
@@ -92,32 +95,43 @@
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="CaptionButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="CaptionButtonStroke"
<SolidColorBrush x:Key="CaptionButtonForeground"
Color="{ThemeResource SystemColorButtonTextColor}" />
<StaticResource x:Key="CaptionButtonStrokeColor"
<StaticResource x:Key="CaptionButtonForegroundColor"
ResourceKey="SystemColorButtonTextColor" />
<SolidColorBrush x:Key="CaptionButtonStrokePointerOver"
<SolidColorBrush x:Key="CaptionButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="CaptionButtonStrokePressed"
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="CloseButtonStrokePressed"
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<StaticResource x:Key="RestoreButtonGlyph"
ResourceKey="RestoreGlyphContrast" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!--
These strings need to be initialized to something, so they're
just initialized to the path for the close button. Each specific
button should override them as needed.
Initializes the string to the close button glyph.
Each specific button overrides it as needed.
-->
<x:String x:Key="CaptionButtonPath">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
<x:String x:Key="CaptionButtonPathWindowMaximized">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
<x:String x:Key="CaptionButtonGlyph">&#xE8BB;</x:String>
<x:String x:Key="MinimizeGlyph">&#xE921;</x:String>
<x:String x:Key="MaximizeGlyph">&#xE922;</x:String>
<x:String x:Key="RestoreGlyph">&#xE923;</x:String>
<x:String x:Key="CloseGlyph">&#xE8BB;</x:String>
<x:String x:Key="MinimizeGlyphContrast">&#xEF2D;</x:String>
<x:String x:Key="MaximizeGlyphContrast">&#xEF2E;</x:String>
<x:String x:Key="RestoreGlyphContrast">&#xEF2F;</x:String>
<x:String x:Key="CloseGlyphContrast">&#xEF2C;</x:String>
<!--
"CaptionButtonHeightWindowed" and
@@ -151,16 +165,13 @@
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Path x:Name="Path"
Width="10"
Height="10"
Data="{ThemeResource CaptionButtonPath}"
Stretch="Fill"
Stroke="{ThemeResource CaptionButtonStroke}"
StrokeEndLineCap="Square"
StrokeStartLineCap="Square"
StrokeThickness="{ThemeResource CaptionButtonStrokeWidth}"
UseLayoutRounding="True" />
<Viewbox Width="10"
Height="10">
<FontIcon x:Name="ButtonIcon"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="{ThemeResource CaptionButtonForeground}"
Glyph="{ThemeResource CaptionButtonGlyph}" />
</Viewbox>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
@@ -172,9 +183,9 @@
Storyboard.TargetProperty="(UIElement.Background).(SolidColorBrush.Color)"
To="{ThemeResource CaptionButtonBackgroundColor}"
Duration="0:0:0.15" />
<ColorAnimation Storyboard.TargetName="Path"
Storyboard.TargetProperty="(UIElement.Stroke).(SolidColorBrush.Color)"
To="{ThemeResource CaptionButtonStrokeColor}"
<ColorAnimation Storyboard.TargetName="ButtonIcon"
Storyboard.TargetProperty="(UIElement.Foreground).(SolidColorBrush.Color)"
To="{ThemeResource CaptionButtonForegroundColor}"
Duration="0:0:0.1" />
</Storyboard>
</VisualTransition>
@@ -183,21 +194,21 @@
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackground}" />
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStroke}" />
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForeground}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPointerOver}" />
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStrokePointerOver}" />
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPointerOver}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPressed}" />
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStrokePressed}" />
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPressed}" />
</VisualState.Setters>
</VisualState>
@@ -209,7 +220,7 @@
<VisualState x:Name="WindowStateMaximized">
<VisualState.Setters>
<Setter Target="Path.Data" Value="{ThemeResource CaptionButtonPathWindowMaximized}" />
<Setter Target="ButtonIcon.Glyph" Value="{ThemeResource RestoreButtonGlyph}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
@@ -235,7 +246,20 @@
Style="{StaticResource CaptionButton}">
<Button.Resources>
<ResourceDictionary>
<x:String x:Key="CaptionButtonPath">M 0 0 H 10</x:String>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MinimizeGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MinimizeGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MinimizeGlyphContrast" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>
@@ -253,10 +277,21 @@
Click="_MaximizeClick"
Style="{StaticResource CaptionButton}">
<Button.Resources>
<!-- These paths are complicated, but taken directly from WinUI's WindowCaptionButton paths. -->
<ResourceDictionary>
<x:String x:Key="CaptionButtonPath">M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001</x:String>
<x:String x:Key="CaptionButtonPathWindowMaximized">M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 11 6 L 11 2 C 11 0 10 -2 8.011 -1.946 L 7.06 -1.969 L 3 -2 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001</x:String>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MaximizeGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MaximizeGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="MaximizeGlyphContrast" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>
@@ -283,42 +318,46 @@
ResourceKey="CloseButtonBackgroundPointerOver" />
<StaticResource x:Key="CaptionButtonBackgroundPressed"
ResourceKey="CloseButtonBackgroundPressed" />
<StaticResource x:Key="CaptionButtonStrokePointerOver"
ResourceKey="CloseButtonStrokePointerOver" />
<StaticResource x:Key="CaptionButtonStrokePressed"
ResourceKey="CloseButtonStrokePressed" />
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
ResourceKey="CloseButtonForegroundPointerOver" />
<StaticResource x:Key="CaptionButtonForegroundPressed"
ResourceKey="CloseButtonForegroundPressed" />
<StaticResource x:Key="CaptionButtonBackground"
ResourceKey="CloseButtonBackground" />
<StaticResource x:Key="CaptionButtonBackgroundColor"
ResourceKey="CloseButtonBackgroundColor" />
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="CloseGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
ResourceKey="CloseButtonBackgroundPointerOver" />
<StaticResource x:Key="CaptionButtonBackgroundPressed"
ResourceKey="CloseButtonBackgroundPressed" />
<StaticResource x:Key="CaptionButtonStrokePointerOver"
ResourceKey="CloseButtonStrokePointerOver" />
<StaticResource x:Key="CaptionButtonStrokePressed"
ResourceKey="CloseButtonStrokePressed" />
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
ResourceKey="CloseButtonForegroundPointerOver" />
<StaticResource x:Key="CaptionButtonForegroundPressed"
ResourceKey="CloseButtonForegroundPressed" />
<StaticResource x:Key="CaptionButtonBackground"
ResourceKey="CloseButtonBackground" />
<StaticResource x:Key="CaptionButtonBackgroundColor"
ResourceKey="CloseButtonBackgroundColor" />
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="CloseGlyph" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
ResourceKey="CloseButtonBackgroundPointerOver" />
<StaticResource x:Key="CaptionButtonBackgroundPressed"
ResourceKey="CloseButtonBackgroundPressed" />
<StaticResource x:Key="CaptionButtonStrokePointerOver"
ResourceKey="CloseButtonStrokePointerOver" />
<StaticResource x:Key="CaptionButtonStrokePressed"
ResourceKey="CloseButtonStrokePressed" />
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
ResourceKey="CloseButtonForegroundPointerOver" />
<StaticResource x:Key="CaptionButtonForegroundPressed"
ResourceKey="CloseButtonForegroundPressed" />
<StaticResource x:Key="CaptionButtonGlyph"
ResourceKey="CloseGlyphContrast" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<x:String x:Key="CaptionButtonPath">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>

View File

@@ -1019,9 +1019,17 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
if (_profile)
{
if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
{
// For 'automatic', we only care about the connection state if we were launched by Terminal
// Since we were launched via defterm, ignore the connection state (i.e. we treat the
// close on exit mode as 'always', see GH #13325 for discussion)
Close();
}
const auto mode = _profile.CloseOnExit();
if ((mode == CloseOnExitMode::Always) ||
(mode == CloseOnExitMode::Graceful && newConnectionState == ConnectionState::Closed))
((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
{
Close();
}
@@ -1568,11 +1576,12 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// Find what borders need to persist after we close the child
_borders = _GetCommonBorders();
// take the control, profile and id of the pane that _wasn't_ closed.
// take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed.
_control = remainingChild->_control;
_connectionState = remainingChild->_connectionState;
_profile = remainingChild->_profile;
_id = remainingChild->Id();
_isDefTermSession = remainingChild->_isDefTermSession;
// Add our new event handler before revoking the old one.
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
@@ -2464,11 +2473,12 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
}
else
{
// Move our control, guid into the first one.
// Move our control, guid, isDefTermSession into the first one.
_firstChild = std::make_shared<Pane>(_profile, _control);
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
_profile = nullptr;
_control = { nullptr };
_firstChild->_isDefTermSession = _isDefTermSession;
}
_splitState = actualSplitType;
@@ -3108,6 +3118,15 @@ int Pane::GetLeafPaneCount() const noexcept
return _IsLeaf() ? 1 : (_firstChild->GetLeafPaneCount() + _secondChild->GetLeafPaneCount());
}
// Method Description:
// - Should be called when this pane is created via a default terminal handoff
// - Finalizes our configuration given the information that we have been
// created via default handoff
void Pane::FinalizeConfigurationGivenDefault()
{
_isDefTermSession = true;
}
// Method Description:
// - Returns true if the pane or one of its descendants is read-only
bool Pane::ContainsReadOnly() const

View File

@@ -132,6 +132,8 @@ public:
bool FocusPane(const std::shared_ptr<Pane> pane);
std::shared_ptr<Pane> FindPane(const uint32_t id);
void FinalizeConfigurationGivenDefault();
bool ContainsReadOnly() const;
// Method Description:
@@ -212,21 +214,24 @@ private:
winrt::Windows::UI::Xaml::Controls::Grid _root{};
winrt::Windows::UI::Xaml::Controls::Border _borderFirst{};
winrt::Windows::UI::Xaml::Controls::Border _borderSecond{};
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
#pragma region Properties that need to be transferred between child / parent panes upon splitting / closing
std::shared_ptr<Pane> _firstChild{ nullptr };
std::shared_ptr<Pane> _secondChild{ nullptr };
SplitState _splitState{ SplitState::None };
float _desiredSplitPosition;
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
bool _isDefTermSession{ false };
#pragma endregion
std::optional<uint32_t> _id;
std::weak_ptr<Pane> _parentChildPath{};
bool _lastActive{ false };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
winrt::event_token _connectionStateChangedToken{ 0 };
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };

View File

@@ -119,6 +119,7 @@
</resheader>
<data name="AppName" xml:space="preserve">
<value>Terminal</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppNameDev" xml:space="preserve">
<value>Terminal Dev</value>
@@ -126,9 +127,11 @@
</data>
<data name="AppNamePre" xml:space="preserve">
<value>Terminal Preview</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppStoreName" xml:space="preserve">
<value>Windows Terminal</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppStoreNameDev" xml:space="preserve">
<value>Windows Terminal Dev</value>
@@ -136,9 +139,11 @@
</data>
<data name="AppStoreNamePre" xml:space="preserve">
<value>Windows Terminal Preview</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppShortName" xml:space="preserve">
<value>Terminal</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppShortNameDev" xml:space="preserve">
<value>Terminal Dev</value>
@@ -146,6 +151,7 @@
</data>
<data name="AppShortNamePre" xml:space="preserve">
<value>Terminal Preview</value>
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
</data>
<data name="AppDescription" xml:space="preserve">
<value>The New Windows Terminal</value>

View File

@@ -12,6 +12,7 @@
#include "TerminalPage.h"
#include "Utils.h"
#include "../../types/inc/utils.hpp"
#include "../../inc/til/string.h"
#include <LibraryResources.h>
@@ -85,34 +86,7 @@ namespace winrt::TerminalApp::implementation
//
// This call to _MakePane won't return nullptr, we already checked that
// case above with the _maybeElevate call.
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection));
const auto tabCount = _tabs.Size();
const auto usedManualProfile = (newTerminalArgs != nullptr) &&
(newTerminalArgs.ProfileIndex() != nullptr ||
newTerminalArgs.Profile().empty());
// Lookup the name of the color scheme used by this profile.
const auto scheme = _settings.GetColorSchemeForProfile(profile);
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
// that as the empty string.
const auto schemeName = scheme ? scheme.Name() : L"\0";
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"TabInformation",
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
TraceLoggingFloat64(settings.DefaultSettings().Opacity(), "TintOpacity", "Opacity preference from the settings"),
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr, existingConnection));
return S_OK;
}
CATCH_RETURN();
@@ -348,7 +322,7 @@ namespace winrt::TerminalApp::implementation
// In the future, it may be preferable to just duplicate the
// current control's live settings (which will include changes
// made through VT).
_CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
_CreateNewTabFromPane(_MakePane(nullptr, tab, nullptr));
const auto runtimeTabText{ tab.GetTabText() };
if (!runtimeTabText.empty())
@@ -371,7 +345,7 @@ namespace winrt::TerminalApp::implementation
try
{
_SetFocusedTab(tab);
_SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
_SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, tab));
}
CATCH_LOG();
}
@@ -404,7 +378,8 @@ namespace winrt::TerminalApp::implementation
// GH#11356 - we can't use the UWP apis for writing the file,
// because they don't work elevated (shocker) So just use the
// shell32 file picker manually.
path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
std::wstring filename{ tab.Title() };
path = co_await SaveFilePicker(*_hostingHwnd, [filename = std::move(filename)](auto&& dialog) mutable {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
try
{
@@ -418,7 +393,8 @@ namespace winrt::TerminalApp::implementation
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));
// Default to using the tab title as the file name
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
filename = til::clean_filename(filename);
THROW_IF_FAILED(dialog->SetFileName((filename + L".txt").c_str()));
});
}
else

View File

@@ -60,7 +60,8 @@
FontSize="12">
<ToolTipService.ToolTip>
<ToolTip Placement="Mouse">
<TextBlock IsTextSelectionEnabled="False">
<TextBlock IsTextSelectionEnabled="False"
TextWrapping="Wrap">
<Run x:Uid="NewTabRun" /> <LineBreak />
<Run x:Uid="NewPaneRun"
FontStyle="Italic" /> <LineBreak />

View File

@@ -20,17 +20,11 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= XAML files ======================== -->
<ItemGroup>
<!-- HERE BE DRAGONS:
@@ -407,13 +401,6 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.

View File

@@ -511,7 +511,7 @@ namespace winrt::TerminalApp::implementation
"NewTabByDragDrop",
TraceLoggingDescription("Event emitted when the user drag&drops onto the new tab button"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
}
@@ -1178,7 +1178,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The profile's GUID"),
TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
return connection;
}
@@ -1326,6 +1326,29 @@ namespace winrt::TerminalApp::implementation
e.Handled(true);
}
bool TerminalPage::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
{
const auto modifiers = _GetPressedModifierKeys();
if (vkey == VK_SPACE && modifiers.IsAltPressed() && down)
{
if (const auto actionMap = _settings.ActionMap())
{
if (const auto cmd = actionMap.GetActionByKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
modifiers.IsWinPressed(),
gsl::narrow_cast<int32_t>(vkey),
scanCode,
}))
{
return _actionDispatch->DoAction(cmd.ActionAndArgs());
}
}
}
return false;
}
// Method Description:
// - Get the modifier keys that are currently pressed. This can be used to
// find out which modifiers (ctrl, alt, shift) are pressed in events that
@@ -1531,6 +1554,20 @@ namespace winrt::TerminalApp::implementation
}
});
hostingTab.ColorPickerRequested([weakTab, weakThis]() {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab)
{
if (!page->_tabColorPicker)
{
page->_tabColorPicker = winrt::make<ColorPickupFlyout>();
}
tab->AttachColorPicker(page->_tabColorPicker);
}
});
// Add an event handler for when the terminal or tab wants to set a
// progress indicator on the taskbar
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
@@ -2500,8 +2537,8 @@ namespace winrt::TerminalApp::implementation
// - newTerminalArgs: an object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
// - duplicate: a boolean to indicate whether the pane we create should be
// a duplicate of the currently focused pane
// - sourceTab: an optional tab reference that indicates that the created
// pane should be a duplicate of the tab's focused pane
// - existingConnection: optionally receives a connection from the outside
// world instead of attempting to create one
// Return Value:
@@ -2509,29 +2546,25 @@ namespace winrt::TerminalApp::implementation
// connection, then we'll return nullptr. Otherwise, we'll return a new
// Pane for this connection.
std::shared_ptr<Pane> TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs,
const bool duplicate,
const winrt::TerminalApp::TabBase& sourceTab,
TerminalConnection::ITerminalConnection existingConnection)
{
TerminalSettingsCreateResult controlSettings{ nullptr };
Profile profile{ nullptr };
if (duplicate)
if (const auto& terminalTab{ _GetTerminalTabImpl(sourceTab) })
{
const auto focusedTab{ _GetFocusedTabImpl() };
if (focusedTab)
profile = terminalTab->GetFocusedProfile();
if (profile)
{
profile = focusedTab->GetFocusedProfile();
if (profile)
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = terminalTab->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}
}
}
@@ -3264,13 +3297,18 @@ namespace winrt::TerminalApp::implementation
{
NewTerminalArgs newTerminalArgs;
newTerminalArgs.Commandline(connection.Commandline());
newTerminalArgs.TabTitle(connection.StartingTitle());
// GH #12370: We absolutely cannot allow a defterm connection to
// auto-elevate. Defterm doesn't work for elevated scenarios in the
// first place. If we try accepting the connection, the spawning an
// elevated version of the Terminal with that profile... that's a
// recipe for disaster. We won't ever open up a tab in this window.
newTerminalArgs.Elevate(false);
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, connection));
const auto newPane = _MakePane(newTerminalArgs, nullptr, connection);
newPane->WalkTree([](auto pane) {
pane->FinalizeConfigurationGivenDefault();
});
_CreateNewTabFromPane(newPane);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
@@ -3457,7 +3495,6 @@ namespace winrt::TerminalApp::implementation
if (const auto infoBar = FindName(L"SetAsDefaultInfoBar").try_as<MUX::Controls::InfoBar>())
{
TraceLoggingWrite(g_hTerminalAppProvider, "SetAsDefaultTipPresented", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
infoBar.IsOpen(true);
}
}
@@ -3482,7 +3519,7 @@ namespace winrt::TerminalApp::implementation
return winrt::hstring{ TabletInputServiceKey };
}
wil::unique_schandle hManager{ OpenSCManager(nullptr, nullptr, 0) };
wil::unique_schandle hManager{ OpenSCManagerW(nullptr, nullptr, 0) };
if (LOG_LAST_ERROR_IF(!hManager.is_valid()))
{
@@ -3490,15 +3527,24 @@ namespace winrt::TerminalApp::implementation
}
DWORD cchBuffer = 0;
GetServiceDisplayName(hManager.get(), TabletInputServiceKey.data(), nullptr, &cchBuffer);
const auto ok = GetServiceDisplayNameW(hManager.get(), TabletInputServiceKey.data(), nullptr, &cchBuffer);
// Windows 11 doesn't have a TabletInputService.
// (It was renamed to TextInputManagementService, because people kept thinking that a
// service called "tablet-something" is system-irrelevant on PCs and can be disabled.)
if (ok || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return winrt::hstring{ TabletInputServiceKey };
}
std::wstring buffer;
cchBuffer += 1; // Add space for a null
buffer.resize(cchBuffer);
if (LOG_LAST_ERROR_IF(!GetServiceDisplayName(hManager.get(),
TabletInputServiceKey.data(),
buffer.data(),
&cchBuffer)))
if (LOG_LAST_ERROR_IF(!GetServiceDisplayNameW(hManager.get(),
TabletInputServiceKey.data(),
buffer.data(),
&cchBuffer)))
{
return winrt::hstring{ TabletInputServiceKey };
}

View File

@@ -135,6 +135,8 @@ namespace winrt::TerminalApp::implementation
void OpenSettingsUI();
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
@@ -171,6 +173,7 @@ namespace winrt::TerminalApp::implementation
TerminalApp::TabRowControl _tabRow{ nullptr };
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
Microsoft::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
winrt::TerminalApp::ColorPickupFlyout _tabColorPicker{ nullptr };
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
@@ -378,7 +381,7 @@ namespace winrt::TerminalApp::implementation
const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection);
std::shared_ptr<Pane> _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr,
const bool duplicate = false,
const winrt::TerminalApp::TabBase& sourceTab = nullptr,
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
void _RefreshUIForSettingsReload();

View File

@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TaskbarState.idl";
import "IDirectKeyListener.idl";
namespace TerminalApp
{
@@ -16,7 +17,7 @@ namespace TerminalApp
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
};
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
{
TerminalPage();

View File

@@ -648,6 +648,46 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Attaches the given color picker to ourselves
// - Typically will be called after we have sent a request for the color picker
// Arguments:
// - colorPicker: The color picker that we should attach to ourselves
// Return Value:
// - <none>
void TerminalTab::AttachColorPicker(TerminalApp::ColorPickupFlyout& colorPicker)
{
auto weakThis{ get_weak() };
_tabColorPickup = colorPicker;
_colorSelectedToken = _tabColorPickup.ColorSelected([weakThis](auto newTabColor) {
if (auto tab{ weakThis.get() })
{
tab->SetRuntimeTabColor(newTabColor);
}
});
_colorClearedToken = _tabColorPickup.ColorCleared([weakThis]() {
if (auto tab{ weakThis.get() })
{
tab->ResetRuntimeTabColor();
}
});
_pickerClosedToken = _tabColorPickup.Closed([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_tabColorPickup.ColorSelected(tab->_colorSelectedToken);
tab->_tabColorPickup.ColorCleared(tab->_colorClearedToken);
tab->_tabColorPickup.Closed(tab->_pickerClosedToken);
tab->_tabColorPickup = nullptr;
}
});
_tabColorPickup.ShowAt(TabViewItem());
}
// Method Description:
// - Find the currently active pane, and then switch the split direction of
// its parent. E.g. switch from Horizontal to Vertical.
@@ -1184,27 +1224,12 @@ namespace winrt::TerminalApp::implementation
chooseColorMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->ActivateColorPicker();
tab->RequestColorPicker();
}
});
chooseColorMenuItem.Text(RS_(L"TabColorChoose"));
chooseColorMenuItem.Icon(colorPickSymbol);
// Color Picker (it's convenient to have it here)
_tabColorPickup.ColorSelected([weakThis](auto newTabColor) {
if (auto tab{ weakThis.get() })
{
tab->SetRuntimeTabColor(newTabColor);
}
});
_tabColorPickup.ColorCleared([weakThis]() {
if (auto tab{ weakThis.get() })
{
tab->ResetRuntimeTabColor();
}
});
Controls::MenuFlyoutItem renameTabMenuItem;
{
// "Rename Tab"
@@ -1452,18 +1477,16 @@ namespace winrt::TerminalApp::implementation
subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
}
hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color));
selectedTabBrush.Color(color);
// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit of transparency
auto deselectedTabColor = color;
deselectedTabColor.A = 64;
deselectedTabBrush.Color(deselectedTabColor);
deselectedTabBrush.Color(color);
deselectedTabBrush.Opacity(0.3);
hoverTabBrush.Color(color);
hoverTabBrush.Opacity(0.6);
// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit of transparency
//
// Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
// use TabViewItem().Background() for that. HOWEVER,
// TabViewItem().Background() only sets the color of the tab background
@@ -1568,14 +1591,15 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Display the tab color picker at the location of the TabViewItem for this tab.
// - Send an event to request for the color picker
// - The listener should attach the color picker via AttachColorPicker()
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalTab::ActivateColorPicker()
void TerminalTab::RequestColorPicker()
{
_tabColorPickup.ShowAt(TabViewItem());
_ColorPickerRequestedHandlers();
}
// Method Description:

View File

@@ -37,6 +37,8 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> DetachPane();
void AttachPane(std::shared_ptr<Pane> pane);
void AttachColorPicker(winrt::TerminalApp::ColorPickupFlyout& colorPicker);
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
@@ -73,7 +75,7 @@ namespace winrt::TerminalApp::implementation
void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
void ResetRuntimeTabColor();
void ActivateColorPicker();
void RequestColorPicker();
void UpdateZoom(std::shared_ptr<Pane> newFocus);
void ToggleZoom();
@@ -104,6 +106,7 @@ namespace winrt::TerminalApp::implementation
WINRT_CALLBACK(SplitTabRequested, winrt::delegate<>);
WINRT_CALLBACK(FindRequested, winrt::delegate<>);
WINRT_CALLBACK(ExportTabRequested, winrt::delegate<>);
WINRT_CALLBACK(ColorPickerRequested, winrt::delegate<>);
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
private:
@@ -112,12 +115,16 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> _zoomedPane{ nullptr };
winrt::hstring _lastIconPath{};
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};
winrt::TerminalApp::TabHeaderControl _headerControl{};
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{ nullptr };
winrt::event_token _colorSelectedToken;
winrt::event_token _colorClearedToken;
winrt::event_token _pickerClosedToken;
struct ControlEventTokens
{
winrt::event_token titleToken;

View File

@@ -15,6 +15,7 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -92,14 +93,6 @@
</Reference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>

View File

@@ -264,13 +264,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_state == AzureState::TermConnected)
{
// Close the websocket connection
auto closedTask = _cloudShellSocket.close();
closedTask.wait();
_cloudShellSocket.close();
}
if (_hOutputThread)
{
// Tear down our output thread
// Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers()
// calls have returned and won't notify our caller (ControlCore) anymore. This ensures that
// we don't call a destroyed event handler asynchronously from a background thread (GH#13880).
WaitForSingleObject(_hOutputThread.get(), INFINITE);
_hOutputThread.reset();
}

View File

@@ -53,7 +53,12 @@ CATCH_RETURN()
HRESULT CTerminalHandoff::s_StopListening()
{
std::unique_lock lock{ _mtx };
return s_StopListeningLocked();
}
// See s_StopListening()
HRESULT CTerminalHandoff::s_StopListeningLocked()
{
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
_pfnHandoff = nullptr;
@@ -97,18 +102,20 @@ static HRESULT _duplicateHandle(const HANDLE in, HANDLE& out) noexcept
// - E_NOT_VALID_STATE if a event handler is not registered before calling. `::DuplicateHandle`
// error codes if we cannot manage to make our own copy of handles to retain. Or S_OK/error
// from the registered handler event function.
HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client)
HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client, TERMINAL_STARTUP_INFO startupInfo)
{
try
{
// Stash a local copy of _pfnHandoff before we stop listening.
std::unique_lock lock{ _mtx };
// s_StopListeningLocked sets _pfnHandoff to nullptr.
// localPfnHandoff is tested for nullness below.
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
auto localPfnHandoff = _pfnHandoff;
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
// COM does not automatically clean that up for us. We must do it.
s_StopListening();
std::unique_lock lock{ _mtx };
LOG_IF_FAILED(s_StopListeningLocked());
// Report an error if no one registered a handoff function before calling this.
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
@@ -125,7 +132,7 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign
THROW_IF_FAILED(_duplicateHandle(client, client));
// Call registered handler from when we started listening.
THROW_IF_FAILED(localPfnHandoff(in, out, signal, ref, server, client));
THROW_IF_FAILED(localPfnHandoff(in, out, signal, ref, server, client, startupInfo));
#pragma warning(suppress : 26477)
TraceLoggingWrite(

View File

@@ -26,10 +26,10 @@ Author(s):
#define __CLSID_CTerminalHandoff "051F34EE-C1FD-4B19-AF75-9BA54648434C"
#endif
using NewHandoffFunction = HRESULT (*)(HANDLE, HANDLE, HANDLE, HANDLE, HANDLE, HANDLE);
using NewHandoffFunction = HRESULT (*)(HANDLE, HANDLE, HANDLE, HANDLE, HANDLE, HANDLE, TERMINAL_STARTUP_INFO);
struct __declspec(uuid(__CLSID_CTerminalHandoff))
CTerminalHandoff : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITerminalHandoff>
CTerminalHandoff : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITerminalHandoff2>
{
#pragma region ITerminalHandoff
STDMETHODIMP EstablishPtyHandoff(HANDLE in,
@@ -37,12 +37,16 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
HANDLE signal,
HANDLE ref,
HANDLE server,
HANDLE client) override;
HANDLE client,
TERMINAL_STARTUP_INFO startupInfo) override;
#pragma endregion
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
static HRESULT s_StopListening();
private:
static HRESULT s_StopListeningLocked();
};
// Disable warnings from the CoCreatableClass macro as the value it provides for

View File

@@ -192,7 +192,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
return S_OK;
}
@@ -203,7 +203,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const HANDLE hOut,
const HANDLE hRef,
const HANDLE hServerProcess,
const HANDLE hClientProcess) :
const HANDLE hClientProcess,
TERMINAL_STARTUP_INFO startupInfo) :
_initialRows{ 25 },
_initialCols{ 80 },
_guid{ Utils::CreateGuid() },
@@ -213,6 +214,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC));
_piClient.hProcess = hClientProcess;
_startupInfo.title = winrt::hstring{ startupInfo.pszTitle, SysStringLen(startupInfo.pszTitle) };
_startupInfo.iconPath = winrt::hstring{ startupInfo.pszIconPath, SysStringLen(startupInfo.pszIconPath) };
_startupInfo.iconIndex = startupInfo.iconIndex;
try
{
_commandline = _commandlineFromProcess(hClientProcess);
@@ -288,6 +293,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return _commandline;
}
winrt::hstring ConptyConnection::StartingTitle() const
{
return _startupInfo.title;
}
void ConptyConnection::Start()
try
{
@@ -336,7 +346,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), til::unwrap_coord_size(dimensions)));
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
@@ -535,28 +545,34 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_transitionToState(ConnectionState::Closing))
{
// EXIT POINT
_clientExitWait.reset(); // immediately stop waiting for the client to exit.
// _clientExitWait holds a CreateThreadpoolWait() which holds a weak reference to "this".
// This manual reset() ensures we wait for it to be teared down via WaitForThreadpoolWaitCallbacks().
_clientExitWait.reset();
_hPC.reset(); // tear down the pseudoconsole (this is like clicking X on a console window)
// CloseHandle() on pipes blocks until any current WriteFile()/ReadFile() has returned.
// CancelSynchronousIo prevents us from deadlocking ourselves.
// At this point in Close(), _inPipe won't be used anymore since the UI parts are torn down.
// _outPipe is probably still stuck in ReadFile() and might currently be written to.
if (_hOutputThread)
{
CancelSynchronousIo(_hOutputThread.get());
}
_inPipe.reset(); // break the pipes
_outPipe.reset();
if (_hOutputThread)
{
// Tear down our output thread -- now that the output pipe was closed on the
// far side, we can run down our local reader.
// Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers()
// calls have returned and won't notify our caller (ControlCore) anymore. This ensures that
// we don't call a destroyed event handler asynchronously from a background thread (GH#13880).
LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(_hOutputThread.get(), INFINITE));
_hOutputThread.reset();
}
if (_piClient.hProcess)
{
// Wait for the client to terminate (which it should do successfully)
LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(_piClient.hProcess, INFINITE));
_piClient.reset();
}
_transitionToState(ConnectionState::Closed);
}
}
@@ -606,10 +622,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
DWORD read{};
const auto readFail{ !ReadFile(_outPipe.get(), _buffer.data(), gsl::narrow_cast<DWORD>(_buffer.size()), &read, nullptr) };
// When we call CancelSynchronousIo() in Close() this is the branch that's taken and gets us out of here.
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return 0;
}
if (readFail) // reading failed (we must check this first, because read will also be 0.)
{
const auto lastError = GetLastError();
if (lastError != ERROR_BROKEN_PIPE && !_isStateAtOrBeyond(ConnectionState::Closing))
if (lastError != ERROR_BROKEN_PIPE)
{
// EXIT POINT
_indicateExitWithStatus(HRESULT_FROM_WIN32(lastError)); // print a message
@@ -622,12 +645,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
if (FAILED(result))
{
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
// This termination was expected.
return 0;
}
// EXIT POINT
_indicateExitWithStatus(result); // print a message
_transitionToState(ConnectionState::Failed);
@@ -667,10 +684,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
winrt::event_token ConptyConnection::NewConnection(const NewConnectionHandler& handler) { return _newConnectionHandlers.add(handler); };
void ConptyConnection::NewConnection(const winrt::event_token& token) { _newConnectionHandlers.remove(token); };
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept
HRESULT ConptyConnection::NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client, TERMINAL_STARTUP_INFO startupInfo) noexcept
try
{
_newConnectionHandlers(winrt::make<ConptyConnection>(signal, in, out, ref, server, client));
_newConnectionHandlers(winrt::make<ConptyConnection>(signal, in, out, ref, server, client, startupInfo));
return S_OK;
}

View File

@@ -8,10 +8,12 @@
#include <conpty-static.h>
#include "ITerminalHandoff.h"
namespace wil
{
// These belong in WIL upstream, so when we reingest the change that has them we'll get rid of ours.
using unique_static_pseudoconsole_handle = wil::unique_any<HPCON, decltype(&::ConptyClosePseudoConsole), ::ConptyClosePseudoConsole>;
using unique_static_pseudoconsole_handle = wil::unique_any<HPCON, decltype(&::ConptyClosePseudoConsole), ::ConptyClosePseudoConsoleNoWait>;
}
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
@@ -23,7 +25,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const HANDLE hOut,
const HANDLE hRef,
const HANDLE hServerProcess,
const HANDLE hClientProcess);
const HANDLE hClientProcess,
TERMINAL_STARTUP_INFO startupInfo);
ConptyConnection() noexcept = default;
void Initialize(const Windows::Foundation::Collections::ValueSet& settings);
@@ -42,6 +45,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
winrt::guid Guid() const noexcept;
winrt::hstring Commandline() const;
winrt::hstring StartingTitle() const;
static void StartInboundListener();
static void StopInboundListener();
@@ -60,7 +64,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private:
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client) noexcept;
static HRESULT NewHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client, TERMINAL_STARTUP_INFO startupInfo) noexcept;
static winrt::hstring _commandlineFromProcess(HANDLE process);
HRESULT _LaunchAttachedClient() noexcept;
@@ -93,6 +97,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::array<char, 4096> _buffer{};
bool _passthroughMode{};
struct StartupInfoFromDefTerm
{
winrt::hstring title{};
winrt::hstring iconPath{};
int32_t iconIndex{};
} _startupInfo{};
DWORD _OutputThread();
};
}

View File

@@ -12,6 +12,7 @@ namespace Microsoft.Terminal.TerminalConnection
ConptyConnection();
Guid Guid { get; };
String Commandline { get; };
String StartingTitle { get; };
void ClearBuffer();

View File

@@ -89,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_settings = winrt::make_self<implementation::ControlSettings>(settings, unfocusedAppearance);
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
_terminal = std::make_shared<::Microsoft::Terminal::Core::Terminal>();
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
@@ -195,13 +195,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
});
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<>>(
_dispatcher,
// NOTE: Calling UpdatePatternLocations from a background
// thread is a workaround for us to hit GH#12607 less often.
_updatePatternLocations = std::make_unique<til::throttled_func_trailing<>>(
UpdatePatternLocationsInterval,
[weakThis = get_weak()]() {
if (auto core{ weakThis.get() }; !core->_IsClosing())
[weakTerminal = std::weak_ptr{ _terminal }]() {
if (const auto t = weakTerminal.lock())
{
core->UpdatePatternLocations();
auto lock = t->LockForWriting();
t->UpdatePatternsUnderLock();
}
});
@@ -389,6 +391,55 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal->SendCharEvent(ch, scanCode, modifiers);
}
bool ControlCore::_shouldTryUpdateSelection(const WORD vkey)
{
// GH#6423 - don't update selection if the key that was pressed was a
// modifier key. We'll wait for a real keystroke to dismiss the
// GH #7395 - don't update selection when taking PrintScreen
// selection.
return HasSelection() && !KeyEvent::IsModifierKey(vkey) && vkey != VK_SNAPSHOT;
}
bool ControlCore::TryMarkModeKeybinding(const WORD vkey,
const ::Microsoft::Terminal::Core::ControlKeyStates mods)
{
if (_shouldTryUpdateSelection(vkey) && _terminal->SelectionMode() == ::Terminal::SelectionInteractionMode::Mark)
{
if (vkey == 'A' && !mods.IsAltPressed() && !mods.IsShiftPressed() && mods.IsCtrlPressed())
{
// Ctrl + A --> Select all
auto lock = _terminal->LockForWriting();
_terminal->SelectAll();
_updateSelectionUI();
return true;
}
else if (vkey == VK_RETURN && !mods.IsCtrlPressed() && !mods.IsAltPressed())
{
// [Shift +] Enter --> copy text
// Don't lock here! CopySelectionToClipboard already locks for you!
CopySelectionToClipboard(mods.IsShiftPressed(), nullptr);
_terminal->ClearSelection();
_updateSelectionUI();
return true;
}
else if (vkey == VK_ESCAPE)
{
_terminal->ClearSelection();
_updateSelectionUI();
return true;
}
else if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(mods, vkey) })
{
// try to update the selection
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, mods);
_updateSelectionUI();
return true;
}
}
return false;
}
// Method Description:
// - Send this particular key event to the terminal.
// See Terminal::SendKeyEvent for more information.
@@ -405,27 +456,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const bool keyDown)
{
// Update the selection, if it's present
// GH#6423 - don't dismiss selection if the key that was pressed was a
// modifier key. We'll wait for a real keystroke to dismiss the
// GH #7395 - don't dismiss selection when taking PrintScreen
// selection.
// GH#8522, GH#3758 - Only modify the selection on key _down_. If we
// modify on key up, then there's chance that we'll immediately dismiss
// a selection created by an action bound to a keydown.
if (HasSelection() &&
!KeyEvent::IsModifierKey(vkey) &&
vkey != VK_SNAPSHOT &&
keyDown)
if (_shouldTryUpdateSelection(vkey) && keyDown)
{
const auto isInMarkMode = _terminal->SelectionMode() == ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Mark;
if (isInMarkMode && modifiers.IsCtrlPressed() && vkey == 'A')
{
auto lock = _terminal->LockForWriting();
_terminal->SelectAll();
_updateSelectionUI();
return true;
}
// try to update the selection
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
@@ -479,7 +514,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// itself - it was initiated by the mouse wheel, or the scrollbar.
_terminal->UserScrollViewport(viewTop);
_updatePatternLocations->Run();
(*_updatePatternLocations)();
}
void ControlCore::AdjustOpacity(const double adjustment)
@@ -555,16 +590,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderer->TriggerRedrawAll();
}
// Method Description:
// - Tell TerminalCore to update its knowledge about the locations of visible regex patterns
// - We should call this (through the throttled function) when something causes the visible
// region to change, such as when new text enters the buffer or the viewport is scrolled
void ControlCore::UpdatePatternLocations()
{
auto lock = _terminal->LockForWriting();
_terminal->UpdatePatternsUnderLock();
}
// Method description:
// - Updates last hovered cell, renders / removes rendering of hyper-link if required
// Arguments:
@@ -634,7 +659,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
if (_lastHoveredCell.has_value())
{
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
auto uri{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
uri.resize(std::min<size_t>(1024u, uri.size())); // Truncate for display
return winrt::hstring{ uri };
}
return {};
}
@@ -860,6 +887,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::_refreshSizeUnderLock()
{
if (_IsClosing())
{
return;
}
auto cx = gsl::narrow_cast<til::CoordType>(_panelWidth * _compositionScale);
auto cy = gsl::narrow_cast<til::CoordType>(_panelHeight * _compositionScale);
@@ -1094,6 +1126,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_terminal->IsSelectionActive())
{
_terminal->SwitchSelectionEndpoint();
_updateSelectionUI();
return true;
}
return false;
@@ -1280,7 +1313,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
// Additionally, start the throttled update of where our links are.
_updatePatternLocations->Run();
(*_updatePatternLocations)();
}
void ControlCore::_terminalCursorPositionChanged()
@@ -1341,7 +1374,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (!_midiAudio)
{
_midiAudio = std::make_unique<MidiAudio>();
const auto windowHandle = reinterpret_cast<HWND>(_owningHwnd);
_midiAudio = std::make_unique<MidiAudio>(windowHandle);
_midiAudio->Initialize();
}
return *_midiAudio;
@@ -1440,32 +1474,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_FoundMatchHandlers(*this, *foundResults);
}
// Method Description:
// - Asynchronously close our connection. The Connection will likely wait
// until the attached process terminates before Close returns. If that's
// the case, we don't want to block the UI thread waiting on that process
// handle.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget ControlCore::_asyncCloseConnection()
{
if (auto localConnection{ std::exchange(_connection, nullptr) })
{
// Close the connection on the background thread.
co_await winrt::resume_background(); // ** DO NOT INTERACT WITH THE CONTROL CORE AFTER THIS LINE **
// Here, the ControlCore very well might be gone.
// _asyncCloseConnection is called on the dtor, so it's entirely
// possible that the background thread is resuming after we've been
// cleaned up.
localConnection.Close();
// connection is destroyed.
}
}
void ControlCore::Close()
{
if (!_IsClosing())
@@ -1475,13 +1483,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Stop accepting new output and state changes before we disconnect everything.
_connection.TerminalOutput(_connectionOutputEventToken);
_connectionStateChangedRevoker.revoke();
// GH#1996 - Close the connection asynchronously on a background
// thread.
// Since TermControl::Close is only ever triggered by the UI, we
// don't really care to wait for the connection to be completely
// closed. We can just do it whenever.
_asyncCloseConnection();
_connection.Close();
}
}
@@ -1569,7 +1571,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
auto lock = _terminal->LockForReading();
return _terminal->GetCursorPosition().to_core_point();
return _terminal->GetViewportRelativeCursorPosition().to_core_point();
}
// This one's really pushing the boundary of what counts as "encapsulation".
@@ -1673,7 +1675,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->Write(hstr);
// Start the throttled update of where our hyperlinks are.
_updatePatternLocations->Run();
(*_updatePatternLocations)();
}
catch (...)
{
@@ -1911,13 +1913,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - This is related to work done for GH#2988.
void ControlCore::GotFocus()
{
_terminal->FocusChanged(true);
_focusChanged(true);
}
// See GotFocus.
void ControlCore::LostFocus()
{
_terminal->FocusChanged(false);
_focusChanged(false);
}
void ControlCore::_focusChanged(bool focused)
{
// GH#13461 - temporarily turn off read-only mode, send the focus event,
// then turn it back on. Even in focus mode, focus events are fine to
// send. We don't want to pop a warning every time the control is
// focused.
const auto previous = std::exchange(_isReadOnly, false);
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
_terminal->FocusChanged(focused);
}
bool ControlCore::_isBackgroundTransparent()

View File

@@ -87,6 +87,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ToggleMarkMode();
Control::SelectionInteractionMode SelectionMode() const;
bool SwitchSelectionEndpoint();
bool TryMarkModeKeybinding(const WORD vkey,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void GotFocus();
void LostFocus();
@@ -95,7 +97,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void AdjustOpacity(const double adjustment);
void ResumeRendering();
void UpdatePatternLocations();
void SetHoveredCell(Core::Point terminalPosition);
void ClearHoveredCell();
winrt::hstring GetHyperlink(const Core::Point position) const;
@@ -230,7 +231,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::com_ptr<ControlSettings> _settings{ nullptr };
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr };
std::shared_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr };
// NOTE: _renderEngine must be ordered before _renderer.
//
@@ -265,15 +266,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
std::unique_ptr<til::throttled_func_trailing<>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
winrt::fire_and_forget _asyncCloseConnection();
bool _setFontSizeUnderLock(int fontSize);
void _updateFont(const bool initialUpdate = false);
void _refreshSizeUnderLock();
void _updateSelectionUI();
bool _shouldTryUpdateSelection(const WORD vkey);
void _sendInputToConnection(std::wstring_view wstr);
@@ -311,6 +311,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _setOpacity(const double opacity);
bool _isBackgroundTransparent();
void _focusChanged(bool focused);
inline bool _IsClosing() const noexcept
{

View File

@@ -14,9 +14,7 @@ namespace Microsoft.Terminal.Control
// !! LOAD BEARING !! If you make this a struct with Booleans (like they
// make the most sense as), then the app will crash trying to toss one of
// these across the process boundary. I haven't the damndest idea why.
[flags]
enum MouseButtonState
{
[flags] enum MouseButtonState {
IsLeftButtonDown = 0x1,
IsMiddleButtonDown = 0x2,
IsRightButtonDown = 0x4
@@ -37,9 +35,7 @@ namespace Microsoft.Terminal.Control
Mark
};
[flags]
enum SelectionEndpointTarget
{
[flags] enum SelectionEndpointTarget {
Start = 0x1,
End = 0x2
};
@@ -79,13 +75,15 @@ namespace Microsoft.Terminal.Control
Double Opacity { get; };
Boolean UseAcrylic { get; };
Boolean TryMarkModeKeybinding(Int16 vkey,
Microsoft.Terminal.Core.ControlKeyStates modifiers);
Boolean TrySendKeyEvent(Int16 vkey,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean keyDown);
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean keyDown);
Boolean SendCharEvent(Char ch,
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers);
Int16 scanCode,
Microsoft.Terminal.Core.ControlKeyStates modifiers);
void SendInput(String text);
void PasteText(String text);
void SelectAll();
@@ -109,7 +107,6 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Core.Point CursorPosition { get; };
void ResumeRendering();
void BlinkAttributeTick();
void UpdatePatternLocations();
void Search(String text, Boolean goForward, Boolean caseSensitive);
Microsoft.Terminal.Core.Color BackgroundColor { get; };

View File

@@ -5,8 +5,6 @@
#include "TSFInputControl.h"
#include "TSFInputControl.g.cpp"
#include <Utils.h>
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Graphics::Display;
using namespace winrt::Windows::UI::Core;
@@ -16,17 +14,7 @@ using namespace winrt::Windows::UI::Xaml;
namespace winrt::Microsoft::Terminal::Control::implementation
{
TSFInputControl::TSFInputControl() :
_editContext{ nullptr },
_inComposition{ false },
_activeTextStart{ 0 },
_focused{ false },
_currentTerminalCursorPos{ 0, 0 },
_currentCanvasWidth{ 0.0 },
_currentTextBlockHeight{ 0.0 },
_currentTextBounds{ 0, 0, 0, 0 },
_currentControlBounds{ 0, 0, 0, 0 },
_currentWindowBounds{ 0, 0, 0, 0 }
TSFInputControl::TSFInputControl()
{
InitializeComponent();
@@ -83,11 +71,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TSFInputControl::NotifyFocusEnter()
{
if (_editContext != nullptr)
{
_editContext.NotifyFocusEnter();
_focused = true;
}
_editContext.NotifyFocusEnter();
_focused = true;
}
// Method Description:
@@ -99,11 +84,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TSFInputControl::NotifyFocusLeave()
{
if (_editContext != nullptr)
{
_editContext.NotifyFocusLeave();
_focused = false;
}
_editContext.NotifyFocusLeave();
_focused = false;
}
// Method Description:
@@ -117,14 +99,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (!_inputBuffer.empty())
{
TextBlock().Text(L"");
const auto bufLen = ::base::ClampedNumeric<int32_t>(_inputBuffer.length());
_inputBuffer.clear();
_editContext.NotifyFocusLeave();
_editContext.NotifyTextChanged({ 0, bufLen }, 0, { 0, 0 });
_editContext.NotifyFocusEnter();
_selection = {};
_activeTextStart = 0;
_inComposition = false;
_editContext.NotifyTextChanged({ 0, INT32_MAX }, 0, _selection);
TextBlock().Text({});
}
}
@@ -303,12 +282,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TSFInputControl::_compositionCompletedHandler(CoreTextEditContext sender, const CoreTextCompositionCompletedEventArgs& /*args*/)
{
_inComposition = false;
// only need to do work if the current buffer has text
if (!_inputBuffer.empty())
{
_SendAndClearText();
}
_SendAndClearText();
}
// Method Description:
@@ -336,16 +310,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TSFInputControl::_textRequestedHandler(CoreTextEditContext sender, const CoreTextTextRequestedEventArgs& args)
{
// the range the TSF wants to know about
const auto range = args.Request().Range();
try
{
const auto textEnd = ::base::ClampMin<size_t>(range.EndCaretPosition, _inputBuffer.length());
const auto length = ::base::ClampSub<size_t>(textEnd, range.StartCaretPosition);
const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, length);
args.Request().Text(textRequested);
const auto range = args.Request().Range();
const auto text = _inputBuffer.substr(
range.StartCaretPosition,
range.EndCaretPosition - range.StartCaretPosition);
args.Request().Text(text);
}
CATCH_LOG();
}
@@ -360,8 +331,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - args: CoreTextSelectionRequestedEventArgs for providing data for the SelectionRequested event. Not used in method.
// Return Value:
// - <none>
void TSFInputControl::_selectionRequestedHandler(CoreTextEditContext sender, const CoreTextSelectionRequestedEventArgs& /*args*/)
void TSFInputControl::_selectionRequestedHandler(CoreTextEditContext sender, const CoreTextSelectionRequestedEventArgs& args)
{
args.Request().Selection(_selection);
}
// Method Description:
@@ -374,8 +346,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - args: CoreTextSelectionUpdatingEventArgs for providing data for the SelectionUpdating event. Not used in method.
// Return Value:
// - <none>
void TSFInputControl::_selectionUpdatingHandler(CoreTextEditContext sender, const CoreTextSelectionUpdatingEventArgs& /*args*/)
void TSFInputControl::_selectionUpdatingHandler(CoreTextEditContext sender, const CoreTextSelectionUpdatingEventArgs& args)
{
_selection = args.Selection();
args.Result(CoreTextSelectionUpdatingResult::Succeeded);
}
// Method Description:
@@ -388,24 +362,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TSFInputControl::_textUpdatingHandler(CoreTextEditContext sender, const CoreTextTextUpdatingEventArgs& args)
{
const auto incomingText = args.Text();
const auto range = args.Range();
try
{
// When a user deletes the last character in their current composition, some machines
// will fire a CompositionCompleted before firing a TextUpdating event that deletes the last character.
// The TextUpdating will have a lower StartCaretPosition, so in this scenario, _activeTextStart
// needs to update to be the StartCaretPosition.
// A known issue related to this behavior is that the last character that's deleted from a composition
// will get sent to the terminal before we receive the TextUpdate to delete the character.
// See GH #5054.
_activeTextStart = ::base::ClampMin(_activeTextStart, ::base::ClampedNumeric<size_t>(range.StartCaretPosition));
const auto incomingText = args.Text();
const auto range = args.Range();
_inputBuffer = _inputBuffer.replace(
range.StartCaretPosition,
::base::ClampSub<size_t>(range.EndCaretPosition, range.StartCaretPosition),
range.EndCaretPosition - range.StartCaretPosition,
incomingText);
_selection = args.NewSelection();
// GH#5054: Pressing backspace might move the caret before the _activeTextStart.
_activeTextStart = std::min(_activeTextStart, _inputBuffer.size());
// Emojis/Kaomojis/Symbols chosen through the IME without starting composition
// will be sent straight through to the terminal.
@@ -432,22 +400,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
// Method Description:
// - Send the portion of the textBuffer starting at _activeTextStart to the end of the buffer.
// Then clear the TextBlock and hide it until the next time text is received.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TSFInputControl::_SendAndClearText()
{
const auto text = _inputBuffer.substr(_activeTextStart);
if (text.empty())
{
return;
}
_CompositionCompletedHandlers(text);
_activeTextStart = _inputBuffer.length();
_activeTextStart = _inputBuffer.size();
TextBlock().Text(L"");
TextBlock().Text({});
// After we reset the TextBlock to empty string, we want to make sure
// ActualHeight reflects the respective height. It seems that ActualHeight

View File

@@ -58,6 +58,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _textUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextTextUpdatingEventArgs& args);
void _formatUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextFormatUpdatingEventArgs& args);
void _SendAndClearText();
void _RedrawCanvas();
winrt::Windows::UI::Text::Core::CoreTextEditContext::TextRequested_revoker _textRequestedRevoker;
winrt::Windows::UI::Text::Core::CoreTextEditContext::SelectionRequested_revoker _selectionRequestedRevoker;
winrt::Windows::UI::Text::Core::CoreTextEditContext::FocusRemoved_revoker _focusRemovedRevoker;
@@ -68,22 +71,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionStarted_revoker _compositionStartedRevoker;
winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionCompleted_revoker _compositionCompletedRevoker;
Windows::UI::Text::Core::CoreTextEditContext _editContext;
Windows::UI::Text::Core::CoreTextEditContext _editContext{ nullptr };
std::wstring _inputBuffer;
winrt::Windows::UI::Text::Core::CoreTextRange _selection{};
size_t _activeTextStart = 0;
bool _inComposition = false;
bool _focused = false;
bool _inComposition;
size_t _activeTextStart;
void _SendAndClearText();
void _RedrawCanvas();
bool _focused;
til::point _currentTerminalCursorPos;
double _currentCanvasWidth;
double _currentTextBlockHeight;
winrt::Windows::Foundation::Rect _currentControlBounds;
winrt::Windows::Foundation::Rect _currentTextBounds;
winrt::Windows::Foundation::Rect _currentWindowBounds;
til::point _currentTerminalCursorPos{};
double _currentCanvasWidth = 0.0;
double _currentTextBlockHeight = 0.0;
winrt::Windows::Foundation::Rect _currentControlBounds{};
winrt::Windows::Foundation::Rect _currentTextBounds{};
winrt::Windows::Foundation::Rect _currentWindowBounds{};
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@@ -432,12 +432,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// achieve the intended effect.
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
ScrollBar().Visibility(Visibility::Collapsed);
ScrollMarksGrid().Visibility(Visibility::Collapsed);
}
else // (default or Visible)
{
// Default behavior
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
ScrollBar().Visibility(Visibility::Visible);
ScrollMarksGrid().Visibility(Visibility::Visible);
}
_interactivity.UpdateSettings();
@@ -677,7 +679,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
margins.Top,
margins.Right,
margins.Bottom };
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(get_strong(), padding, interactivityAutoPeer);
return _automationPeer;
}
}
@@ -902,6 +904,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
// GH#11479: TSF wants to be notified of any character input via ICoreTextEditContext::NotifyTextChanged().
// TSF is built and tested around the idea that you inform it of any text changes that happen
// when it doesn't currently compose characters. For instance writing "xin chaof" with the
// Vietnamese IME should produce "xin chào". After writing "xin" it'll emit a composition
// completion event and we'll write "xin" to the shell. It now has no input focus and won't know
// about the whitespace. If you then write "chaof", it'll emit another composition completion
// event for "xinchaof" and the resulting output in the shell will finally read "xinxinchaof".
// A composition completion event technically doesn't mean that the completed text is now
// immutable after all. We could (and probably should) inform TSF of any input changes,
// but we technically aren't a text input field. The immediate solution was
// to simply force TSF to clear its text whenever we have input focus.
TSFInputControl().ClearBuffer();
_HidePointerCursorHandlers(*this, nullptr);
const auto ch = e.Character();
@@ -1066,7 +1081,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// keybindings on the keyUp, then we'll still send the keydown to the
// connected terminal application, and something like ctrl+shift+T will
// emit a ^T to the pipe.
if (!modifiers.IsAltGrPressed() && keyDown && _TryHandleKeyBinding(vkey, scanCode, modifiers))
if (!modifiers.IsAltGrPressed() &&
keyDown &&
_TryHandleKeyBinding(vkey, scanCode, modifiers))
{
e.Handled(true);
return;
@@ -1092,6 +1109,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - modifiers: The ControlKeyStates representing the modifier key states.
bool TermControl::_TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const
{
// Mark mode has a specific set of pre-defined key bindings.
// If we're in mark mode, we should be prioritizing those over
// the custom defined key bindings.
if (_core.TryMarkModeKeybinding(vkey, modifiers))
{
return true;
}
// TODO: GH#5000
// The Core owning the keybindings is weird. That's for sure. In the
// future, we may want to pass the keybindings into the control
@@ -1163,12 +1188,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
const auto window = CoreWindow::GetForCurrentThread();
if (vkey == VK_ESCAPE ||
vkey == VK_RETURN)
{
TSFInputControl().ClearBuffer();
}
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.

View File

@@ -67,10 +67,10 @@ static constexpr bool IsReadable(std::wstring_view text)
namespace winrt::Microsoft::Terminal::Control::implementation
{
TermControlAutomationPeer::TermControlAutomationPeer(TermControl* owner,
TermControlAutomationPeer::TermControlAutomationPeer(winrt::com_ptr<TermControl> owner,
const Core::Padding padding,
Control::InteractivityAutomationPeer impl) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner.get()), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner },
_contentAutomationPeer{ impl }
{
@@ -134,8 +134,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis{ get_weak() }]() {
if (auto strongThis{ weakThis.get() })
{
// The event that is raised when the text selection is modified.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
if (auto control{ strongThis->_termControl.get() })
{
// The event that is raised when the text selection is modified.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
}
}
});
}
@@ -157,8 +160,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis{ get_weak() }]() {
if (auto strongThis{ weakThis.get() })
{
// The event that is raised when textual content is modified.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged);
if (auto control{ strongThis->_termControl.get() })
{
// The event that is raised when textual content is modified.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged);
}
}
});
}
@@ -180,13 +186,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis{ get_weak() }]() {
if (auto strongThis{ weakThis.get() })
{
// The event that is raised when the text was changed in an edit control.
// Do NOT fire a TextEditTextChanged. Generally, an app on the other side
// will expect more information. Though you can dispatch that event
// on its own, it may result in a nullptr exception on the other side
// because no additional information was provided. Crashing the screen
// reader.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
if (auto control{ strongThis->_termControl.get() })
{
// The event that is raised when the text was changed in an edit control.
// Do NOT fire a TextEditTextChanged. Generally, an app on the other side
// will expect more information. Though you can dispatch that event
// on its own, it may result in a nullptr exception on the other side
// because no additional information was provided. Crashing the screen
// reader.
strongThis->RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
}
}
});
}
@@ -236,14 +245,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis{ get_weak() }, sanitizedCopy{ hstring{ sanitized } }]() {
if (auto strongThis{ weakThis.get() })
{
try
if (auto control{ strongThis->_termControl.get() })
{
strongThis->RaiseNotificationEvent(AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::All,
sanitizedCopy,
L"TerminalTextOutput");
try
{
strongThis->RaiseNotificationEvent(AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::All,
sanitizedCopy,
L"TerminalTextOutput");
}
CATCH_LOG();
}
CATCH_LOG();
}
});
}
@@ -284,17 +296,29 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring TermControlAutomationPeer::GetNameCore() const
{
// fallback to title if profile name is empty
auto profileName = _termControl->GetProfileName();
if (profileName.empty())
if (auto control{ _termControl.get() })
{
return _termControl->Title();
const auto profileName = control->GetProfileName();
if (profileName.empty())
{
return control->Title();
}
else
{
return profileName;
}
}
return profileName;
return L"";
}
hstring TermControlAutomationPeer::GetHelpTextCore() const
{
return _termControl->Title();
if (const auto control{ _termControl.get() })
{
return control->Title();
}
return L"";
}
AutomationLiveSetting TermControlAutomationPeer::GetLiveSettingCore() const

View File

@@ -42,7 +42,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Types::IUiaEventDispatcher
{
public:
TermControlAutomationPeer(Microsoft::Terminal::Control::implementation::TermControl* owner,
TermControlAutomationPeer(winrt::com_ptr<Microsoft::Terminal::Control::implementation::TermControl> owner,
const Core::Padding padding,
Control::InteractivityAutomationPeer implementation);
@@ -78,7 +78,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma endregion
private:
winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl;
winrt::weak_ref<Microsoft::Terminal::Control::implementation::TermControl> _termControl;
Control::InteractivityAutomationPeer _contentAutomationPeer;
std::deque<wchar_t> _keyEvents;
};

View File

@@ -147,10 +147,6 @@
<PRIResource Include="Resources\en-US\Resources.resw" />
<OCResourceDirectory Include="Resources" />
</ItemGroup>
<ItemGroup Condition="'$(WindowsTerminalBranding)'=='' or '$(WindowsTerminalBranding)'=='Dev' or '$(WindowsTerminalBranding)'=='Preview'">
<!-- GH#13252 Only vend this dependency for Dev and Preview builds. -->
<SDKReference Include="Microsoft.Midi.GmDls, Version=10.0.22000.0" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
<ProjectReference Include="..\..\types\lib\types.vcxproj" />

View File

@@ -1615,3 +1615,12 @@ til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal::
}
}
}
// Method Description:
// - Returns the position of the cursor relative to the active viewport
til::point Terminal::GetViewportRelativeCursorPosition() const noexcept
{
const auto absoluteCursorPosition{ GetCursorPosition() };
const auto viewport{ _GetMutableViewport() };
return absoluteCursorPosition - viewport.Origin();
}

View File

@@ -84,6 +84,8 @@ public:
bool IsXtermBracketedPasteModeEnabled() const;
std::wstring_view GetWorkingDirectory();
til::point GetViewportRelativeCursorPosition() const noexcept;
// Write comes from the PTY and goes to our parser to be stored in the output buffer
void Write(std::wstring_view stringView);
@@ -424,6 +426,7 @@ private:
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const;
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
til::point _ConvertToBufferCell(const til::point viewportPos) const;
void _ScrollToPoint(const til::point pos);
void _MoveByChar(SelectionDirection direction, til::point& pos);
void _MoveByWord(SelectionDirection direction, til::point& pos);
void _MoveByViewport(SelectionDirection direction, til::point& pos);

View File

@@ -228,7 +228,7 @@ void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<Selec
// - the new start/end for a selection
std::pair<til::point, til::point> Terminal::_PivotSelection(const til::point targetPos, bool& targetStart) const
{
if (targetStart = targetPos <= _selection->pivot)
if (targetStart = _activeBuffer().GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
{
// target is before pivot
// treat target as start
@@ -298,14 +298,24 @@ void Terminal::ToggleMarkMode()
// Enter Mark Mode
// NOTE: directly set cursor state. We already should have locked before calling this function.
_activeBuffer().GetCursor().SetIsOn(false);
const auto cursorPos{ _activeBuffer().GetCursor().GetPosition() };
_selection = SelectionAnchors{};
_selection->start = cursorPos;
_selection->end = cursorPos;
_selection->pivot = cursorPos;
if (!IsSelectionActive())
{
// No selection --> start one at the cursor
const auto cursorPos{ _activeBuffer().GetCursor().GetPosition() };
_selection = SelectionAnchors{};
_selection->start = cursorPos;
_selection->end = cursorPos;
_selection->pivot = cursorPos;
_blockSelection = false;
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
}
else if (WI_AreAllFlagsClear(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End))
{
// Selection already existed
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
}
_ScrollToPoint(_selection->start);
_selectionMode = SelectionInteractionMode::Mark;
_blockSelection = false;
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
}
}
@@ -459,22 +469,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
}
// 4. Scroll (if necessary)
if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(targetPos))
{
if (const auto amtAboveView = visibleViewport.Top() - targetPos.Y; amtAboveView > 0)
{
// anchor is above visible viewport, scroll by that amount
_scrollOffset += amtAboveView;
}
else
{
// anchor is below visible viewport, scroll by that amount
const auto amtBelowView = targetPos.Y - visibleViewport.BottomInclusive();
_scrollOffset -= amtBelowView;
}
_NotifyScrollEvent();
_activeBuffer().TriggerScroll();
}
_ScrollToPoint(targetPos);
}
void Terminal::SelectAll()
@@ -485,6 +480,7 @@ void Terminal::SelectAll()
_selection->end = { bufferSize.RightInclusive(), _GetMutableViewport().BottomInclusive() };
_selection->pivot = _selection->end;
_selectionMode = SelectionInteractionMode::Keyboard;
_ScrollToPoint(_selection->start);
}
void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
@@ -524,7 +520,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
case SelectionDirection::Left:
{
const auto wordStartPos{ _activeBuffer().GetWordStart(pos, _wordDelimiters) };
if (_selection->pivot < pos)
if (_activeBuffer().GetSize().CompareInBounds(_selection->pivot, pos) < 0)
{
// If we're moving towards the pivot, move one more cell
pos = wordStartPos;
@@ -547,7 +543,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
case SelectionDirection::Right:
{
const auto wordEndPos{ _activeBuffer().GetWordEnd(pos, _wordDelimiters) };
if (pos < _selection->pivot)
if (_activeBuffer().GetSize().CompareInBounds(pos, _selection->pivot) < 0)
{
// If we're moving towards the pivot, move one more cell
pos = _activeBuffer().GetWordEnd(pos, _wordDelimiters);
@@ -685,3 +681,27 @@ void Terminal::ColorSelection(const til::point, const til::point, const TextAttr
{
THROW_HR(E_NOTIMPL);
}
// Method Description:
// - if necessary, scroll the viewport such that the given point is visible
// Arguments:
// - pos: a coordinate relative to the buffer (not viewport)
void Terminal::_ScrollToPoint(const til::point pos)
{
if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(pos))
{
if (const auto amtAboveView = visibleViewport.Top() - pos.Y; amtAboveView > 0)
{
// anchor is above visible viewport, scroll by that amount
_scrollOffset += amtAboveView;
}
else
{
// anchor is below visible viewport, scroll by that amount
const auto amtBelowView = pos.Y - visibleViewport.BottomInclusive();
_scrollOffset -= amtBelowView;
}
_NotifyScrollEvent();
_activeBuffer().TriggerScroll();
}
}

View File

@@ -36,22 +36,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
static const std::array<hstring, 16> TableColorNames = {
RS_(L"ColorScheme_Black/Header"),
RS_(L"ColorScheme_Red/Header"),
RS_(L"ColorScheme_Green/Header"),
RS_(L"ColorScheme_Yellow/Header"),
RS_(L"ColorScheme_Blue/Header"),
RS_(L"ColorScheme_Purple/Header"),
RS_(L"ColorScheme_Cyan/Header"),
RS_(L"ColorScheme_White/Header"),
RS_(L"ColorScheme_BrightBlack/Header"),
RS_(L"ColorScheme_BrightRed/Header"),
RS_(L"ColorScheme_BrightGreen/Header"),
RS_(L"ColorScheme_BrightYellow/Header"),
RS_(L"ColorScheme_BrightBlue/Header"),
RS_(L"ColorScheme_BrightPurple/Header"),
RS_(L"ColorScheme_BrightCyan/Header"),
RS_(L"ColorScheme_BrightWhite/Header")
RS_(L"ColorScheme_Black/Text"),
RS_(L"ColorScheme_Red/Text"),
RS_(L"ColorScheme_Green/Text"),
RS_(L"ColorScheme_Yellow/Text"),
RS_(L"ColorScheme_Blue/Text"),
RS_(L"ColorScheme_Purple/Text"),
RS_(L"ColorScheme_Cyan/Text"),
RS_(L"ColorScheme_White/Text"),
RS_(L"ColorScheme_BrightBlack/Text"),
RS_(L"ColorScheme_BrightRed/Text"),
RS_(L"ColorScheme_BrightGreen/Text"),
RS_(L"ColorScheme_BrightYellow/Text"),
RS_(L"ColorScheme_BrightBlue/Text"),
RS_(L"ColorScheme_BrightPurple/Text"),
RS_(L"ColorScheme_BrightCyan/Text"),
RS_(L"ColorScheme_BrightWhite/Text")
};
static const std::array<std::wstring, 9> InBoxSchemes = {
@@ -187,7 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), schemeName) != std::end(InBoxSchemes))
{
// load disclaimer for in-box profiles
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox");
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox/Text");
}
DeleteButtonDisclaimer().Text(disclaimer);

View File

@@ -16,6 +16,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_Label{ label },
_SubPage{ subPage } {}
hstring ToString() { return _Label; }
WINRT_PROPERTY(IInspectable, Tag);
WINRT_PROPERTY(winrt::hstring, Label);
WINRT_PROPERTY(BreadcrumbSubPage, SubPage);

View File

@@ -20,7 +20,7 @@ namespace Microsoft.Terminal.Settings.Editor
Profile_Advanced
};
runtimeclass Breadcrumb
runtimeclass Breadcrumb : Windows.Foundation.IStringable
{
IInspectable Tag;
String Label;

View File

@@ -34,6 +34,7 @@
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -353,12 +354,5 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View File

@@ -139,43 +139,43 @@
<value>Background</value>
<comment>This is the header for a control that lets the user select the background color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Black.Header" xml:space="preserve">
<data name="ColorScheme_Black.Text" xml:space="preserve">
<value>Black</value>
<comment>This is the header for a control that lets the user select the black color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Blue.Header" xml:space="preserve">
<data name="ColorScheme_Blue.Text" xml:space="preserve">
<value>Blue</value>
<comment>This is the header for a control that lets the user select the blue color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightBlack.Header" xml:space="preserve">
<data name="ColorScheme_BrightBlack.Text" xml:space="preserve">
<value>Bright black</value>
<comment>This is the header for a control that lets the user select the bright black color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightBlue.Header" xml:space="preserve">
<data name="ColorScheme_BrightBlue.Text" xml:space="preserve">
<value>Bright blue</value>
<comment>This is the header for a control that lets the user select the bright blue color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightCyan.Header" xml:space="preserve">
<data name="ColorScheme_BrightCyan.Text" xml:space="preserve">
<value>Bright cyan</value>
<comment>This is the header for a control that lets the user select the bright cyan color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightGreen.Header" xml:space="preserve">
<data name="ColorScheme_BrightGreen.Text" xml:space="preserve">
<value>Bright green</value>
<comment>This is the header for a control that lets the user select the bright green color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightPurple.Header" xml:space="preserve">
<data name="ColorScheme_BrightPurple.Text" xml:space="preserve">
<value>Bright purple</value>
<comment>This is the header for a control that lets the user select the bright purple color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightRed.Header" xml:space="preserve">
<data name="ColorScheme_BrightRed.Text" xml:space="preserve">
<value>Bright red</value>
<comment>This is the header for a control that lets the user select the bright red color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightWhite.Header" xml:space="preserve">
<data name="ColorScheme_BrightWhite.Text" xml:space="preserve">
<value>Bright white</value>
<comment>This is the header for a control that lets the user select the bright white color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_BrightYellow.Header" xml:space="preserve">
<data name="ColorScheme_BrightYellow.Text" xml:space="preserve">
<value>Bright yellow</value>
<comment>This is the header for a control that lets the user select the bright yellow color for text displayed on the screen.</comment>
</data>
@@ -183,7 +183,7 @@
<value>Cursor color</value>
<comment>This is the header for a control that lets the user select the text cursor's color displayed on the screen.</comment>
</data>
<data name="ColorScheme_Cyan.Header" xml:space="preserve">
<data name="ColorScheme_Cyan.Text" xml:space="preserve">
<value>Cyan</value>
<comment>This is the header for a control that lets the user select the cyan color for text displayed on the screen.</comment>
</data>
@@ -191,11 +191,11 @@
<value>Foreground</value>
<comment>This is the header for a control that lets the user select the foreground color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Green.Header" xml:space="preserve">
<data name="ColorScheme_Green.Text" xml:space="preserve">
<value>Green</value>
<comment>This is the header for a control that lets the user select the green color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Purple.Header" xml:space="preserve">
<data name="ColorScheme_Purple.Text" xml:space="preserve">
<value>Purple</value>
<comment>This is the header for a control that lets the user select the purple color for text displayed on the screen.</comment>
</data>
@@ -203,15 +203,15 @@
<value>Selection background</value>
<comment>This is the header for a control that lets the user select the background color for selected text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Red.Header" xml:space="preserve">
<data name="ColorScheme_Red.Text" xml:space="preserve">
<value>Red</value>
<comment>This is the header for a control that lets the user select the red color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_White.Header" xml:space="preserve">
<data name="ColorScheme_White.Text" xml:space="preserve">
<value>White</value>
<comment>This is the header for a control that lets the user select the white color for text displayed on the screen.</comment>
</data>
<data name="ColorScheme_Yellow.Header" xml:space="preserve">
<data name="ColorScheme_Yellow.Text" xml:space="preserve">
<value>Yellow</value>
<comment>This is the header for a control that lets the user select the yellow color for text displayed on the screen.</comment>
</data>
@@ -699,6 +699,10 @@
<value>Never close automatically</value>
<comment>An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal.</comment>
</data>
<data name="Profile_CloseOnExitAutomatic.Content" xml:space="preserve">
<value>Automatic</value>
<comment>An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal.</comment>
</data>
<data name="Profile_ColorScheme.Header" xml:space="preserve">
<value>Color scheme</value>
<comment>Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user.</comment>
@@ -1194,7 +1198,7 @@
<value>Rename</value>
<comment>Text label for a button that can be used to begin the renaming process.</comment>
</data>
<data name="ColorScheme_DeleteButtonDisclaimerInBox" xml:space="preserve">
<data name="ColorScheme_DeleteButtonDisclaimerInBox.Text" xml:space="preserve">
<value>This color scheme cannot be deleted or renamed because it is included by default.</value>
<comment>Disclaimer presented next to the delete button when it is disabled.</comment>
</data>

View File

@@ -62,7 +62,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
template<typename TIconSource>
TIconSource _getColoredBitmapIcon(const winrt::hstring& path)
{
if (!path.empty())
// FontIcon uses glyphs in the private use area, whereas valid URIs only contain ASCII characters.
// To skip throwing on Uri construction, we can quickly check if the first character is ASCII.
if (!path.empty() && path.front() < 128)
{
try
{

View File

@@ -72,7 +72,7 @@ Author(s):
X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \
X(guid, ConnectionType, "connectionType") \
X(hstring, Icon, "icon", L"\uE756") \
X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Graceful) \
X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) \
X(hstring, TabTitle, "tabTitle") \
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
X(bool, UseAtlasEngine, "experimental.useAtlasEngine", false) \

View File

@@ -14,6 +14,7 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalVisualStudioSetup>true</TerminalVisualStudioSetup>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -274,13 +275,6 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!-- This target will take our defaults.json and stamp it into a .h file that
we can include in the code directly. This way, we don't need to worry about
failing to load the default settings at runtime. -->

View File

@@ -26,7 +26,8 @@ namespace Microsoft.Terminal.Settings.Model
{
Never = 0,
Graceful,
Always
Always,
Automatic
};
[flags]

View File

@@ -108,10 +108,11 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::TextAntialiasingMode)
// - Helper for converting a user-specified closeOnExit value to its corresponding enum
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode)
{
JSON_MAPPINGS(3) = {
JSON_MAPPINGS(4) = {
pair_type{ "always", ValueType::Always },
pair_type{ "graceful", ValueType::Graceful },
pair_type{ "never", ValueType::Never },
pair_type{ "automatic", ValueType::Automatic },
};
// Override mapping parser to add boolean parsing

View File

@@ -7,8 +7,6 @@
#include "LegacyProfileGeneratorNamespaces.h"
#include "../../inc/DefaultSettings.h"
#include <io.h>
#include <fcntl.h>
#include "DynamicProfileUtils.h"
static constexpr std::wstring_view WslHomeDirectory{ L"~" };
@@ -68,105 +66,6 @@ static winrt::com_ptr<implementation::Profile> makeProfile(const std::wstring& d
return WSLDistro;
}
// Method Description:
// - Enumerates all the installed WSL distros to create profiles for them.
// Arguments:
// - <none>
// Return Value:
// - a vector with all distros for all the installed WSL distros
static void legacyGenerate(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
{
wil::unique_handle readPipe;
wil::unique_handle writePipe;
SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, true };
THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, &sa, 0));
STARTUPINFO si{ 0 };
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = writePipe.get();
si.hStdError = writePipe.get();
wil::unique_process_information pi;
wil::unique_cotaskmem_string systemPath;
THROW_IF_FAILED(wil::GetSystemDirectoryW(systemPath));
std::wstring command(systemPath.get());
command += L"\\wsl.exe --list";
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr,
const_cast<LPWSTR>(command.c_str()),
nullptr,
nullptr,
TRUE,
CREATE_NO_WINDOW,
nullptr,
nullptr,
&si,
&pi));
switch (WaitForSingleObject(pi.hProcess, 2000))
{
case WAIT_OBJECT_0:
break;
case WAIT_ABANDONED:
case WAIT_TIMEOUT:
return;
case WAIT_FAILED:
THROW_LAST_ERROR();
default:
THROW_HR(ERROR_UNHANDLED_EXCEPTION);
}
DWORD exitCode;
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
{
THROW_HR(E_INVALIDARG);
}
else if (exitCode != 0)
{
return;
}
DWORD bytesAvailable;
THROW_IF_WIN32_BOOL_FALSE(PeekNamedPipe(readPipe.get(), nullptr, NULL, nullptr, &bytesAvailable, nullptr));
// "The _open_osfhandle call transfers ownership of the Win32 file handle to the file descriptor."
// (https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-osfhandle?view=vs-2019)
// so, we detach_from_smart_pointer it -- but...
// "File descriptors passed into _fdopen are owned by the returned FILE * stream.
// If _fdopen is successful, do not call _close on the file descriptor.
// Calling fclose on the returned FILE * also closes the file descriptor."
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fdopen-wfdopen?view=vs-2019
auto stdioPipeHandle = _wfdopen(_open_osfhandle((intptr_t)wil::detach_from_smart_pointer(readPipe), _O_WTEXT | _O_RDONLY), L"r");
auto closeFile = wil::scope_exit([&]() { fclose(stdioPipeHandle); });
std::wfstream pipe{ stdioPipeHandle };
std::wstring wline;
std::getline(pipe, wline); // remove the header from the output.
while (pipe.tellp() < bytesAvailable)
{
std::getline(pipe, wline);
std::wstringstream wlinestream(wline);
if (wlinestream)
{
std::wstring distName;
std::getline(wlinestream, distName, L'\r');
if (til::starts_with(distName, DockerDistributionPrefix))
{
// Docker for Windows creates some utility distributions to handle Docker commands.
// Pursuant to GH#3556, because they are _not_ user-facing we want to hide them.
continue;
}
const auto firstChar = distName.find_first_of(L"( ");
// Some localizations don't have a space between the name and "(Default)"
// https://github.com/microsoft/terminal/issues/1168#issuecomment-500187109
if (firstChar < distName.size())
{
distName.resize(firstChar);
}
profiles.emplace_back(makeProfile(distName));
}
}
}
// Function Description:
// - Create a list of Profiles for each distro listed in names.
// - Skips distros that are utility distros for docker (see GH#3556)
@@ -310,9 +209,9 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
// Method Description:
// - Generate a list of profiles for each on the installed WSL distros. This
// will first try to read the installed distros from the registry. If that
// fails, we'll fall back to the legacy way of launching WSL.exe to read the
// distros from the commandline. Reading the registry is slightly more stable
// (see GH#7199, GH#9905), but it is certainly BODGY
// fails, we'll assume there are no WSL distributions installed.
// Reading the registry is slightly more stable (see GH#7199, GH#9905),
// but it is certainly BODGY
// Arguments:
// - <none>
// Return Value:
@@ -333,6 +232,4 @@ void WslDistroGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementat
}
}
}
legacyGenerate(profiles);
}

View File

@@ -43,7 +43,7 @@
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
"colorScheme": "Campbell",
"antialiasingMode": "grayscale",
"closeOnExit": "graceful",
"closeOnExit": "automatic",
"cursorShape": "bar",
"fontFace": "Cascadia Mono",
"fontSize": 12,
@@ -62,7 +62,7 @@
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
"colorScheme": "Campbell",
"antialiasingMode": "grayscale",
"closeOnExit": "graceful",
"closeOnExit": "automatic",
"cursorShape": "bar",
"fontFace": "Cascadia Mono",
"fontSize": 12,

View File

@@ -14,6 +14,7 @@
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -105,14 +106,6 @@
</Reference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>

View File

@@ -227,8 +227,13 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
TEST_METHOD(SimpleAltBufferTest);
TEST_METHOD(AltBufferToAltBufferTest);
TEST_METHOD(TestPowerLineFirstFrame);
TEST_METHOD(AltBufferResizeCrash);
TEST_METHOD(TestNoExtendedAttrsOptimization);
TEST_METHOD(TestNoBackgroundAttrsOptimization);
private:
bool _writeCallback(const char* const pch, const size_t cch);
void _flushFirstFrame();
@@ -4108,12 +4113,88 @@ void ConptyRoundtripTests::AltBufferToAltBufferTest()
verifyBuffer(*termAltTb, term->_GetMutableViewport().ToExclusive(), Frame::StillInAltBuffer);
}
void ConptyRoundtripTests::TestPowerLineFirstFrame()
{
Log::Comment(L"This is a test for GH#8341. If we received colored spaces "
L"BEFORE the first frame, we should still emit them!");
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& sm = si.GetStateMachine();
auto* hostTb = &si.GetTextBuffer();
auto* termTb = term->_mainBuffer.get();
_checkConptyOutput = false;
TextAttribute whiteOnGreen{};
whiteOnGreen.SetIndexedForeground(TextColor::DARK_WHITE);
whiteOnGreen.SetIndexedBackground(TextColor::BRIGHT_GREEN);
TextAttribute greenOnBlack{};
greenOnBlack.SetIndexedForeground(TextColor::BRIGHT_GREEN);
greenOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
TextAttribute whiteOnBlack{};
whiteOnBlack.SetIndexedForeground(TextColor::DARK_WHITE);
whiteOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
TextAttribute blackOnDefault{};
blackOnDefault.SetIndexedForeground(TextColor::BRIGHT_BLACK);
TextAttribute defaultOnDefault{};
Log::Comment(L"========== Fill test content ==========");
// As a pwsh one-liner:
//
// "`e[37m`e[102m foo\bar `e[92m`e[100m▶ `e[37mBar `e[90m`e[49m▶ `e[m"
//
// Generally taken from
// https://github.com/microsoft/terminal/issues/8341#issuecomment-731310022,
// but minimized for easier testing.
sm.ProcessString(L"\x1b[37m\x1b[102m" // dark white on bright green
L" foo\\bar ");
sm.ProcessString(L"\x1b[92m\x1b[100m" // bright green on bright black
L"");
sm.ProcessString(L"\x1b[37m" // dark white on bright black
L"Bar ");
sm.ProcessString(L"\x1b[90m\x1b[49m" // bright black on default
L"");
sm.ProcessString(L"\x1b[m\n"); // default on default
auto verifyBuffer = [&](const TextBuffer& tb) {
// If this test fails on character 8, then it's because we didn't emit the space, we just moved ahead.
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, whiteOnGreen, 9u);
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ greenOnBlack, 2u });
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ whiteOnBlack, 4u });
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ blackOnDefault, 2u });
};
Log::Comment(L"========== Check host buffer ==========");
verifyBuffer(*hostTb);
Log::Comment(L"========== Paint first frame ==========");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Check terminal buffer ==========");
verifyBuffer(*termTb);
}
void ConptyRoundtripTests::AltBufferResizeCrash()
{
Log::Comment(L"During the review for GH#12719, it was noticed that this "
L"particular combination of resizing could crash the terminal."
L" This test makes sure we don't.");
// Anything that resizes the buffer needs IsolationLevel:Method
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
END_TEST_METHOD_PROPERTIES()
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
@@ -4154,3 +4235,99 @@ void ConptyRoundtripTests::AltBufferResizeCrash()
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
}
void ConptyRoundtripTests::TestNoExtendedAttrsOptimization()
{
Log::Comment(L"We don't want conpty to optimize out runs of spaces that DO "
L"have extended attrs, because EL / ECH don't fill space with "
L"those attributes");
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& sm = si.GetStateMachine();
auto* hostTb = &si.GetTextBuffer();
auto* termTb = term->_mainBuffer.get();
gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state.
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
_flushFirstFrame();
_checkConptyOutput = false;
TextAttribute reverseAttrs{};
reverseAttrs.SetReverseVideo(true);
auto verifyBuffer = [&](const TextBuffer& tb) {
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L' ', reverseAttrs, 9u);
TestUtils::VerifyExpectedString(L"test", iter0);
TestUtils::VerifyLineContains(iter0, L' ', reverseAttrs, 9u);
TestUtils::VerifyLineContains(tb, { 0, 1 }, L' ', reverseAttrs, static_cast<uint32_t>(TerminalViewWidth));
};
Log::Comment(L"========== Fill test content ==========");
sm.ProcessString(L"\x1b[7m test \x1b[m\n");
sm.ProcessString(L"\x1b[7m");
sm.ProcessString(std::wstring(TerminalViewWidth, L' '));
sm.ProcessString(L"\x1b[m\n");
Log::Comment(L"========== Check host buffer ==========");
verifyBuffer(*hostTb);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Check terminal buffer ==========");
verifyBuffer(*termTb);
}
void ConptyRoundtripTests::TestNoBackgroundAttrsOptimization()
{
Log::Comment(L"Same as above, with BG attrs");
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& sm = si.GetStateMachine();
auto* hostTb = &si.GetTextBuffer();
auto* termTb = term->_mainBuffer.get();
gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state.
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
_flushFirstFrame();
_checkConptyOutput = false;
TextAttribute bgAttrs{};
bgAttrs.SetIndexedBackground(TextColor::DARK_WHITE);
auto verifyBuffer = [&](const TextBuffer& tb) {
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L' ', bgAttrs, 9u);
TestUtils::VerifyExpectedString(L"test", iter0);
TestUtils::VerifyLineContains(iter0, L' ', bgAttrs, 9u);
TestUtils::VerifyLineContains(tb, { 0, 1 }, L' ', bgAttrs, static_cast<uint32_t>(TerminalViewWidth));
};
Log::Comment(L"========== Fill test content ==========");
sm.ProcessString(L"\x1b[47m test \x1b[m\n");
sm.ProcessString(L"\x1b[47m");
sm.ProcessString(std::wstring(TerminalViewWidth, L' '));
sm.ProcessString(L"\x1b[m\n");
Log::Comment(L"========== Check host buffer ==========");
verifyBuffer(*hostTb);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Check terminal buffer ==========");
verifyBuffer(*termTb);
}

View File

@@ -435,6 +435,27 @@ void TerminalCoreUnitTests::TerminalApiTest::SetWorkingDirectory()
stateMachine.ProcessString(L"\x1b]9;9\"C:\\\"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
stateMachine.ProcessString(L"\x1b"
LR"(]9;9;"C:\invalid path "with" quotes")"
L"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
// These OSC 9;9 sequences will result in invalid CWD. It should end up empty, like above.
stateMachine.ProcessString(L"\x1b]9;9;\"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
stateMachine.ProcessString(L"\x1b]9;9;\"\"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
stateMachine.ProcessString(L"\x1b]9;9;\"\"\"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
stateMachine.ProcessString(L"\x1b]9;9;\"\"\"\"\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
stateMachine.ProcessString(L"\x1b]9;9;No quotes \"until\" later\x1b\\");
VERIFY_IS_TRUE(term.GetWorkingDirectory().empty());
// Valid sequences should change CWD
stateMachine.ProcessString(L"\x1b]9;9;\"C:\\\"\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"C:\\");
@@ -454,17 +475,4 @@ void TerminalCoreUnitTests::TerminalApiTest::SetWorkingDirectory()
stateMachine.ProcessString(L"\x1b]9;9;D:\\中文\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"D:\\中文");
// These OSC 9;9 sequences will result in invalid CWD. We shouldn't crash on these.
stateMachine.ProcessString(L"\x1b]9;9;\"\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"\"");
stateMachine.ProcessString(L"\x1b]9;9;\"\"\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"\"\"");
stateMachine.ProcessString(L"\x1b]9;9;\"\"\"\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"\"");
stateMachine.ProcessString(L"\x1b]9;9;\"\"\"\"\x1b\\");
VERIFY_ARE_EQUAL(term.GetWorkingDirectory(), L"\"\"");
}

View File

@@ -405,6 +405,12 @@ void AppHost::Initialize()
}
});
_window->AutomaticShutdownRequested([this]() {
// Raised when the OS is beginning an update of the app. We will quit,
// to save our state, before the OS manually kills us.
_windowManager.RequestQuitAll();
});
_logic.Create();
_revokers.TitleChanged = _logic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged });
@@ -677,13 +683,6 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, Launc
// 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:

View File

@@ -648,6 +648,43 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
break;
}
case WM_ENDSESSION:
{
// For WM_QUERYENDSESSION and WM_ENDSESSION, refer to:
//
// https://docs.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications
//
// The OS will send us a WM_QUERYENDSESSION when it's preparing an
// update for our app. It will then send us a WM_ENDSESSION, which gives
// us a small timeout (~30s) to actually shut down gracefully. After
// that timeout, it will send us a WM_CLOSE. If we still don't close
// after the WM_CLOSE, it'll force-kill us (causing a crash which will be
// bucketed to MoAppHang).
//
// If we need to do anything to prepare for being told to shutdown,
// start it in WM_QUERYENDSESSION. If (in the future) we need to prevent
// logoff, we can return false there. (DefWindowProc returns true)
//
// The OS is going to shut us down here. We will manually start a quit,
// so that we can persist the state. If we refuse to gracefully shut
// down here, the OS will crash us to forcefully terminate us. We choose
// to quit here, rather than just close, to skip over any warning
// dialogs (e.g. "Are you sure you want to close all tabs?") which might
// prevent a WM_CLOSE from cleanly closing the window.
//
// This will cause a appHost._RequestQuitAll, which will notify the
// monarch to collect up all the window state and save it.
TraceLoggingWrite(
g_hWindowsTerminalProvider,
"EndSession",
TraceLoggingDescription("Emitted when the OS has sent a WM_ENDSESSION"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_AutomaticShutdownRequestedHandlers();
return true;
}
default:
// We'll want to receive this message when explorer.exe restarts
// so that we can re-add our icon to the notification area.
@@ -834,10 +871,20 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
// - <none>
void IslandWindow::ShowWindowChanged(const bool showOrHide)
{
const auto hwnd = GetHandle();
if (hwnd)
if (const auto hwnd = GetHandle())
{
PostMessage(hwnd, WM_SYSCOMMAND, showOrHide ? SC_RESTORE : SC_MINIMIZE, 0);
// IMPORTANT!
//
// ONLY "restore" if already minimized. If the window is maximized or
// snapped, a restore will restore-down the window instead.
if (showOrHide == true && ::IsIconic(hwnd))
{
::PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
}
else if (showOrHide == false)
{
::PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
}
}
@@ -1206,7 +1253,6 @@ bool IslandWindow::RegisterHotKey(const int index, const winrt::Microsoft::Termi
// TODO GH#8888: We should display a warning of some kind if this fails.
// This can fail if something else already bound this hotkey.
const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey);
LOG_IF_WIN32_BOOL_FALSE(result);
TraceLoggingWrite(g_hWindowsTerminalProvider,
"RegisterHotKey",
@@ -1452,23 +1498,31 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration,
}
else
{
const auto windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
const auto currentThreadId = GetCurrentThreadId();
// Try first to send a message to the current foreground window. If it's not responding, it may
// be waiting on us to finsh launching. Passing SMTO_NOTIMEOUTIFNOTHUNG means that we get the same
// behavior as before--that is, waiting for the message loop--but we've done an early return if
// it turns out that it was hung.
// SendMessageTimeoutW returns nonzero if it succeeds.
if (0 != SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0, SMTO_NOTIMEOUTIFNOTHUNG | SMTO_BLOCK | SMTO_ABORTIFHUNG, 1000, nullptr))
{
const auto windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
const auto currentThreadId = GetCurrentThreadId();
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true));
// Just in case, add the thread detach as a scope_exit, to make _sure_ we do it.
auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() {
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false));
});
LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get()));
ShowWindow(_window.get(), SW_SHOW);
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true));
// Just in case, add the thread detach as a scope_exit, to make _sure_ we do it.
auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() {
LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false));
});
LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get()));
ShowWindow(_window.get(), SW_SHOW);
// Activate the window too. This will force us to the virtual desktop this
// window is on, if it's on another virtual desktop.
LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get()));
// Activate the window too. This will force us to the virtual desktop this
// window is on, if it's on another virtual desktop.
LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get()));
// Throw us on the active monitor.
_moveToMonitor(oldForegroundWindow, toMonitor);
// Throw us on the active monitor.
_moveToMonitor(oldForegroundWindow, toMonitor);
}
}
}

View File

@@ -76,6 +76,7 @@ public:
WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate<void()>);
WINRT_CALLBACK(ShouldExitFullscreen, winrt::delegate<void()>);
WINRT_CALLBACK(MaximizeChanged, winrt::delegate<void(bool)>);
WINRT_CALLBACK(AutomaticShutdownRequested, winrt::delegate<void(void)>);
WINRT_CALLBACK(WindowMoved, winrt::delegate<void()>);
WINRT_CALLBACK(WindowVisibilityChanged, winrt::delegate<void(bool)>);

View File

@@ -21,6 +21,7 @@
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
<TerminalThemeHelpers>true</TerminalThemeHelpers>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
@@ -141,14 +142,6 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!-- Override GetPackagingOutputs to roll up all our dependencies.
This ensures that when the WAP packaging project asks what files go into
the package, we tell it.

View File

@@ -99,12 +99,6 @@ static bool _messageIsAltSpaceKeypress(const MSG& message)
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));
::Microsoft::Console::ErrorReporting::EnableFallbackFailureReporting(g_hWindowsTerminalProvider);
// If Terminal is spawned by a shortcut that requests that it run in a new process group

View File

@@ -8,6 +8,8 @@ namespace Microsoft.Terminal.Wpf
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
@@ -52,6 +54,30 @@ namespace Microsoft.Terminal.Wpf
}
}
/// <summary>
/// WPF's HwndHost likes to mark the WM_GETOBJECT message as handled to
/// force the usage of the WPF automation peer. We explicitly mark it as
/// not handled and don't return an automation peer in "OnCreateAutomationPeer" below.
/// This forces the message to go down to the HwndTerminal where we return terminal's UiaProvider.
/// </summary>
/// <inheritdoc/>
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == (int)NativeMethods.WindowMessage.WM_GETOBJECT)
{
handled = false;
return IntPtr.Zero;
}
return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
}
/// <inheritdoc/>
protected override AutomationPeer OnCreateAutomationPeer()
{
return null;
}
/// <summary>
/// Event that is fired when the terminal buffer scrolls from text output.
/// </summary>

View File

@@ -8,6 +8,7 @@ namespace Microsoft.Terminal.Wpf
using System;
using System.Threading;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
@@ -42,6 +43,20 @@ namespace Microsoft.Terminal.Wpf
this.GotFocus += this.TerminalControl_GotFocus;
}
/// <inheritdoc/>
protected override AutomationPeer OnCreateAutomationPeer()
{
var peer = FrameworkElementAutomationPeer.FromElement(this);
if (peer == null)
{
// Provide our own automation peer here that just sets IsContentElement/IsControlElement to false
// (aka AccessibilityView = Raw). This makes it not pop up in the UIA tree.
peer = new TermControlAutomationPeer(this);
}
return peer;
}
/// <summary>
/// Gets the current character rows available to the terminal.
/// </summary>
@@ -266,5 +281,22 @@ namespace Microsoft.Terminal.Wpf
var viewTop = (int)e.NewValue;
this.termContainer.UserScroll(viewTop);
}
private class TermControlAutomationPeer : UserControlAutomationPeer
{
public TermControlAutomationPeer(UserControl owner) : base(owner)
{
}
protected override bool IsContentElementCore()
{
return false;
}
protected override bool IsControlElementCore()
{
return false;
}
}
}
}

View File

@@ -14,14 +14,6 @@
<Import Project="$(OpenConsoleDir)\src\common.build.pre.props" />
<Import Project="$(SolutionDir)\src\common.nugetversions.props" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="precomp.h" />

View File

@@ -58,6 +58,9 @@
<!-- VisualStudioSetup -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="'$(TerminalVisualStudioSetup)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="'$(TerminalMUX)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
@@ -95,6 +98,9 @@
<!-- VisualStudioSetup -->
<Error Condition="'$(TerminalVisualStudioSetup)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" />
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
<Error Condition="'$(TerminalMUX)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
<Error Condition="!Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />

View File

@@ -117,18 +117,6 @@
</alwaysDisabledBrandingTokens>
</feature>
<feature>
<name>Feature_DECPSViaMidiPlayer</name>
<description>Enables playing sound via DECPS using the MIDI player.</description>
<stage>AlwaysDisabled</stage>
<!-- We're disabling this for WindowsInbox and Stable because it requires an additional
package dependency or library dependency. -->
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Preview</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_ScrollbarMarks</name>
<description>Enables the experimental scrollbar marks feature.</description>

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