Compare commits

...

16 Commits

Author SHA1 Message Date
Mike Griese
997b4acce5 Prevent ConPTY from de-snapping the terminal (#13164)
This is a big hammer to put out this fire. We're keeping the hiding around for now, cause we think that's likely the one that the internal tests use that we really care about here. If we need to bring this back, we can.

* [x] Closes #13158
* [x] Closes #13162
* [x] Validated these both manually
* [x] `[Native]::ShowWindow([Native]::GetConsoleWindow(), 6)` still works

(cherry picked from commit 11b810e403)
Service-Card-Id: 82371517
Service-Version: 1.14
2022-05-25 15:02:42 -05:00
Ian O'Neill
287db8a483 Don't attempt to split settings tabs (#13172)
Prevents a null pointer dereference when attempting to split a settings tab, due to it not being a terminal tab.

* [x] Closes #13166

## Validation Steps Performed
Manually tested.

(cherry picked from commit 6439b4d807)
Service-Card-Id: 82395540
Service-Version: 1.14
2022-05-25 15:02:41 -05:00
Dustin L. Howett
07f7717112 Remove location-specific suffixes from the zh- lang specifiers (#13148)
This only impacts the UI. We can take a workitem to rename the loc data
later. When the user specifies zh-Hans/zh-Hant, the resource mapper does
the right thing.

Related to #8984

(cherry picked from commit 78852e04ec)
Service-Card-Id: 82315897
Service-Version: 1.14
2022-05-25 15:02:40 -05:00
Mike Griese
39c71d6421 Debounce window state changes caused by the PTY (#13147)
Use a throttled update to update our window state. Throttling should prevent scenarios where the Terminal window state and PTY window state get de-sync'd, and cause the window to minimize/restore constantly in a loop.  "Should" is doing a lot of work in this sentence.

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

I think we're overall not super confident that this fixes the root causes of the issue. Rather, we're hopeful that a small amount of throttling here should leave time for the Terminal and pty to sync back up. We're comfortable enough with that as a bandaid for 1.14 preview, to see how this behaves in the wild.

(cherry picked from commit c5fad74c54)
Service-Card-Id: 82315892
Service-Version: 1.14
2022-05-23 18:05:01 -05:00
Mike Griese
c0e6c259fc Fix the SUI being in the wrong Theme color (#13145)
ThemeResources are a persistent pain.

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

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

* [x] Fixes something we noticed right before shipping

(cherry picked from commit bb03b00ebf)
Service-Card-Id: 82303612
Service-Version: 1.14
2022-05-23 13:26:21 -05:00
Mike Griese
6d636056a0 Add the background back to showTabsInTitlebar: false's tab row. (#13144)
We're doing it this way because ThemeResources are tricky. We
default in XAML to using the appropriate ThemeResource background
color for our TabRow. When tabs in the titlebar are _disabled_,
this will ensure that the tab row has the correct theme-dependent
value. When tabs in the titlebar are _enabled_ (the default),
we'll switch the BG to Transparent, to let the Titlebar Control's
background be used as the BG for the tab row.

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

* [x] Closes #13143
* [x] I work here
* [x] validated manually:
  - [x] showTabsInTitlebar: false, true
  - [x] useAcrylicInTabRow: false, true
  - [x] theme: light, dark
* [x] Need to check if this is regressed the same in 1.13. I suspect it is.

(cherry picked from commit 4c333536b4)
Service-Card-Id: 82298858
Service-Version: 1.14
2022-05-23 13:26:19 -05:00
Mike Griese
20a6c7078b Fis ShowHide for DefTerm (#13129)
Well this one feels dumb.

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

* [x] Closes #13066 for real.
* [x] tested manually.

(cherry picked from commit d82af9367f)
Service-Card-Id: 82146739
Service-Version: 1.14
2022-05-19 18:39:15 -05:00
Mike Griese
eec6a882d6 Fix ShowWindow(GetConsoleWindow()) (#13118)
A bad merge, that actually revealed a horrible bug.

There was a secret conflict between the code in #12526 and #12515. 69b77ca was a bad merge that hid just how bad the issue was. Fixing the one line `nullptr`->`this` in `InteractivityFactory` resulted in a window that would flash uncontrollably, as it minimized and restored itself in a loop. Great.

This can seemingly be fixed by making sure that the conpty window is initially created with the owner already set, rather than relying on a `SetParent` call in post. This does pose some complications for the #1256 future we're approaching. However, this is a blocking bug _now_, and we can figure out the tearout/`SetParent` thing in post.

* fixes #13066.
* Tested with the script in that issue.
* Window doesn't flash uncontrollably.
* `gci | ogv` still works right
* I work here.
* Opening a new tab doesn't spontaneously cause the window to minimize
* Restoring from minimized doesn't yeet focus to an invisible window
* Opening a new tab doesn't yeet focus to an invisible window
* There _is_ a viable way to call `GetAncestor` s.t. it returns the Terminal's hwnd in Terminal, and the console's in Conhost

The `SW_SHOWNOACTIVATE` change is also quite load bearing. With just `SW_NORMAL`, the pseudo window (which is invisible!) gets activated whenever the terminal window is restored from minimized. That's BAD.

There's actually more to this as well.

Calling `SetParent` on a window that is `WS_VISIBLE` will cause the OS to hide the window, make it a _child_ window, then call `SW_SHOW` on the window to re-show it. `SW_SHOW`, however, will cause the OS to also set that window as the _foreground_ window, which would result in the pty's hwnd stealing the foreground away from the owning terminal window. That's bad.

`SetWindowLongPtr` seems to do the job of changing who the window owner is, without all the other side effects of reparenting the window.

Without `SetParent`, however, the pty HWND is no longer a descendant of the Terminal HWND, so that means `GA_ROOT` can no longer be used to find the owner's hwnd. For even more insanity, without `WS_POPUP`, none of the values of `GetAncestor` will actually get the terminal HWND. So, now we also need `WS_POPUP` on the pty hwnd. To get at the Terminal hwnd, you'll need

```c++
GetAncestor(GetConsoleWindow(), GA_ROOTOWNER)
```

(cherry picked from commit 77215d9d77)
Service-Card-Id: 82170678
Service-Version: 1.14
2022-05-19 18:39:14 -05:00
James Holderness
bc9e43a19d Fix handling of cursor position reports in passthrough mode (#13109)
## Summary of the Pull Request

When the conpty passthrough mode is enabled, it often needs to send `DSR-CPR` queries (cursor position reports) to the client terminal to obtain the current cursor position. However, the code that originally handled the responses to these queries got broken by the refactoring of the `ConGetSet` API. This PR is an attempt to correct that regression.

## References

The conpty passthrough mode was introduced in PR #11264.
The refactoring that broke the cursor position handling was in PR #12703.

## PR Checklist
* [x] Closes #13106
* [x] CLA signed.
* [ ] 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.
* [ ] 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

Prior to the `ConGetSet` refactoring, the code that handled `DSR-CPR` responses (`InteractDispatch::MoveCursor`) would pass the cursor position to `ConGetSet::SetCursorPosition`, which in turn would forward it to the `SetConsoleCursorPositionImpl` API, and from there to the `VtIo` class.

After the refactor, all of those intermediate steps were removed - the cursor was simply updated directly in `InteractDispatch::MoveCursor`, and the `VtIo` call was moved from `SetConsoleCursorPositionImpl` to `InteractDispatch` (since that was the only place it was actually required).

However, when the conpty passthrough mode was introduced - which happened in parallel - it relied on the `SetConsoleCursorPositionImpl` API being called from `InteractDispatch` in order to handle its own `DSR-CPR` responses, and that's why things stopped working when the two PRs merged.

So what I've done now is made `InteractDispatch::MoveCursor` method call `SetConsoleCursorPositionImpl` again (although without the intermediate `ConGetSet` overhead), and moved the `VtIo::SetCursorPosition` call back into `SetConsoleCursorPositionImpl`.

This is not ideal, and there are still a bunch of problems with the `DSR-CPR` handling in passthrough mode, but it's at least as good as it was before.

## Validation Steps Performed

I've just manually tested various shells with passthrough mode enabled, and confirmed that they're working better now. There are still issues, but nothing that wasn't already a problem in the initial implementation, at least as far as I can tell.

(cherry picked from commit e1086de512)
Service-Card-Id: 82005205
Service-Version: 1.14
2022-05-17 08:03:28 -07:00
Leonard Hecker
858359e62e AtlasEngine: Stop resizing buffers on scroll (#13100)
This regressed in ad2358d.
We're interested in the size of the viewport only, but it can shift up/down
during scrolling. In these situations we shouldn't resize our buffers of course.

## Validation Steps Performed
* Scroll
* Not setting `ApiInvalidations::Size` 

(cherry picked from commit fa6b066747)
Service-Card-Id: 82005202
Service-Version: 1.14
2022-05-17 08:03:27 -07:00
Carlos Zamora
5fbd5392e7 Add experimental.useBackgroundImageForWindow to schema (#13114)
Adds the `experimental.useBackgroundImageForWindow` global setting introduced in #12893 to the schema.

(cherry picked from commit b699f9275f)
Service-Card-Id: 82002294
Service-Version: 1.14
2022-05-17 08:03:26 -07:00
Carlos Zamora
28284e824f Revert "Hide the window from DWM until we're finished with initialization (#12979)" (#13098)
This reverts commit 14098d71f2.

## Summary of the Pull Request
@zadjii-msft found that this is causing persisted windows on a secondary monitor to shrink a little each time. We're choosing to revert this commit until that gets resolved.

## References
#12979

(cherry picked from commit 6ffc3dc7a8)
Service-Card-Id: 81910920
Service-Version: 1.14
2022-05-13 13:09:59 -07:00
Leonard Hecker
61e6927f34 Fix various issues with useBackgroundImageForWindow (#13090)
Fixes the following issues:
* `desktopWallpaper` not working
* switching tabs/panes causes the background to flicker
* settings preview having a transparent background

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

## Validation Steps Performed
Tested the 3 cases above. 

(cherry picked from commit 0630b18eff)
Service-Card-Id: 81868221
Service-Version: 1.14
2022-05-12 16:50:45 -07:00
Carlos Zamora
95693e9079 PGO: train 1.14 separately 2022-05-12 14:43:05 -07:00
Carlos Zamora
eb40b51b79 Fix the 'RunMakeKillTabs' PGO test (#13089)
## Summary of the Pull Request
For some reason, the PGO tests (specifically the `RunMakeKillTabs` test) started to fail after #12979 merged. After closer inspection, the test was actually improperly written. We should be using <kbd>ctrl+shift+t</kbd> to open new tabs, not <kbd>alt+shift+t</kbd>. Presumably, the <kbd>alt</kbd> was copied over from the previous test, because they look _very_ similar.

So I went ahead and fixed the test, and it now (1) tests what it's intended to test and (2) doesn't fail. Why did #12979 cause the tests to fail? idk, but it works now.

## References
#10071 - Introduce PGO Tests

## Validation Steps Performed
Ran PGO tests locally and confirmed that it works.
Ran PGO pipeline and confirmed that it works.

(cherry picked from commit 17d1e2437c)
Service-Card-Id: 81863976
Service-Version: 1.14
2022-05-12 14:40:21 -07:00
James Holderness
521d968a75 Restore virtual viewport position after resize with reflow (#13087)
When the buffer is resized with a reflow, we were previously calculating
the new virtual bottom based on the position of last non-space
character. If the viewport was largely blank when resized, this could
result in the new virtual bottom being higher than it should be.

This PR attempts to address that problem by restoring the virtual bottom
to a position that is the same distance from the cursor row as it was
prior to the resize.

This was a regression introduced in PR #12972.

We still take the last non-space row into account when determining the
virtual bottom, because if the content of the screen is forced to wrap,
the virtual bottom will need to be lower (relative to the cursor) than
it was before.

We also need to check that we don't overflow the bottom of the buffer,
which can occur when the viewport is at the bottom of the buffer, and
the cursor position is pushed down as a result of content wrapping above
it.

I've manually confirmed that this fixes the problem reported in issue
#13078, and I've also extended the existing `RefreshWithReflow` unit
test to cover that particular scenario.

Closes #13078

(cherry picked from commit c2f830843a)
Service-Card-Id: 81864034
Service-Version: 1.14
2022-05-12 14:40:20 -07:00
37 changed files with 325 additions and 198 deletions

View File

@@ -30,7 +30,6 @@ DERR
dlldata
DONTADDTORECENT
DWORDLONG
DWMWA
endfor
enumset
environstrings
@@ -155,7 +154,6 @@ serializer
SETVERSION
SHELLEXECUTEINFOW
shobjidl
SHOWDEFAULT
SHOWHIDE
SHOWMINIMIZED
SHOWTIP

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.14</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

@@ -1708,6 +1708,11 @@
"description": "Force the terminal to use the legacy input encoding. Certain keys in some applications may stop working when enabling this setting.",
"type": "boolean"
},
"experimental.useBackgroundImageForWindow": {
"default": false,
"description": "When set to true, the background image for the currently focused profile is expanded to encompass the entire window, beneath other panes.",
"type": "boolean"
},
"initialCols": {
"default": 120,
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",

View File

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

View File

@@ -199,7 +199,6 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
FORWARDED_TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, Initialized);
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);

View File

@@ -122,7 +122,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> Initialized;
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;

View File

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

View File

@@ -218,6 +218,24 @@ namespace winrt::TerminalApp::implementation
// Inform the host that our titlebar content has changed.
_SetTitleBarContentHandlers(*this, _tabRow);
// GH#13143 Manually set the tab row's background to transparent here.
//
// We're doing it this way because ThemeResources are tricky. We
// default in XAML to using the appropriate ThemeResource background
// color for our TabRow. When tabs in the titlebar are _disabled_,
// this will ensure that the tab row has the correct theme-dependent
// value. When tabs in the titlebar are _enabled_ (the default),
// we'll switch the BG to Transparent, to let the Titlebar Control's
// background be used as the BG for the tab row.
//
// We can't do it the other way around (default to Transparent, only
// switch to a color when disabling tabs in the titlebar), because
// looking up the correct ThemeResource from and App dictionary is a
// capital-H Hard problem.
const auto transparent = Media::SolidColorBrush();
transparent.Color(Windows::UI::Colors::Transparent());
_tabRow.Background(transparent);
}
// Hookup our event handlers to the ShortcutActionDispatch
@@ -659,7 +677,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPage::_CompleteInitialization()
void TerminalPage::_CompleteInitialization()
{
_startupState = StartupState::Initialized;
@@ -679,55 +697,10 @@ namespace winrt::TerminalApp::implementation
if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener))
{
_LastTabClosedHandlers(*this, nullptr);
co_return;
}
else
{
// GH#11561: When we start up, our window is initially just a frame
// with a transparent content area. We're gonna do all this startup
// init on the UI thread, so the UI won't actually paint till it's
// all done. This results in a few frames where the frame is
// visible, before the page paints for the first time, before any
// tabs appears, etc.
//
// To mitigate this, we're gonna wait for the UI thread to finish
// everything it's gotta do for the initial init, and _then_ fire
// our Initialized event. By waiting for everything else to finish
// (CoreDispatcherPriority::Low), we let all the tabs and panes
// actually get created. In the window layer, we're gonna cloak the
// window till this event is fired, so we don't actually see this
// frame until we're actually all ready to go.
//
// This will result in the window seemingly not loading as fast, but
// it will actually take exactly the same amount of time before it's
// usable.
//
// We also experimented with drawing a solid BG color before the
// initialization is finished. However, there are still a few frames
// after the frame is displayed before the XAML content first draws,
// so that didn't actually resolve any issues.
auto weak{ get_weak() };
// Switch to the BG thread -
co_await winrt::resume_background();
if (auto self{ weak.get() })
{
// Then enqueue the rest of this function for after the UI thread settles.
co_await wil::resume_foreground(self->Dispatcher(), CoreDispatcherPriority::Low);
}
else
{
// We don't exist anymore? Well, we probably don't need to fire
// off an Initialized event then...
co_return;
}
if (auto self{ weak.get() })
{
self->_InitializedHandlers(*self, nullptr);
}
_InitializedHandlers(*this, nullptr);
}
}
@@ -1849,9 +1822,17 @@ namespace winrt::TerminalApp::implementation
// Instead, let's just promote this first split to be a tab instead.
// Crash avoided, and we don't need to worry about inserting a new-tab
// command in at the start.
if (!focusedTab && _tabs.Size() == 0)
if (!focusedTab)
{
_CreateNewTabFromPane(newPane);
if (_tabs.Size() == 0)
{
_CreateNewTabFromPane(newPane);
}
else
{
// The focused tab isn't a terminal tab
return;
}
}
else
{
@@ -2476,7 +2457,10 @@ namespace winrt::TerminalApp::implementation
TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection };
// GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now.
term.WindowVisibilityChanged(_visible);
if (_visible)
{
term.WindowVisibilityChanged(_visible);
}
if (_hostingHwnd.has_value())
{
@@ -2596,7 +2580,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance)
{
if (newAppearance.BackgroundImagePath().empty())
const auto path = newAppearance.ExpandedBackgroundImagePath();
if (path.empty())
{
_tabContent.Background(nullptr);
return;
@@ -2605,7 +2590,7 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Uri imageUri{ nullptr };
try
{
imageUri = Windows::Foundation::Uri{ newAppearance.BackgroundImagePath() };
imageUri = Windows::Foundation::Uri{ path };
}
catch (...)
{
@@ -2622,7 +2607,7 @@ namespace winrt::TerminalApp::implementation
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
imageSource.UriSource().RawUri() != imageUri.RawUri())
!imageSource.UriSource().Equals(imageUri))
{
Media::ImageBrush b{};
// Note that BitmapImage handles the image load asynchronously,

View File

@@ -147,7 +147,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(AlwaysOnTopChanged, IInspectable, IInspectable);
TYPED_EVENT(RaiseVisualBell, IInspectable, IInspectable);
TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
TYPED_EVENT(Initialized, IInspectable, IInspectable);
TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable);
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
@@ -390,7 +390,7 @@ namespace winrt::TerminalApp::implementation
void _StartInboundListener();
winrt::fire_and_forget _CompleteInitialization();
void _CompleteInitialization();
void _FocusActiveControl(IInspectable sender, IInspectable eventArgs);

View File

@@ -51,7 +51,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> Initialized;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.RoutedEventArgs> Initialized;
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;

View File

@@ -311,13 +311,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, flags, &_inPipe, &_outPipe, &_hPC));
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
if (_initialParentHwnd != 0)
{
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
}
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
if (_initialVisibility)
{
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
}
THROW_IF_FAILED(_LaunchAttachedClient());
}
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
@@ -336,6 +340,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
if (_initialVisibility)
{
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
}
}
_startTime = std::chrono::high_resolution_clock::now();

View File

@@ -73,7 +73,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
hstring _commandline{};
hstring _startingDirectory{};
hstring _startingTitle{};
bool _initialVisibility{ false };
bool _initialVisibility{ true };
Windows::Foundation::Collections::ValueSet _environment{ nullptr };
guid _guid{}; // A unique session identifier for connected client
hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).

View File

@@ -1210,8 +1210,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::_terminalShowWindowChanged(bool showOrHide)
{
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
_ShowWindowChangedHandlers(*this, *showWindow);
if (_initializedTerminal)
{
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
_ShowWindowChangedHandlers(*this, *showWindow);
}
}
bool ControlCore::HasSelection() const
@@ -1712,10 +1715,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::WindowVisibilityChanged(const bool showOrHide)
{
// show is true, hide is false
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
if (_initializedTerminal)
{
conpty.ShowHide(showOrHide);
// show is true, hide is false
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ShowHide(showOrHide);
}
}
}

View File

@@ -426,7 +426,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
imageSource.UriSource().RawUri() != imageUri.RawUri())
!imageSource.UriSource().Equals(imageUri))
{
// Note that BitmapImage handles the image load asynchronously,
// which is especially important since the image

View File

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

View File

@@ -9,13 +9,43 @@
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Background="{ThemeResource SettingsPageBackground}"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml" />
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<!--
These two values are "SolidBackgroundFillColorTertiary".
We can't just put those here though as
StaticResources, because XAML will evaluate their
values when the App is first loaded, and we'll
always use the value from the OS theme, regardless
of the requested theme. Kinda the same thing we've
had to do with TabViewBackground in the past.
-->
<ResourceDictionary x:Key="Dark">
<Color x:Key="SettingsPageBackground">#282828</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="SettingsPageBackground">#F9F9F9</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- Define resources for HighContrast mode here -->
<StaticResource x:Key="SettingsPageBackground"
ResourceKey="SystemColorWindowColorBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground"
@@ -30,7 +60,7 @@
</Page.Resources>
<muxc:NavigationView x:Name="SettingsNav"
Background="Transparent"
Background="{ThemeResource SettingsPageBackground}"
IsBackButtonVisible="Collapsed"
IsSettingsVisible="False"
ItemInvoked="SettingsNav_ItemInvoked"

View File

@@ -107,7 +107,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Model::TerminalSettings ProfileViewModel::TermSettings() const
{
return Model::TerminalSettings::CreateWithProfile(_appSettings, _profile, nullptr).DefaultSettings();
return Model::TerminalSettings::CreateForPreview(_appSettings, _profile);
}
// Method Description:

View File

@@ -50,6 +50,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return { horizAlign, vertAlign };
}
winrt::com_ptr<implementation::TerminalSettings> TerminalSettings::_CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
{
auto settings{ winrt::make_self<TerminalSettings>() };
const auto globals = appSettings.GlobalSettings();
settings->_ApplyProfileSettings(profile);
settings->_ApplyGlobalSettings(globals);
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes());
return settings;
}
Model::TerminalSettings TerminalSettings::CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
{
const auto settings = _CreateWithProfileCommon(appSettings, profile);
settings->UseBackgroundImageForWindow(false);
return *settings;
}
// Method Description:
// - Create a TerminalSettingsCreateResult for the provided profile guid. We'll
// use the guid to look up the profile that should be used to
@@ -64,17 +83,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// one for when the terminal is focused and the other for when the terminal is unfocused
Model::TerminalSettingsCreateResult TerminalSettings::CreateWithProfile(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const IKeyBindings& keybindings)
{
auto settings{ winrt::make_self<TerminalSettings>() };
const auto settings = _CreateWithProfileCommon(appSettings, profile);
settings->_KeyBindings = keybindings;
const auto globals = appSettings.GlobalSettings();
settings->_ApplyProfileSettings(profile);
settings->_ApplyGlobalSettings(globals);
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes());
Model::TerminalSettings child{ nullptr };
if (const auto& unfocusedAppearance{ profile.UnfocusedAppearance() })
{
const auto globals = appSettings.GlobalSettings();
auto childImpl = settings->CreateChild();
childImpl->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes());
child = *childImpl;

View File

@@ -55,6 +55,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
TerminalSettings() = default;
static Model::TerminalSettings CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
static Model::TerminalSettingsCreateResult CreateWithProfile(const Model::CascadiaSettings& appSettings,
const Model::Profile& profile,
const Control::IKeyBindings& keybindings);
@@ -159,6 +161,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
private:
std::optional<std::array<Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE>> _ColorTable;
gsl::span<Microsoft::Terminal::Core::Color> _getColorTableImpl();
static winrt::com_ptr<implementation::TerminalSettings> _CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
void _ApplyProfileSettings(const Model::Profile& profile);
void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept;

View File

@@ -26,6 +26,7 @@ namespace Microsoft.Terminal.Settings.Model
{
TerminalSettings();
static TerminalSettings CreateForPreview(CascadiaSettings appSettings, Profile profile);
static TerminalSettingsCreateResult CreateWithProfile(CascadiaSettings appSettings, Profile profile, Microsoft.Terminal.Control.IKeyBindings keybindings);
static TerminalSettingsCreateResult CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings);

View File

@@ -73,7 +73,8 @@ AppHost::AppHost() noexcept :
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
this,
std::placeholders::_1,
std::placeholders::_2);
std::placeholders::_2,
std::placeholders::_3);
_window->SetCreateCallback(pfn);
_window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::AppLogic::CalcSnappedDimension,
@@ -116,6 +117,19 @@ AppHost::AppHost() noexcept :
{
_BecomeMonarch(nullptr, nullptr);
}
// Create a throttled function for updating the window state, to match the
// one requested by the pty. A 200ms delay was chosen because it's the
// typical animation timeout in Windows. This does result in a delay between
// the PTY requesting a change to the window state and the Terminal
// realizing it, but should mitigate issues where the Terminal and PTY get
// de-sync'd.
_showHideWindowThrottler = std::make_shared<ThrottledFuncTrailing<bool>>(
winrt::Windows::System::DispatcherQueue::GetForCurrentThread(),
std::chrono::milliseconds(200),
[this](const bool show) {
_window->ShowWindowChanged(show);
});
}
AppHost::~AppHost()
@@ -128,6 +142,8 @@ AppHost::~AppHost()
// out the window, then close the app.
_revokers = {};
_showHideWindowThrottler.reset();
_window = nullptr;
_app.Close();
_app = nullptr;
@@ -378,7 +394,6 @@ void AppHost::Initialize()
_revokers.FullscreenChanged = _logic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged });
_revokers.FocusModeChanged = _logic.FocusModeChanged(winrt::auto_revoke, { this, &AppHost::_FocusModeChanged });
_revokers.AlwaysOnTopChanged = _logic.AlwaysOnTopChanged(winrt::auto_revoke, { this, &AppHost::_AlwaysOnTopChanged });
_revokers.Initialized = _logic.Initialized(winrt::auto_revoke, { this, &AppHost::_AppInitializedHandler });
_revokers.RaiseVisualBell = _logic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell });
_revokers.SystemMenuChangeRequested = _logic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested });
_revokers.ChangeMaximizeRequested = _logic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested });
@@ -520,31 +535,12 @@ LaunchPosition AppHost::_GetWindowLaunchPosition()
return pos;
}
// Method Description:
// - Callback for when the window is first being created (during WM_CREATE).
// Stash the proposed size for later. We'll need that once we're totally
// initialized, so that we can show the window in the right position *when we
// want to show it*. If we did the _initialResizeAndRepositionWindow work now,
// it would have no effect, because the window is not yet visible.
// Arguments:
// - hwnd: The HWND of the window we're about to create.
// - proposedRect: The location and size of the window that we're about to
// create. We'll use this rect to determine which monitor the window is about
// to appear on.
void AppHost::_HandleCreateWindow(const HWND /* hwnd */, RECT proposedRect)
{
// GH#11561: Hide the window until we're totally done being initialized.
// More commentary in TerminalPage::_CompleteInitialization
_proposedRect = proposedRect;
}
// Method Description:
// - Resize the window we're about to create to the appropriate dimensions, as
// specified in the settings. This is called once the app has finished it's
// initial setup, once we have created all the tabs, panes, etc. We'll load
// the settings for the app, then get the proposed size of the terminal from
// the app. Using that proposed size, we'll resize the window we're creating,
// so that it'll match the values in the settings.
// specified in the settings. This will be called during the handling of
// WM_CREATE. We'll load the settings for the app, then get the proposed size
// of the terminal from the app. Using that proposed size, we'll resize the
// window we're creating, so that it'll match the values in the settings.
// Arguments:
// - hwnd: The HWND of the window we're about to create.
// - proposedRect: The location and size of the window that we're about to
@@ -553,7 +549,7 @@ void AppHost::_HandleCreateWindow(const HWND /* hwnd */, RECT proposedRect)
// - launchMode: A LaunchMode enum reference that indicates the launch mode
// Return Value:
// - None
void AppHost::_initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, LaunchMode& launchMode)
void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode& launchMode)
{
launchMode = _logic.GetLaunchMode();
@@ -1416,7 +1412,14 @@ void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&,
void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Microsoft::Terminal::Control::ShowWindowArgs& args)
{
_window->ShowWindowChanged(args.ShowOrHide());
// GH#13147: Enqueue a throttled update to our window state. Throttling
// should prevent scenarios where the Terminal window state and PTY window
// state get de-sync'd, and cause the window to minimize/restore constantly
// in a loop.
if (_showHideWindowThrottler)
{
_showHideWindowThrottler->Run(args.ShowOrHide());
}
}
void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
@@ -1575,22 +1578,3 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*
const auto pos = _GetWindowLaunchPosition();
_logic.CloseWindow(pos);
}
void AppHost::_AppInitializedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*arg*/)
{
// GH#11561: We're totally done being initialized. Resize the window to
// match the initial settings, and then call ShowWindow to finally make us
// visible.
LaunchMode launchMode{};
_initialResizeAndRepositionWindow(_window->GetHandle(), _proposedRect, launchMode);
auto nCmdShow = SW_SHOWDEFAULT;
if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode))
{
nCmdShow = SW_MAXIMIZE;
}
ShowWindow(_window->GetHandle(), nCmdShow);
}

View File

@@ -4,7 +4,7 @@
#include "pch.h"
#include "NonClientIslandWindow.h"
#include "NotificationIcon.h"
#include <til/throttled_func.h>
#include <ThrottledFunc.h>
class AppHost
{
@@ -31,16 +31,16 @@ private:
bool _shouldCreateWindow{ false };
bool _useNonClientArea{ false };
RECT _proposedRect{};
std::optional<til::throttled_func_trailing<>> _getWindowLayoutThrottler;
std::shared_ptr<ThrottledFuncTrailing<bool>> _showHideWindowThrottler;
winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts();
winrt::fire_and_forget _SaveWindowLayoutsRepeat();
void _HandleCommandlineArgs();
winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition();
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect);
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::UIElement& arg);
void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&,
@@ -53,9 +53,6 @@ private:
const winrt::Windows::Foundation::IInspectable& arg);
void _AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& arg);
void _AppInitializedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& arg);
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& arg);
void _WindowMouseWheeled(const til::point coord, const int32_t delta);
@@ -125,9 +122,6 @@ private:
const winrt::Windows::Foundation::IInspectable& args);
void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
std::unique_ptr<NotificationIcon> _notificationIcon;
winrt::event_token _ReAddNotificationIconToken;
winrt::event_token _NotificationIconPressedToken;
@@ -154,7 +148,6 @@ private:
winrt::TerminalApp::AppLogic::FullscreenChanged_revoker FullscreenChanged;
winrt::TerminalApp::AppLogic::FocusModeChanged_revoker FocusModeChanged;
winrt::TerminalApp::AppLogic::AlwaysOnTopChanged_revoker AlwaysOnTopChanged;
winrt::TerminalApp::AppLogic::Initialized_revoker Initialized;
winrt::TerminalApp::AppLogic::RaiseVisualBell_revoker RaiseVisualBell;
winrt::TerminalApp::AppLogic::SystemMenuChangeRequested_revoker SystemMenuChangeRequested;
winrt::TerminalApp::AppLogic::ChangeMaximizeRequested_revoker ChangeMaximizeRequested;

View File

@@ -112,7 +112,7 @@ void IslandWindow::Close()
// window.
// Return Value:
// - <none>
void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept
void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT, LaunchMode& launchMode)> pfn) noexcept
{
_pfnCreateCallback = pfn;
}
@@ -154,13 +154,19 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce
rc.right = rc.left + pcs->cx;
rc.bottom = rc.top + pcs->cy;
auto launchMode = LaunchMode::DefaultMode;
if (_pfnCreateCallback)
{
_pfnCreateCallback(_window.get(), rc);
_pfnCreateCallback(_window.get(), rc, launchMode);
}
// GH#11561: DO NOT call ShowWindow here. The AppHost will call ShowWindow
// once the app has completed its initialization.
auto nCmdShow = SW_SHOW;
if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode))
{
nCmdShow = SW_MAXIMIZE;
}
ShowWindow(_window.get(), nCmdShow);
UpdateWindow(_window.get());

View File

@@ -37,7 +37,7 @@ public:
virtual void Initialize();
void SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept;
void SetCreateCallback(std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> pfn) noexcept;
void SetSnapDimensionCallback(std::function<float(bool widthOrHeight, float dimension)> pfn) noexcept;
void FocusModeChanged(const bool focusMode);
@@ -94,7 +94,7 @@ protected:
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
wil::com_ptr<ITaskbarList3> _taskbar;
std::function<void(const HWND, const RECT)> _pfnCreateCallback;
std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> _pfnCreateCallback;
std::function<float(bool, float)> _pfnSnapDimensionCallback;
void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;

View File

@@ -155,13 +155,13 @@ namespace WindowsTerminal.UIA.Tests
{
var root = app.GetRoot();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();

View File

@@ -75,11 +75,20 @@ void PtySignalInputThread::ConnectConsole() noexcept
_DoShowHide(_initialShowHide->show);
}
// If we were given a owner HWND, then manually start the pseudo window now.
if (_earlyReparent)
{
_DoSetWindowParent(*_earlyReparent);
}
// We should have successfully used the _earlyReparent message in CreatePseudoWindow.
}
// Method Description:
// - Create our pseudo window. We're doing this here, instead of in
// ConnectConsole, because the window is created in
// ConsoleInputThreadProcWin32, before ConnectConsole is first called. Doing
// this here ensures that the window is first created with the initial owner
// set up (if so specified).
// - Refer to GH#13066 for details.
void PtySignalInputThread::CreatePseudoWindow()
{
HWND owner = _earlyReparent.has_value() ? reinterpret_cast<HWND>((*_earlyReparent).handle) : HWND_DESKTOP;
ServiceLocator::LocatePseudoWindow(owner);
}
// Method Description:
@@ -227,15 +236,28 @@ void PtySignalInputThread::_DoShowHide(const bool show)
// - <none>
void PtySignalInputThread::_DoSetWindowParent(const SetParentData& data)
{
const auto owner{ reinterpret_cast<HWND>(data.handle) };
// This will initialize s_interactivityFactory for us. It will also
// conveniently return 0 when we're on OneCore.
//
// If the window hasn't been created yet, by some other call to
// LocatePseudoWindow, then this will also initialize the owner of the
// window.
if (const auto pseudoHwnd{ ServiceLocator::LocatePseudoWindow(reinterpret_cast<HWND>(data.handle)) })
if (const auto pseudoHwnd{ ServiceLocator::LocatePseudoWindow(owner) })
{
LOG_LAST_ERROR_IF_NULL(::SetParent(pseudoHwnd, reinterpret_cast<HWND>(data.handle)));
// DO NOT USE SetParent HERE!
//
// Calling SetParent on a window that is WS_VISIBLE will cause the OS to
// hide the window, make it a _child_ window, then call SW_SHOW on the
// window to re-show it. SW_SHOW, however, will cause the OS to also set
// that window as the _foreground_ window, which would result in the
// pty's hwnd stealing the foreground away from the owning terminal
// window. That's bad.
//
// SetWindowLongPtr seems to do the job of changing who the window owner
// is, without all the other side effects of reparenting the window.
// See #13066
::SetWindowLongPtr(pseudoHwnd, GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(owner));
}
}

View File

@@ -33,6 +33,7 @@ namespace Microsoft::Console
PtySignalInputThread& operator=(const PtySignalInputThread&) = delete;
void ConnectConsole() noexcept;
void CreatePseudoWindow();
private:
enum class PtySignal : unsigned short

View File

@@ -300,18 +300,34 @@ bool VtIo::IsUsingVt() const
if (_pPtySignalInputThread)
{
// IMPORTANT! Start the pseudo window on this thread. This thread has a
// message pump. If you DON'T, then a DPI change in the owning hwnd will
// cause us to get a dpi change as well, which we'll never deque and
// handle, effectively HANGING THE OWNER HWND.
// Let the signal thread know that the console is connected.
//
// Let the signal thread know that the console is connected
// By this point, the pseudo window should have already been created, by
// ConsoleInputThreadProcWin32. That thread has a message pump, which is
// needed to ensure that DPI change messages to the owning terminal
// window don't end up hanging because the pty didn't also process it.
_pPtySignalInputThread->ConnectConsole();
}
return S_OK;
}
// Method Description:
// - Create our pseudo window. This is exclusively called by
// ConsoleInputThreadProcWin32 on the console input thread.
// * It needs to be called on that thread, before any other calls to
// LocatePseudoWindow, to make sure that the input thread is the HWND's
// message thread.
// * It needs to be plumbed through the signal thread, because the signal
// thread knows if someone should be marked as the window's owner. It's
// VERY IMPORTANT that any initial owners are set up when the window is
// first created.
// - Refer to GH#13066 for details.
void VtIo::CreatePseudoWindow()
{
_pPtySignalInputThread->CreatePseudoWindow();
}
// Method Description:
// - Create and start the signal thread. The signal thread can be created
// independent of the i/o threads, and doesn't require a client first

View File

@@ -52,6 +52,8 @@ namespace Microsoft::Console::VirtualTerminal
[[nodiscard]] HRESULT ManuallyClearScrollback() const noexcept;
void CreatePseudoWindow();
private:
// After CreateIoHandlers is called, these will be invalid.
wil::unique_hfile _hInput;

View File

@@ -719,6 +719,10 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
position.Y < 0));
// clang-format on
// MSFT: 15813316 - Try to use this SetCursorPosition call to inherit the cursor position.
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(position));
RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true));
LOG_IF_FAILED(ConsoleImeResizeCompStrView());

View File

@@ -285,7 +285,7 @@ void ConhostInternalGetSet::ShowWindow(bool showOrHide)
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto hwnd = gci.IsInVtIoMode() ? ServiceLocator::LocatePseudoWindow() : ServiceLocator::LocateConsoleWindow()->GetWindowHandle();
::ShowWindow(hwnd, showOrHide ? SW_NORMAL : SW_MINIMIZE);
::ShowWindow(hwnd, showOrHide ? SW_SHOWNOACTIVATE : SW_MINIMIZE);
}
// Routine Description:

View File

@@ -1453,6 +1453,8 @@ bool SCREEN_INFORMATION::IsMaximizedY() const
// Save cursor's relative height versus the viewport
const auto sCursorHeightInViewportBefore = _textBuffer->GetCursor().GetPosition().Y - _viewport.Top();
// Also save the distance to the virtual bottom so it can be restored after the resize
const auto cursorDistanceFromBottom = _virtualBottom - _textBuffer->GetCursor().GetPosition().Y;
// skip any drawing updates that might occur until we swap _textBuffer with the new buffer or we exit early.
newTextBuffer->GetCursor().StartDeferDrawing();
@@ -1464,14 +1466,20 @@ bool SCREEN_INFORMATION::IsMaximizedY() const
if (SUCCEEDED(hr))
{
// Make sure the new virtual bottom is far enough down to include both
// the cursor row and the last non-space row. It also shouldn't be less
// than the height of the viewport, otherwise the top of the virtual
// viewport would end up negative.
// Since the reflow doesn't preserve the virtual bottom, we try and
// estimate where it ought to be by making it the same distance from
// the cursor row as it was before the resize. However, we also need
// to make sure it is far enough down to include the last non-space
// row, and it shouldn't be less than the height of the viewport,
// otherwise the top of the virtual viewport would end up negative.
const auto cursorRow = newTextBuffer->GetCursor().GetPosition().Y;
const auto lastNonSpaceRow = newTextBuffer->GetLastNonSpaceCharacter().Y;
const auto estimatedBottom = gsl::narrow_cast<short>(cursorRow + cursorDistanceFromBottom);
const auto viewportBottom = gsl::narrow_cast<short>(_viewport.Height() - 1);
_virtualBottom = std::max({ cursorRow, lastNonSpaceRow, viewportBottom });
_virtualBottom = std::max({ lastNonSpaceRow, estimatedBottom, viewportBottom });
// We can't let it extend past the bottom of the buffer either.
_virtualBottom = std::min(_virtualBottom, newTextBuffer->GetSize().BottomInclusive());
// Adjust the viewport so the cursor doesn't wildly fly off up or down.
const auto sCursorHeightInViewportAfter = cursorRow - _viewport.Top();

View File

@@ -6291,6 +6291,18 @@ void ScreenBufferTests::UpdateVirtualBottomAfterResizeWithReflow()
Log::Comment(L"Confirm that the virtual viewport includes the last non-space row");
const auto lastNonSpaceRow = si.GetTextBuffer().GetLastNonSpaceCharacter().Y;
VERIFY_IS_GREATER_THAN_OR_EQUAL(si._virtualBottom, lastNonSpaceRow);
Log::Comment(L"Clear the screen and note the cursor distance to the virtual bottom");
stateMachine.ProcessString(L"\033[H\033[2J");
const auto cursorDistanceFromBottom = si._virtualBottom - si.GetTextBuffer().GetCursor().GetPosition().Y;
VERIFY_ARE_EQUAL(si.GetViewport().Height() - 1, cursorDistanceFromBottom);
Log::Comment(L"Stretch the viewport back to full width");
bufferSize.X *= 2;
VERIFY_NT_SUCCESS(si.ResizeWithReflow(bufferSize));
Log::Comment(L"Confirm cursor distance to the virtual bottom is unchanged");
VERIFY_ARE_EQUAL(cursorDistanceFromBottom, si._virtualBottom - si.GetTextBuffer().GetCursor().GetPosition().Y);
}
void ScreenBufferTests::DontShrinkVirtualBottomDuringResizeWithReflowAtTop()

View File

@@ -320,8 +320,14 @@ using namespace Microsoft::Console::Interactivity;
// as far as the difference between parent/child and owner/owned
// windows). Evan K said we should do it this way, and he
// definitely knows.
const auto windowStyle = WS_OVERLAPPEDWINDOW;
const auto exStyles = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED;
//
// GH#13066: Load-bearing: Make sure to set WS_POPUP. If you
// don't, then GetAncestor(GetConsoleWindow(), GA_ROOTOWNER)
// will return the console handle again, not the owning
// terminal's handle. It's not entirely clear why, but WS_POPUP
// is absolutely vital for this to work correctly.
const auto windowStyle = WS_OVERLAPPEDWINDOW | WS_POPUP;
const auto exStyles = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE;
// Attempt to create window.
hwnd = CreateWindowExW(exStyles,
@@ -335,7 +341,7 @@ using namespace Microsoft::Console::Interactivity;
owner,
nullptr,
nullptr,
nullptr);
this);
if (hwnd == nullptr)
{
@@ -472,7 +478,18 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function<void(bool)> fun
// - <none>
void InteractivityFactory::_WritePseudoWindowCallback(bool showOrHide)
{
if (_pseudoWindowMessageCallback)
// BODGY
//
// GH#13158 - At least temporarily, only allow the PTY to HIDE the terminal
// window. There seem to be many issues with this so far, and the quickest
// route to mitigate them seems to be limiting the interaction here to
// allowing ConPTY to minimize the terminal only. This will still allow
// applications to hide the Terminal via GetConsoleWindow(), but should
// broadly prevent any other impact of this feature.
//
// Should we need to restore this functionality in the future, we should
// only do so with great caution.
if (_pseudoWindowMessageCallback && showOrHide == false)
{
_pseudoWindowMessageCallback(showOrHide);
}

View File

@@ -1036,9 +1036,18 @@ DWORD WINAPI ConsoleInputThreadProcWin32(LPVOID /*lpParameter*/)
// If we are headless (because we're a pseudo console), we
// will still need a window handle in the win32 environment
// in case anyone sends messages at that HWND (vim.exe is an example.)
// We have to CreateWindow on the same thread that will pump the messages
// which is this thread.
ServiceLocator::LocatePseudoWindow();
//
// IMPORTANT! We have to CreateWindow on the same thread that will pump
// the messages, which is this thread. If you DON'T, then a DPI change
// in the owning hwnd will cause us to get a dpi change as well, which
// we'll never deque and handle, effectively HANGING THE OWNER HWND.
// ServiceLocator::LocatePseudoWindow();
//
// Instead of just calling LocatePseudoWindow, make sure to go through
// VtIo's CreatePseudoWindow, which will make sure that the window is
// successfully created with the owner configured when the window is
// first created. See GH#13066 for details.
ServiceLocator::LocateGlobals().getConsoleInformation().GetVtIo()->CreatePseudoWindow();
}
UnlockConsole();

View File

@@ -174,9 +174,15 @@ constexpr HRESULT vec2_narrow(U x, U y, AtlasEngine::vec2<T>& out) noexcept
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept
{
_api.cellCount.x = gsl::narrow_cast<u16>(srNewViewport.Right - srNewViewport.Left + 1);
_api.cellCount.y = gsl::narrow_cast<u16>(srNewViewport.Bottom - srNewViewport.Top + 1);
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
const u16x2 cellCount{
gsl::narrow_cast<u16>(srNewViewport.Right - srNewViewport.Left + 1),
gsl::narrow_cast<u16>(srNewViewport.Bottom - srNewViewport.Top + 1),
};
if (_api.cellCount != cellCount)
{
_api.cellCount = cellCount;
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
}
return S_OK;
}

View File

@@ -150,15 +150,10 @@ bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col)
const auto coordCursorShort = til::unwrap_coord(coordCursor);
// MSFT: 15813316 - Try to use this MoveCursor call to inherit the cursor position.
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(coordCursorShort));
// Finally, attempt to set the adjusted cursor position back into the console.
auto& cursor = _api.GetTextBuffer().GetCursor();
cursor.SetPosition(coordCursorShort);
cursor.SetHasMoved(true);
return true;
const auto api = gsl::not_null{ ServiceLocator::LocateGlobals().api };
auto& info = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer();
return SUCCEEDED(api->SetConsoleCursorPositionImpl(info, coordCursorShort));
}
// Routine Description:
@@ -204,11 +199,19 @@ bool InteractDispatch::FocusChanged(const bool focused) const
{
// They want focus, we found a pseudo hwnd.
// Note: ::GetParent(pseudoHwnd) will return 0. GetAncestor works though.
// GA_PARENT and GA_ROOT seemingly return the same thing for
// Terminal. We're going with GA_ROOT since it seems
// semantically more correct here.
if (const auto ownerHwnd{ ::GetAncestor(pseudoHwnd, GA_ROOT) })
// BODGY
//
// This needs to be GA_ROOTOWNER here. Not GA_ROOT, GA_PARENT,
// or GetParent. The ConPTY hwnd is an owned, top-level, popup,
// non-parented window. It does not have a parent set. It does
// have an owner set. It is not a WS_CHILD window. This
// combination of things allows us to find the owning window
// with GA_ROOTOWNER. GA_ROOT will get us ourselves, and
// GA_PARENT will return the desktop HWND.
//
// See GH#13066
if (const auto ownerHwnd{ ::GetAncestor(pseudoHwnd, GA_ROOTOWNER) })
{
// We have an owner from a previous call to ReparentWindow