Repository navigation
Handle window resize event (CSI t, resize) - #17721
Conversation
|
@microsoft-github-policy-service agree |
Leonard Hecker (lhecker)
left a comment
There was a problem hiding this comment.
Nice PR! Thank you so much for working on this! 😻
I found just 1 potential issue (the lack of a weak-ref) and otherwise only a couple nits. We don't use a weak-ref elsewhere in the class either, so this isn't something I'd block over.
|
|
||
| winrt::fire_and_forget TerminalPage::_WindowSizeChanged(const IInspectable /*sender*/, const Microsoft::Terminal::Control::WindowSizeChangedEventArgs args) | ||
| { | ||
| co_await wil::resume_foreground(Dispatcher()); |
There was a problem hiding this comment.
Since you already switched to the UI thread in TermControl you won't need to do it again here.
Oher functions in this file do it, because our event handling code hasn't been cleaned up in a while and so some functions switch to the UI thread here and some in TermControl. I think it's better to do it in TermControl, because that makes it safer and more consistent.
|
|
||
| winrt::fire_and_forget TermControl::_bubbleWindowSizeChanged(const IInspectable& /*sender*/, const Control::WindowSizeChangedEventArgs args) | ||
| { | ||
| co_await wil::resume_foreground(Dispatcher()); |
There was a problem hiding this comment.
This will need a weak-ref to this so that we can safely check if the class instance went away while we switched to the UI thread. Like this (you can change the code style / variable names to whatever you prefer):
terminal/src/cascadia/TerminalControl/TermControl.cpp
Lines 1010 to 1012 in 1511d2c
| { | ||
| co_await wil::resume_foreground(Dispatcher()); | ||
|
|
||
| til::size cellSize{ args.Width(), args.Height() }; |
There was a problem hiding this comment.
I think it'd be easier to understand if this was named cellCount (we use cellSize for the size of a cell in pixels elsewhere).
| const auto islandWidth = Utils::ClampToShortMax(lrintf(static_cast<float>(newSize.width)), 1); | ||
| const auto islandHeight = Utils::ClampToShortMax(lrintf(static_cast<float>(newSize.height)), 1); |
There was a problem hiding this comment.
The float-cast + lrintf seems redundant. I think this could be just
const auto islandWidth = Utils::ClampToShortMax(newSize.width, 1);
const auto islandHeight = Utils::ClampToShortMax(newSize.height, 1);|
You can fix the Import-Module .\tools\OpenConsole.psm1
Invoke-CodeFormat -IgnoreXamlDon't worry about the AuditMode failure. It broke because we updated our CI's MSVC version. I'm going to fix that soon. |
|
What happens if a size is requested that exceeds the "maximum" size of the window (maximum in quotes because I'm not sure exactly what the limit would be)? I don't know if strict bounds are necessary, but there might need to be some limit to avoid e.g. hardware-accelerated renderer crashes (IIRC GPUs generally can't handle surfaces larger than 32768 pixels in either dimension). |
|
Emanuel Hoogeveen (@ehoogeveen-medweb) At the end dimensions can't be larger than 32767 because there is clamping before calling |
|
I've just been testing this branch, and there appears to be an issue with the height calculation (I think only when you have the tab bar visible). Because when I request a height of 24, I actually end up with a height of 22. You can see this when toggling There is another issue which may be more difficult to fix, but it seems like there is a delay before the size change takes effect. For example, if you request a resize, and then immediately query the new size like this: I'm not sure how big a deal this would be in practice, but again you can see the effect in Vttest (even without the shrinking height problem mentioned above), because the output appears to be formatted as if it were in a window of the wrong size, I'm guessing because |
I fear we may have to fix that issue later on. The resize necessarily needs to happen on the UI thread, because not all state until the We could hotfix it by unlocking the console lock, raising the |
|
Apparently I'm not taking the title bar and tab bar height into account when calculating the window height. I noticed that there is some built-in logic in the |
|
Holy s--t, somebody actually did it! Thanks so much! We've had a lot of folks on vacation this week, but I'll make sure we get more reviewers on it next week. |
Mike Griese (zadjii-msft)
left a comment
There was a problem hiding this comment.
blocking over the resize rejection case, and the TermControl::GetProposedDimensions thing. If fixing the TermControl::GetProposedDimensions doesn't fix the weird off-by-one on the resize, I'm guessing it's cause of snapToGridOnResize. There's known issues with how it handles DPI: #4175
FWIW, on a 100% scale display, this does work like a charm 👌 but yea I'm consistently seeing... inconsitent off-by-ones on the 125% display. Flippin' weird.
| void TerminalWindow::_WindowSizeChanged(const IInspectable&, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args) | ||
| { | ||
| til::size cellCount{ args.Width(), args.Height() }; | ||
| const auto dpi = GetDpiForWindow(_hwnd); |
There was a problem hiding this comment.
IIRC const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); is the right way to get the DPI from inside XAML land. That would remove the need for the TerminalWindow to actually store it's HWND
There was a problem hiding this comment.
This is a temporary workaround that I had suggested. I think it's better to use GetDpiForWindow here because it more closely mirrors other, related code that also uses GetDpiForWindow. I'd like to clean this up all at once in the 1.23 period.
| // - Not in fullscreen | ||
| // - Only one tab exists | ||
| // - Only one pane exists | ||
| if (!_isFullscreen && NumberOfTabs() == 1 && _GetFocusedTabImpl()->GetLeafPaneCount() == 1) |
There was a problem hiding this comment.
🛑: If we reject this resize, we need to make sure to let conpty know. We need to do something to signal a UserResize() back to the conpty. Otherwise, conpty will go on it's merry way at the new size, with the terminal at the wrong size
There was a problem hiding this comment.
Is there any existing mechanism right now that signals back to conpty? Maybe I can check that to have some idea. Resizing is rejected also if the window is in quake mode (in AppHost). Probably that will also need to signal back.
There was a problem hiding this comment.
ConptyConnection::Resize is the method you're looking for. The trick is just plumbing the rejection back to the correct layer to actually call that.
It does look like TermControl projects it's Connection, so that might not be so hard to call.
| const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) }; | ||
| auto pixelSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi, cellCount.width, cellCount.height); |
There was a problem hiding this comment.
This might be problematic. The default settings for the default profile might not be what's currently active in that terminal window. Consider the scenario where you ctrl+mouse to increase the font size. The settings object doesn't actually know about that change, only the TermControl raising the event does.
I bet this could be solved by having the TermControl itself handle the WindowSizeChanged from the ControlCore (instead of straight bubbling it). The TermControl would get the size in chars, use that to figure out how big it wants to be in DIPs, then raise the size in DIPs up to the TerminalPage
|
(other than that though, the plumbing on this looks great, and overall I think you did cover a lot of the wacky weird edge cases like the tab row height well. Thanks for all this! Very excited to get this over the finish line) |
Thanks for review. I'll check what I can do about the issues you mentioned when I have time. |
After these changes there was no shrinking/growing problem at different DPI scales (at least on my setup). I hope there are no other flaws. Reviews are welcome. :) |
|
I realized there is no need to copy everything from |
The ResizeWindow event in TerminalApi is handled and bubbled to TerminalApi->ControlCore->TermControl->TerminalPage->TerminalWindow->AppHost. Resizing is accepted only if the window is not in fullscreen or quake mode, and has 1 tab and pane.
… of conpty if resizing is rejected
ab8d331 to
0deb984
Compare
Mike Griese (zadjii-msft)
left a comment
There was a problem hiding this comment.
Oh this is so close to ready, I can feel it. I'm tempted to just merge this as-is and fix that in post, but since we're not shipping this till 1.23, I don't think there's any big rush
| { | ||
| WindowSizeChanged.raise(*this, args); | ||
| } | ||
| else if (const auto& control{ _GetActiveControl() }) |
There was a problem hiding this comment.
Okay this is subtle, but this WindowSizeChanged handler might be being called for an inactive tab. So like, (as written) an inactive tab might get a CSI t, and then we'll reset the size of the focused tab, not the one that raised the event.
Fortunately, this is easy to handle! We've already set the sender of this event to the TermControl requesting the resize. So you can uncomment /*sender*/ above, and instead do:
| else if (const auto& control{ _GetActiveControl() }) | |
| else if (const auto& control{ sender.try_as<TermControl>() }) |
|
|
||
| if (_pfnWindowSizeChanged) | ||
| { | ||
| _pfnWindowSizeChanged(width, height); |
There was a problem hiding this comment.
Looks like the "audit mode" CI caught this:
src\cascadia\TerminalCore\TerminalApi.cpp(112,0): Error C26447: The function is declared 'noexcept' but calls function 'operator()()' which may throw exceptions (f.6).
I bet you can just remove noexcept from Terminal::ResizeWindow. or wrap it in a try{} CATCH_LOG()



ResizeWindowevent inTerminalApiis handled and bubbled toTerminalApi->ControlCore->TermControl->TerminalPage->AppHost. Resizing is accepted only if the window is not in fullscreen or quake mode, and has 1 tab and pane.Relevant issues: #5094