terminal/src/cascadia/WindowsTerminal/AppHost.cpp
Mike Griese fb597ed304
Add support for renaming windows (#9662)
## Summary of the Pull Request

This PR adds support for renaming windows.

![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif)
![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif)


It does so through two new actions:
* `renameWindow` takes a `name` parameter, and attempts to set the window's name
  to the provided name. This is useful if you always want to hit <kbd>F3</kbd>
  and rename a window to "foo" (READ: probably not that useful)
* `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a
  `TextBox`. When the user hits Ok, it'll request a rename for the provided
  value. This lets the user pick a new name for the window at runtime.

In both cases, if there's already a window with that name, then the monarch will
reject the rename, and pop a `Toast` in the window informing the user that the
rename failed. Nifty!

## References
* Builds on the toasts from #9523
* #5000 - process model megathread

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747
* [x] I work here
* [x] Tests addded (and pass with the help of #9660)
* [ ] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews.

> PAIN: We can't immediately focus the textbox in the TeachingTip. It's
> not technically focusable until it is opened. However, it doesn't
> provide an even tto tell us when it is opened. That's tracked in
> microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
> click on the text box manually.
> We're also not using a ContentDialog for this, because in Xaml
> Islands a text box in a ContentDialog won't recieve _any_ keypresses.
> Fun!

## Validation Steps Performed

I've been playing with 

```json
        { "keys": "f1", "command": "identifyWindow" },
        { "keys": "f2", "command": "identifyWindows" },
        { "keys": "f3", "command": "openWindowRenamer" },
        { "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } },
        { "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } },
```

and they seem to work as expected
2021-04-02 16:00:04 +00:00

677 lines
27 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AppHost.h"
#include "../types/inc/Viewport.hpp"
#include "../types/inc/utils.hpp"
#include "../types/inc/User32Utils.hpp"
#include "resource.h"
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Hosting;
using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Console::Types;
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
// "If the high-order bit is 1, the key is down; otherwise, it is up."
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
AppHost::AppHost() noexcept :
_app{},
_windowManager{},
_logic{ nullptr }, // don't make one, we're going to take a ref on app's
_window{ nullptr }
{
_logic = _app.Logic(); // get a ref to app's logic
// Inform the WindowManager that it can use us to find the target window for
// a set of commandline args. This needs to be done before
// _HandleCommandlineArgs, because WE might end up being the monarch. That
// would mean we'd need to be responsible for looking that up.
_windowManager.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow });
// If there were commandline args to our process, try and process them here.
// Do this before AppLogic::Create, otherwise this will have no effect.
//
// This will send our commandline to the Monarch, to ask if we should make a
// new window or not. If not, exit immediately.
_HandleCommandlineArgs();
if (!_shouldCreateWindow)
{
return;
}
_useNonClientArea = _logic.GetShowTabsInTitlebar();
if (_useNonClientArea)
{
_window = std::make_unique<NonClientIslandWindow>(_logic.GetRequestedTheme());
}
else
{
_window = std::make_unique<IslandWindow>();
}
// Tell the window to callback to us when it's about to handle a WM_CREATE
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3);
_window->SetCreateCallback(pfn);
_window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::AppLogic::CalcSnappedDimension,
_logic,
std::placeholders::_1,
std::placeholders::_2));
_window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled });
_window->WindowActivated({ this, &AppHost::_WindowActivated });
_window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop());
_window->MakeWindow();
}
AppHost::~AppHost()
{
// destruction order is important for proper teardown here
_window = nullptr;
_app.Close();
_app = nullptr;
}
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
{
if (_logic)
{
return _logic.OnDirectKeyEvent(vkey, scanCode, down);
}
return false;
}
// Method Description:
// - Event handler to update the taskbar progress indicator
// - Upon receiving the event, we ask the underlying logic for the taskbar state/progress values
// of the last active control
// Arguments:
// - sender: not used
// - args: not used
void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/)
{
if (_logic)
{
const auto state = gsl::narrow_cast<size_t>(_logic.GetLastActiveControlTaskbarState());
const auto progress = gsl::narrow_cast<size_t>(_logic.GetLastActiveControlTaskbarProgress());
_window->SetTaskbarProgress(state, progress);
}
}
void _buildArgsFromCommandline(std::vector<winrt::hstring>& args)
{
if (auto commandline{ GetCommandLineW() })
{
int argc = 0;
// Get the argv, and turn them into a hstring array to pass to the app.
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(commandline, &argc) };
if (argv)
{
for (auto& elem : wil::make_range(argv.get(), argc))
{
args.emplace_back(elem);
}
}
}
if (args.empty())
{
args.emplace_back(L"wt.exe");
}
}
// Method Description:
// - Retrieve any commandline args passed on the commandline, and pass them to
// the WindowManager, to ask if we should become a window process.
// - If we should create a window, then pass the arguments to the app logic for
// processing.
// - If we shouldn't become a window, set _shouldCreateWindow to false and exit
// immediately.
// - If the logic determined there's an error while processing that commandline,
// display a message box to the user with the text of the error, and exit.
// * We display a message box because we're a Win32 application (not a
// console app), and the shell has undoubtedly returned to the foreground
// of the console. Text emitted here might mix unexpectedly with output
// from the shell process.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_HandleCommandlineArgs()
{
std::vector<winrt::hstring> args;
_buildArgsFromCommandline(args);
std::wstring cwd{ wil::GetCurrentDirectoryW<std::wstring>() };
Remoting::CommandlineArgs eventArgs{ { args }, { cwd } };
_windowManager.ProposeCommandline(eventArgs);
_shouldCreateWindow = _windowManager.ShouldCreateWindow();
if (!_shouldCreateWindow)
{
return;
}
if (auto peasant{ _windowManager.CurrentWindow() })
{
if (auto args{ peasant.InitialArgs() })
{
const auto result = _logic.SetStartupCommandline(args.Commandline());
const auto message = _logic.ParseCommandlineMessage();
if (!message.empty())
{
const auto displayHelp = result == 0;
const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE;
const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR;
// TODO:GH#4134: polish this dialog more, to make the text more
// like msiexec /?
MessageBoxW(nullptr,
message.data(),
GetStringResource(messageTitle).data(),
MB_OK | messageIcon);
if (_logic.ShouldExitEarly())
{
ExitProcess(result);
}
}
}
// After handling the initial args, hookup the callback for handling
// future commandline invocations. When our peasant is told to execute a
// commandline (in the future), it'll trigger this callback, that we'll
// use to send the actions to the app.
peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline });
peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId });
_logic.WindowName(peasant.WindowName());
_logic.WindowId(peasant.GetID());
}
}
// Method Description:
// - Initializes the XAML island, creates the terminal app, and sets the
// island's content to that of the terminal app's content. Also registers some
// callbacks with TermApp.
// !!! IMPORTANT!!!
// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread.
// If it isn't, then we won't be able to create the XAML island.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::Initialize()
{
_window->Initialize();
if (auto withWindow{ _logic.try_as<IInitializeWithWindow>() })
{
withWindow->Initialize(_window->GetHandle());
}
if (_useNonClientArea)
{
// Register our callback for when the app's non-client content changes.
// This has to be done _before_ App::Create, as the app might set the
// content in Create.
_logic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent });
}
// Register the 'X' button of the window for a warning experience of multiple
// tabs opened, this is consistent with Alt+F4 closing
_window->WindowCloseButtonClicked([this]() { _logic.WindowCloseButtonClicked(); });
// Add an event handler to plumb clicks in the titlebar area down to the
// application layer.
_window->DragRegionClicked([this]() { _logic.TitlebarClicked(); });
_logic.RequestedThemeChanged({ this, &AppHost::_UpdateTheme });
_logic.FullscreenChanged({ this, &AppHost::_FullscreenChanged });
_logic.FocusModeChanged({ this, &AppHost::_FocusModeChanged });
_logic.AlwaysOnTopChanged({ this, &AppHost::_AlwaysOnTopChanged });
_logic.RaiseVisualBell({ this, &AppHost::_RaiseVisualBell });
_logic.Create();
_logic.TitleChanged({ this, &AppHost::AppTitleChanged });
_logic.LastTabClosed({ this, &AppHost::LastTabClosed });
_logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress });
_logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested });
_logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested });
_window->UpdateTitle(_logic.Title());
// Set up the content of the application. If the app has a custom titlebar,
// set that content as well.
_window->SetContent(_logic.GetRoot());
_window->OnAppInitialized();
// THIS IS A HACK
//
// We've got a weird crash that happens terribly inconsistently, but pretty
// readily on migrie's laptop, only in Debug mode. Apparently, there's some
// weird ref-counting magic that goes on during teardown, and our
// Application doesn't get closed quite right, which can cause us to crash
// into the debugger. This of course, only happens on exit, and happens
// somewhere in the XamlHost.dll code.
//
// Crazily, if we _manually leak the Application_ here, then the crash
// doesn't happen. This doesn't matter, because we really want the
// Application to live for _the entire lifetime of the process_, so the only
// time when this object would actually need to get cleaned up is _during
// exit_. So we can safely leak this Application object, and have it just
// get cleaned up normally when our process exits.
::winrt::TerminalApp::App a{ _app };
::winrt::detach_abi(a);
}
// Method Description:
// - Called when the app's title changes. Fires off a window message so we can
// update the window's title on the main thread.
// Arguments:
// - sender: unused
// - newTitle: the string to use as the new window title
// Return Value:
// - <none>
void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle)
{
_window->UpdateTitle(newTitle);
}
// Method Description:
// - Called when no tab is remaining to close the window.
// Arguments:
// - sender: unused
// - LastTabClosedEventArgs: unused
// Return Value:
// - <none>
void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/)
{
_window->Close();
}
// Method Description:
// - Resize the window we're about to create to the appropriate dimensions, as
// 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
// create. We'll use this rect to determine which monitor the window is about
// to appear on.
// - launchMode: A LaunchMode enum reference that indicates the launch mode
// Return Value:
// - None
void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode& launchMode)
{
launchMode = _logic.GetLaunchMode();
// Acquire the actual initial position
auto initialPos = _logic.GetInitialPosition(proposedRect.left, proposedRect.top);
const auto centerOnLaunch = _logic.CenterOnLaunch();
proposedRect.left = static_cast<long>(initialPos.X);
proposedRect.top = static_cast<long>(initialPos.Y);
long adjustedHeight = 0;
long adjustedWidth = 0;
// Find nearest monitor.
HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
// Get nearest monitor information
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hmon, &monitorInfo);
// This API guarantees that dpix and dpiy will be equal, but neither is an
// optional parameter so give two UINTs.
UINT dpix = USER_DEFAULT_SCREEN_DPI;
UINT dpiy = USER_DEFAULT_SCREEN_DPI;
// If this fails, we'll use the default of 96.
GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
// We need to check if the top left point of the titlebar of the window is within any screen
RECT offScreenTestRect;
offScreenTestRect.left = proposedRect.left;
offScreenTestRect.top = proposedRect.top;
offScreenTestRect.right = offScreenTestRect.left + 1;
offScreenTestRect.bottom = offScreenTestRect.top + 1;
bool isTitlebarIntersectWithMonitors = false;
EnumDisplayMonitors(
nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL {
auto intersectWithMonitor = reinterpret_cast<bool*>(lParam);
*intersectWithMonitor = true;
// Continue the enumeration
return FALSE;
},
reinterpret_cast<LPARAM>(&isTitlebarIntersectWithMonitors));
if (!isTitlebarIntersectWithMonitors)
{
// If the title bar is out-of-screen, we set the initial position to
// the top left corner of the nearest monitor
proposedRect.left = monitorInfo.rcWork.left;
proposedRect.top = monitorInfo.rcWork.top;
}
auto initialSize = _logic.GetLaunchDimensions(dpix);
const short islandWidth = Utils::ClampToShortMax(
static_cast<long>(ceil(initialSize.Width)), 1);
const short islandHeight = Utils::ClampToShortMax(
static_cast<long>(ceil(initialSize.Height)), 1);
// Get the size of a window we'd need to host that client rect. This will
// add the titlebar space.
const auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
adjustedWidth = islandWidth + nonClientSize.cx;
adjustedHeight = islandHeight + nonClientSize.cy;
const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
Utils::ClampToShortMax(adjustedHeight, 1) };
if (centerOnLaunch)
{
// Find nearest monitor for the position that we've actually settled on
HMONITOR hMonNearest = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
MONITORINFO nearestMonitorInfo;
nearestMonitorInfo.cbSize = sizeof(MONITORINFO);
// Get monitor dimensions:
GetMonitorInfo(hMonNearest, &nearestMonitorInfo);
const COORD desktopDimensions{ gsl::narrow<short>(nearestMonitorInfo.rcWork.right - nearestMonitorInfo.rcWork.left),
gsl::narrow<short>(nearestMonitorInfo.rcWork.bottom - nearestMonitorInfo.rcWork.top) };
// Move our proposed location into the center of that specific monitor.
proposedRect.left = nearestMonitorInfo.rcWork.left +
((desktopDimensions.X / 2) - (dimensions.X / 2));
proposedRect.top = nearestMonitorInfo.rcWork.top +
((desktopDimensions.Y / 2) - (dimensions.Y / 2));
}
const COORD origin{ gsl::narrow<short>(proposedRect.left),
gsl::narrow<short>(proposedRect.top) };
const auto newPos = Viewport::FromDimensions(origin, dimensions);
bool succeeded = SetWindowPos(hwnd,
nullptr,
newPos.Left(),
newPos.Top(),
newPos.Width(),
newPos.Height(),
SWP_NOACTIVATE | SWP_NOZORDER);
// Refresh the dpi of HWND because the dpi where the window will launch may be different
// at this time
_window->RefreshCurrentDPI();
// 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:
// - Called when the app wants to set its titlebar content. We'll take the
// UIElement and set the Content property of our Titlebar that element.
// Arguments:
// - sender: unused
// - arg: the UIElement to use as the new Titlebar content.
// Return Value:
// - <none>
void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::UIElement& arg)
{
if (_useNonClientArea)
{
(static_cast<NonClientIslandWindow*>(_window.get()))->SetTitlebarContent(arg);
}
}
// Method Description:
// - Called when the app wants to change its theme. We'll forward this to the
// IslandWindow, so it can update the root UI element of the entire XAML tree.
// Arguments:
// - sender: unused
// - arg: the ElementTheme to use as the new theme for the UI
// Return Value:
// - <none>
void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
{
_window->OnApplicationThemeChanged(arg);
}
void AppHost::_FocusModeChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->FocusModeChanged(_logic.FocusMode());
}
void AppHost::_FullscreenChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->FullscreenChanged(_logic.Fullscreen());
}
void AppHost::_AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->SetAlwaysOnTop(_logic.AlwaysOnTop());
}
// Method Description
// - Called when the app wants to flash the taskbar, indicating to the user that
// something needs their attention
// Arguments
// - <unused>
void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->FlashTaskbar();
}
// Method Description:
// - Called when the IslandWindow has received a WM_MOUSEWHEEL message. This can
// happen on some laptops, where their trackpads won't scroll inactive windows
// _ever_.
// - We're going to take that message and manually plumb it through to our
// TermControl's, or anything else that implements IMouseWheelListener.
// - See GH#979 for more details.
// Arguments:
// - coord: The Window-relative, logical coordinates location of the mouse during this event.
// - delta: the wheel delta that triggered this event.
// Return Value:
// - <none>
void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
{
if (_logic)
{
// Find all the elements that are underneath the mouse
auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord, _logic.GetRoot());
for (const auto& e : elems)
{
// If that element has implemented IMouseWheelListener, call OnMouseWheel on that element.
if (auto control{ e.try_as<winrt::Microsoft::Terminal::Control::IMouseWheelListener>() })
{
try
{
// Translate the event to the coordinate space of the control
// we're attempting to dispatch it to
const auto transform = e.TransformToVisual(nullptr);
const til::point controlOrigin{ til::math::flooring, transform.TransformPoint(til::point{ 0, 0 }) };
const til::point offsetPoint = coord - controlOrigin;
const auto lButtonDown = WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed);
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
{
// If the element handled the mouse wheel event, don't
// continue to iterate over the remaining controls.
break;
}
}
CATCH_LOG();
}
}
}
}
bool AppHost::HasWindow()
{
return _shouldCreateWindow;
}
// Method Description:
// - Event handler for the Peasant::ExecuteCommandlineRequested event. Take the
// provided commandline args, and attempt to parse them and perform the
// actions immediately. The parsing is performed by AppLogic.
// - This is invoked when another wt.exe instance runs something like `wt -w 1
// new-tab`, and the Monarch delegates the commandline to this instance.
// Arguments:
// - args: the bundle of a commandline and working directory to use for this invocation.
// Return Value:
// - <none>
void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/,
Remoting::CommandlineArgs args)
{
_window->SummonWindow();
_logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory());
}
// Method Description:
// - Event handler for the WindowManager::FindTargetWindowRequested event. The
// manager will ask us how to figure out what the target window is for a set
// of commandline arguments. We'll take those arguments, and ask AppLogic to
// parse them for us. We'll then set ResultTargetWindow in the given args, so
// the sender can use that result.
// Arguments:
// - args: the bundle of a commandline and working directory to find the correct target window for.
// Return Value:
// - <none>
void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const Remoting::FindTargetWindowArgs& args)
{
const auto targetWindow = _logic.FindTargetWindow(args.Args().Commandline());
args.ResultTargetWindow(targetWindow.WindowId());
args.ResultTargetWindowName(targetWindow.WindowName());
}
winrt::fire_and_forget AppHost::_WindowActivated()
{
co_await winrt::resume_background();
if (auto peasant{ _windowManager.CurrentWindow() })
{
const auto currentDesktopGuid{ _CurrentDesktopGuid() };
// TODO: projects/5 - in the future, we'll want to actually get the
// desktop GUID in IslandWindow, and bubble that up here, then down to
// the Peasant. For now, we're just leaving space for it.
Remoting::WindowActivatedArgs args{ peasant.GetID(),
(uint64_t)_window->GetHandle(),
currentDesktopGuid,
winrt::clock().now() };
peasant.ActivateWindow(args);
}
}
GUID AppHost::_CurrentDesktopGuid()
{
GUID currentDesktopGuid{ 0 };
try
{
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
if (manager)
{
LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), &currentDesktopGuid));
}
}
CATCH_LOG();
return currentDesktopGuid;
}
// Method Description:
// - Called when this window wants _all_ windows to display their
// identification. We'll hop to the BG thread, and raise an event (eventually
// handled by the monarch) to bubble this request to all the Terminal windows.
// Arguments:
// - <unused>
// Return Value:
// - <none>
winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable /*sender*/,
const winrt::Windows::Foundation::IInspectable /*args*/)
{
// We'll be raising an event that may result in a RPC call to the monarch -
// make sure we're on the background thread, or this will silently fail
co_await winrt::resume_background();
if (auto peasant{ _windowManager.CurrentWindow() })
{
peasant.RequestIdentifyWindows();
}
}
// Method Description:
// - Called when the monarch wants us to display our window ID. We'll call down
// to the app layer to display the toast.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void AppHost::_DisplayWindowId(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
_logic.IdentifyWindow();
}
winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Foundation::IInspectable /*sender*/,
const winrt::TerminalApp::RenameWindowRequestedArgs args)
{
// Capture calling context.
winrt::apartment_context ui_thread;
// Switch to the BG thread - anything x-proc must happen on a BG thread
co_await winrt::resume_background();
if (auto peasant{ _windowManager.CurrentWindow() })
{
Remoting::RenameRequestArgs requestArgs{ args.ProposedName() };
peasant.RequestRename(requestArgs);
// Switch back to the UI thread. Setting the WindowName needs to happen
// on the UI thread, because it'll raise a PropertyChanged event
co_await ui_thread;
if (requestArgs.Succeeded())
{
_logic.WindowName(args.ProposedName());
}
else
{
_logic.RenameFailed();
}
}
}