diff --git a/NOTICE.md b/NOTICE.md
index a63cd566485..ccff11e177e 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -251,3 +251,33 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
+
+## `VirtualDesktopUtils`
+
+**Source**: [https://github.com/microsoft/PowerToys](https://github.com/microsoft/PowerToys)
+
+### License
+
+```
+The MIT License
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+```
diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
index 34f70956ae8..2241d3c0e54 100644
--- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
+++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
@@ -28,6 +28,9 @@
Monarch.idl
+
+ Peasant.idl
+
Peasant.idl
@@ -60,6 +63,9 @@
Monarch.idl
+
+ Peasant.idl
+
Peasant.idl
diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp
index 66cbe6ad21b..fc1be97e3a1 100644
--- a/src/cascadia/Remoting/Monarch.cpp
+++ b/src/cascadia/Remoting/Monarch.cpp
@@ -711,7 +711,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// If no name was provided, then just summon the MRU window.
if (searchedForName.empty())
{
- windowId = _getMostRecentPeasantID(true);
+ // Use the value of the `desktop` arg to determine if we should
+ // limit to the current desktop (desktop:onCurrent) or not
+ // (desktop:any or desktop:toCurrent)
+ windowId = _getMostRecentPeasantID(args.OnCurrentDesktop());
}
else
{
@@ -720,7 +723,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
if (auto targetPeasant{ _getPeasant(windowId) })
{
- targetPeasant.Summon();
+ targetPeasant.Summon(args.SummonBehavior());
args.FoundMatch(true);
}
}
diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl
index ca62cdc0440..d87d780b3b6 100644
--- a/src/cascadia/Remoting/Monarch.idl
+++ b/src/cascadia/Remoting/Monarch.idl
@@ -22,13 +22,15 @@ namespace Microsoft.Terminal.Remoting
SummonWindowSelectionArgs();
SummonWindowSelectionArgs(String windowName);
String WindowName;
+ Boolean OnCurrentDesktop;
// TODO GH#8888 Other options:
- // * CurrentDesktop
// * CurrentMonitor
Boolean FoundMatch;
+ SummonWindowBehavior SummonBehavior;
}
+
[default_interface] runtimeclass Monarch {
Monarch();
diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp
index 990d1d591b0..d7b58b73b8b 100644
--- a/src/cascadia/Remoting/Peasant.cpp
+++ b/src/cascadia/Remoting/Peasant.cpp
@@ -4,6 +4,7 @@
#include "pch.h"
#include "Peasant.h"
#include "CommandlineArgs.h"
+#include "SummonWindowBehavior.h"
#include "Peasant.g.cpp"
#include "../../types/inc/utils.hpp"
@@ -126,14 +127,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// -
// Return Value:
// -
- void Peasant::Summon()
+ void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior)
{
- _SummonRequestedHandlers(*this, nullptr);
+ auto localCopy = winrt::make(summonBehavior);
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_Summon",
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
+ TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+
+ _SummonRequestedHandlers(*this, localCopy);
}
// Method Description:
diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h
index 72c672c2e44..7b5b44e8138 100644
--- a/src/cascadia/Remoting/Peasant.h
+++ b/src/cascadia/Remoting/Peasant.h
@@ -24,7 +24,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
- void Summon();
+ void Summon(const Remoting::SummonWindowBehavior& summonBehavior);
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
@@ -39,7 +39,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
- TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
+ TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
private:
Peasant(const uint64_t testPID);
diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl
index 6ff404b73fe..af74cf3d065 100644
--- a/src/cascadia/Remoting/Peasant.idl
+++ b/src/cascadia/Remoting/Peasant.idl
@@ -30,6 +30,13 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.DateTime ActivatedTime { get; };
};
+ [default_interface] runtimeclass SummonWindowBehavior {
+ SummonWindowBehavior();
+ Boolean MoveToCurrentDesktop;
+ // Other options:
+ // * CurrentMonitor
+ }
+
interface IPeasant
{
CommandlineArgs InitialArgs { get; };
@@ -46,14 +53,14 @@ namespace Microsoft.Terminal.Remoting
String WindowName { get; };
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
- void Summon();
+ void Summon(SummonWindowBehavior behavior);
event Windows.Foundation.TypedEventHandler WindowActivated;
event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler RenameRequested;
- event Windows.Foundation.TypedEventHandler SummonRequested;
+ event Windows.Foundation.TypedEventHandler SummonRequested;
};
[default_interface] runtimeclass Peasant : IPeasant
diff --git a/src/cascadia/Remoting/SummonWindowBehavior.cpp b/src/cascadia/Remoting/SummonWindowBehavior.cpp
new file mode 100644
index 00000000000..516acff06d0
--- /dev/null
+++ b/src/cascadia/Remoting/SummonWindowBehavior.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+#include "pch.h"
+#include "SummonWindowBehavior.h"
+#include "SummonWindowBehavior.g.cpp"
diff --git a/src/cascadia/Remoting/SummonWindowBehavior.h b/src/cascadia/Remoting/SummonWindowBehavior.h
new file mode 100644
index 00000000000..c460ef98843
--- /dev/null
+++ b/src/cascadia/Remoting/SummonWindowBehavior.h
@@ -0,0 +1,35 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Class Name:
+- SummonWindowBehavior.h
+
+Abstract:
+- TODO!
+
+--*/
+
+#pragma once
+
+#include "SummonWindowBehavior.g.h"
+#include "../cascadia/inc/cppwinrt_utils.h"
+
+namespace winrt::Microsoft::Terminal::Remoting::implementation
+{
+ struct SummonWindowBehavior : public SummonWindowBehaviorT
+ {
+ public:
+ SummonWindowBehavior() = default;
+ WINRT_PROPERTY(bool, MoveToCurrentDesktop, true);
+
+ public:
+ SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) :
+ _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() } {};
+ };
+}
+
+namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
+{
+ BASIC_FACTORY(SummonWindowBehavior);
+}
diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.h b/src/cascadia/Remoting/SummonWindowSelectionArgs.h
index 4aec6488f41..2696925efc1 100644
--- a/src/cascadia/Remoting/SummonWindowSelectionArgs.h
+++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.h
@@ -30,7 +30,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_WindowName{ name } {};
WINRT_PROPERTY(winrt::hstring, WindowName);
+
WINRT_PROPERTY(bool, FoundMatch, false);
+ WINRT_PROPERTY(bool, OnCurrentDesktop, false);
+ WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
};
}
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h
index 68132691604..3074db1b24e 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.h
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h
@@ -1044,17 +1044,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
GlobalSummonArgs() = default;
WINRT_PROPERTY(winrt::hstring, Name, L"");
+ WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent);
static constexpr std::string_view NameKey{ "name" };
+ static constexpr std::string_view DesktopKey{ "desktop" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
- if (auto otherAsUs = other.try_as(); otherAsUs)
+ if (auto otherAsUs = other.try_as())
{
- return otherAsUs->_Name == _Name;
+ return otherAsUs->_Name == _Name &&
+ otherAsUs->_Desktop == _Desktop;
}
return false;
};
@@ -1063,12 +1066,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self();
JsonUtils::GetValueForKey(json, NameKey, args->_Name);
+ JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop);
return { *args, {} };
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self() };
copy->_Name = _Name;
+ copy->_Desktop = _Desktop;
return *copy;
}
// SPECIAL! This deserializer creates a GlobalSummonArgs with the
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
index ca29300fe52..abf52e50b86 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
@@ -84,6 +84,13 @@ namespace Microsoft.Terminal.Settings.Model
Disabled,
};
+ enum DesktopBehavior
+ {
+ Any,
+ ToCurrent,
+ OnCurrent,
+ };
+
[default_interface] runtimeclass NewTerminalArgs {
NewTerminalArgs();
NewTerminalArgs(Int32 profileIndex);
@@ -256,5 +263,6 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass GlobalSummonArgs : IActionArgs
{
String Name { get; };
+ DesktopBehavior Desktop { get; };
};
}
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
index bdda3eef5a2..57607607ead 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
@@ -448,3 +448,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode)
pair_type{ "useExisting", ValueType::UseExisting },
};
};
+
+JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior)
+{
+ JSON_MAPPINGS(3) = {
+ pair_type{ "any", ValueType::Any },
+ pair_type{ "toCurrent", ValueType::ToCurrent },
+ pair_type{ "onCurrent", ValueType::OnCurrent },
+ };
+};
diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
index 694dcef8fb0..5fa03dbefae 100644
--- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
+++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
@@ -36,6 +36,31 @@ using namespace winrt::Microsoft::Terminal;
namespace RemotingUnitTests
{
+ struct MockDesktopManager : implements
+ {
+ HRESULT GetWindowDesktopId(HWND /*topLevelWindow*/, GUID* /*desktopId*/)
+ {
+ VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests.");
+ return E_FAIL;
+ }
+ HRESULT MoveWindowToDesktop(HWND /*topLevelWindow*/, REFGUID /*desktopId*/)
+ {
+ VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests.");
+ return E_FAIL;
+ }
+ HRESULT IsWindowOnCurrentVirtualDesktop(HWND topLevelWindow, BOOL* onCurrentDesktop)
+ {
+ if (pfnIsWindowOnCurrentVirtualDesktop)
+ {
+ return pfnIsWindowOnCurrentVirtualDesktop(topLevelWindow, onCurrentDesktop);
+ }
+ VERIFY_IS_TRUE(false, L"You didn't set up the pfnIsWindowOnCurrentVirtualDesktop for this test!");
+ return E_FAIL;
+ }
+
+ std::function pfnIsWindowOnCurrentVirtualDesktop;
+ };
+
// This is a silly helper struct.
// It will always throw an hresult_error on any of its methods.
//
@@ -60,13 +85,13 @@ namespace RemotingUnitTests
Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; }
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; }
- void Summon() { throw winrt::hresult_error{}; };
+ void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error{}; };
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs);
- TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
+ TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior);
};
class RemotingTests
@@ -114,6 +139,10 @@ namespace RemotingUnitTests
TEST_METHOD(TestSummonNamedDeadWindow);
TEST_METHOD(TestSummonMostRecentDeadWindow);
+ TEST_METHOD(TestSummonOnCurrent);
+ TEST_METHOD(TestSummonOnCurrentWithName);
+ TEST_METHOD(TestSummonOnCurrentDeadWindow);
+
TEST_CLASS_SETUP(ClassSetup)
{
return true;
@@ -1945,4 +1974,554 @@ namespace RemotingUnitTests
VERIFY_IS_TRUE(args.FoundMatch());
}
+ void RemotingTests::TestSummonOnCurrent()
+ {
+ Log::Comment(L"Tests summoning a window, using OnCurrentDesktop to only"
+ L"select windows on the current desktop.");
+
+ const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
+ const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
+
+ constexpr auto monarch0PID = 12345u;
+ constexpr auto peasant1PID = 23456u;
+ constexpr auto peasant2PID = 34567u;
+ constexpr auto peasant3PID = 45678u;
+
+ com_ptr m0;
+ m0.attach(new Remoting::implementation::Monarch(monarch0PID));
+
+ com_ptr p1;
+ p1.attach(new Remoting::implementation::Peasant(peasant1PID));
+
+ com_ptr p2;
+ p2.attach(new Remoting::implementation::Peasant(peasant2PID));
+
+ com_ptr p3;
+ p3.attach(new Remoting::implementation::Peasant(peasant3PID));
+
+ VERIFY_IS_NOT_NULL(m0);
+ VERIFY_IS_NOT_NULL(p1);
+ VERIFY_IS_NOT_NULL(p2);
+ VERIFY_IS_NOT_NULL(p3);
+ p1->WindowName(L"one");
+ p2->WindowName(L"two");
+ p3->WindowName(L"three");
+
+ VERIFY_ARE_EQUAL(0, p1->GetID());
+ VERIFY_ARE_EQUAL(0, p2->GetID());
+ VERIFY_ARE_EQUAL(0, p3->GetID());
+
+ m0->AddPeasant(*p1);
+ m0->AddPeasant(*p2);
+ m0->AddPeasant(*p3);
+
+ VERIFY_ARE_EQUAL(1, p1->GetID());
+ VERIFY_ARE_EQUAL(2, p2->GetID());
+ VERIFY_ARE_EQUAL(3, p3->GetID());
+
+ VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
+
+ bool p1ExpectedToBeSummoned = false;
+ bool p2ExpectedToBeSummoned = false;
+ bool p3ExpectedToBeSummoned = false;
+
+ p1->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p1 summoned");
+ VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
+ });
+ p2->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p2 summoned");
+ VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
+ });
+ p3->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p3 summoned");
+ VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
+ });
+
+ {
+ Log::Comment(L"Activate the first peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
+ p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p1->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the second peasant, second desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
+ p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid2,
+ winrt::clock().now() };
+ p2->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the third peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
+ p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p3->ActivateWindow(activatedArgs);
+ }
+
+ Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
+ winrt::com_ptr manager;
+ manager.attach(new MockDesktopManager());
+ m0->_desktopManager = manager.try_as();
+
+ auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"firstCallback: Checking if window is on desktop 1");
+
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID || hwnd == peasant3PID)
+ {
+ *result = true;
+ }
+ else if (hwnd == peasant2PID)
+ {
+ *result = false;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
+
+ Remoting::SummonWindowSelectionArgs args;
+
+ Log::Comment(L"Summon window three - it is the MRU on desktop 1");
+ p3ExpectedToBeSummoned = true;
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ {
+ Log::Comment(L"Activate the first peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
+ p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p1->ActivateWindow(activatedArgs);
+ }
+
+ Log::Comment(L"Summon window one - it is the MRU on desktop 1");
+ p1ExpectedToBeSummoned = true;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Now we'll pretend we switched to desktop 2");
+
+ auto secondCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"secondCallback: Checking if window is on desktop 2");
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID || hwnd == peasant3PID)
+ {
+ *result = false;
+ }
+ else if (hwnd == peasant2PID)
+ {
+ *result = true;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = secondCallback;
+
+ Log::Comment(L"Summon window one - it is the MRU on desktop 2");
+ p1ExpectedToBeSummoned = false;
+ p2ExpectedToBeSummoned = true;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ {
+ Log::Comment(L"Activate the third peasant, second desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
+ p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid2,
+ winrt::clock().now() };
+ p3->ActivateWindow(activatedArgs);
+ }
+
+ auto thirdCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"thirdCallback: Checking if window is on desktop 2. (windows 2 and 3 are)");
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID)
+ {
+ *result = false;
+ }
+ else if (hwnd == peasant2PID || hwnd == peasant3PID)
+ {
+ *result = true;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = thirdCallback;
+
+ Log::Comment(L"Summon window three - it is the MRU on desktop 2");
+ p1ExpectedToBeSummoned = false;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = true;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Now we'll pretend we switched to desktop 1");
+
+ auto fourthCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"fourthCallback: Checking if window is on desktop 1. (window 1 is)");
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID)
+ {
+ *result = true;
+ }
+ else if (hwnd == peasant2PID || hwnd == peasant3PID)
+ {
+ *result = false;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = fourthCallback;
+
+ Log::Comment(L"Summon window one - it is the only window on desktop 1");
+ p1ExpectedToBeSummoned = true;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Now we'll pretend we switched to desktop 3");
+
+ auto fifthCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"fifthCallback: Checking if window is on desktop 3. (none are)");
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID || hwnd == peasant2PID || hwnd == peasant3PID)
+ {
+ *result = false;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = fifthCallback;
+
+ Log::Comment(L"This summon won't find a window.");
+ p1ExpectedToBeSummoned = false;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_FALSE(args.FoundMatch());
+ }
+
+ void RemotingTests::TestSummonOnCurrentWithName()
+ {
+ Log::Comment(L"Test that specifying a WindowName forces us to ignore OnCurrentDesktop");
+
+ const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
+ const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
+
+ constexpr auto monarch0PID = 12345u;
+ constexpr auto peasant1PID = 23456u;
+ constexpr auto peasant2PID = 34567u;
+ constexpr auto peasant3PID = 45678u;
+
+ com_ptr m0;
+ m0.attach(new Remoting::implementation::Monarch(monarch0PID));
+
+ com_ptr p1;
+ p1.attach(new Remoting::implementation::Peasant(peasant1PID));
+
+ com_ptr p2;
+ p2.attach(new Remoting::implementation::Peasant(peasant2PID));
+
+ com_ptr p3;
+ p3.attach(new Remoting::implementation::Peasant(peasant3PID));
+
+ VERIFY_IS_NOT_NULL(m0);
+ VERIFY_IS_NOT_NULL(p1);
+ VERIFY_IS_NOT_NULL(p2);
+ VERIFY_IS_NOT_NULL(p3);
+ p1->WindowName(L"one");
+ p2->WindowName(L"two");
+ p3->WindowName(L"three");
+
+ VERIFY_ARE_EQUAL(0, p1->GetID());
+ VERIFY_ARE_EQUAL(0, p2->GetID());
+ VERIFY_ARE_EQUAL(0, p3->GetID());
+
+ m0->AddPeasant(*p1);
+ m0->AddPeasant(*p2);
+ m0->AddPeasant(*p3);
+
+ VERIFY_ARE_EQUAL(1, p1->GetID());
+ VERIFY_ARE_EQUAL(2, p2->GetID());
+ VERIFY_ARE_EQUAL(3, p3->GetID());
+
+ VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
+
+ bool p1ExpectedToBeSummoned = false;
+ bool p2ExpectedToBeSummoned = false;
+ bool p3ExpectedToBeSummoned = false;
+
+ p1->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p1 summoned");
+ VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
+ });
+ p2->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p2 summoned");
+ VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
+ });
+ p3->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p3 summoned");
+ VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
+ });
+
+ {
+ Log::Comment(L"Activate the first peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
+ p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p1->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the second peasant, second desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
+ p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid2,
+ winrt::clock().now() };
+ p2->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the third peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
+ p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p3->ActivateWindow(activatedArgs);
+ }
+
+ Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
+ winrt::com_ptr manager;
+ manager.attach(new MockDesktopManager());
+ m0->_desktopManager = manager.try_as();
+
+ auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"firstCallback: Checking if window is on desktop 1");
+
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID || hwnd == peasant3PID)
+ {
+ *result = true;
+ }
+ else if (hwnd == peasant2PID)
+ {
+ *result = false;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
+
+ Remoting::SummonWindowSelectionArgs args;
+
+ Log::Comment(L"Summon window three - it is the MRU on desktop 1");
+ p3ExpectedToBeSummoned = true;
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Look for window 1 by name. When given a name, we don't care about OnCurrentDesktop.");
+ p1ExpectedToBeSummoned = true;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.WindowName(L"one");
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Look for window 2 by name. When given a name, we don't care about OnCurrentDesktop.");
+ p1ExpectedToBeSummoned = false;
+ p2ExpectedToBeSummoned = true;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.WindowName(L"two");
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Look for window 3 by name. When given a name, we don't care about OnCurrentDesktop.");
+ p1ExpectedToBeSummoned = false;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = true;
+ args.FoundMatch(false);
+ args.WindowName(L"three");
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+ }
+
+ void RemotingTests::TestSummonOnCurrentDeadWindow()
+ {
+ Log::Comment(L"Test that we can summon a window on the current desktop,"
+ L" when the MRU window on that desktop dies.");
+
+ const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
+ const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
+
+ constexpr auto monarch0PID = 12345u;
+ constexpr auto peasant1PID = 23456u;
+ constexpr auto peasant2PID = 34567u;
+ constexpr auto peasant3PID = 45678u;
+
+ com_ptr m0;
+ m0.attach(new Remoting::implementation::Monarch(monarch0PID));
+
+ com_ptr p1;
+ p1.attach(new Remoting::implementation::Peasant(peasant1PID));
+
+ com_ptr p2;
+ p2.attach(new Remoting::implementation::Peasant(peasant2PID));
+
+ com_ptr p3;
+ p3.attach(new Remoting::implementation::Peasant(peasant3PID));
+
+ VERIFY_IS_NOT_NULL(m0);
+ VERIFY_IS_NOT_NULL(p1);
+ VERIFY_IS_NOT_NULL(p2);
+ VERIFY_IS_NOT_NULL(p3);
+ p1->WindowName(L"one");
+ p2->WindowName(L"two");
+ p3->WindowName(L"three");
+
+ VERIFY_ARE_EQUAL(0, p1->GetID());
+ VERIFY_ARE_EQUAL(0, p2->GetID());
+ VERIFY_ARE_EQUAL(0, p3->GetID());
+
+ m0->AddPeasant(*p1);
+ m0->AddPeasant(*p2);
+ m0->AddPeasant(*p3);
+
+ VERIFY_ARE_EQUAL(1, p1->GetID());
+ VERIFY_ARE_EQUAL(2, p2->GetID());
+ VERIFY_ARE_EQUAL(3, p3->GetID());
+
+ VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
+
+ bool p1ExpectedToBeSummoned = false;
+ bool p2ExpectedToBeSummoned = false;
+ bool p3ExpectedToBeSummoned = false;
+
+ p1->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p1 summoned");
+ VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
+ });
+ p2->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p2 summoned");
+ VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
+ });
+ p3->SummonRequested([&](auto&&, auto&&) {
+ Log::Comment(L"p3 summoned");
+ VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
+ });
+
+ {
+ Log::Comment(L"Activate the first peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
+ p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p1->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the second peasant, second desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
+ p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid2,
+ winrt::clock().now() };
+ p2->ActivateWindow(activatedArgs);
+ }
+ {
+ Log::Comment(L"Activate the third peasant, first desktop");
+ Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
+ p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
+ guid1,
+ winrt::clock().now() };
+ p3->ActivateWindow(activatedArgs);
+ }
+
+ Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
+ winrt::com_ptr manager;
+ manager.attach(new MockDesktopManager());
+ m0->_desktopManager = manager.try_as();
+
+ auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
+ Log::Comment(L"firstCallback: Checking if window is on desktop 1");
+
+ const uint64_t hwnd = reinterpret_cast(h);
+ if (hwnd == peasant1PID || hwnd == peasant3PID)
+ {
+ *result = true;
+ }
+ else if (hwnd == peasant2PID)
+ {
+ *result = false;
+ }
+ else
+ {
+ VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
+ }
+ return S_OK;
+ };
+ manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
+
+ Remoting::SummonWindowSelectionArgs args;
+
+ Log::Comment(L"Summon window three - it is the MRU on desktop 1");
+ p3ExpectedToBeSummoned = true;
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+
+ Log::Comment(L"Kill window 3. Window 1 is now the MRU on desktop 1.");
+ RemotingTests::_killPeasant(m0, p3->GetID());
+
+ Log::Comment(L"Summon window three - it is the MRU on desktop 1");
+ p1ExpectedToBeSummoned = true;
+ p2ExpectedToBeSummoned = false;
+ p3ExpectedToBeSummoned = false;
+ args.FoundMatch(false);
+ args.OnCurrentDesktop(true);
+ m0->SummonWindow(args);
+ VERIFY_IS_TRUE(args.FoundMatch());
+ }
}
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index f4054fbb7d4..3430e69ba28 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -8,6 +8,7 @@
#include "../types/inc/User32Utils.hpp"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include "resource.h"
+#include "VirtualDesktopUtils.h"
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
@@ -686,6 +687,12 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex)
{
Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() };
+ // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false
+ // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false
+ // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true
+ args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent);
+ args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent);
+
_windowManager.SummonWindow(args);
if (args.FoundMatch())
{
@@ -740,24 +747,65 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global
co_return;
}
+// Method Description:
+// - Helper to initialize our instance of IVirtualDesktopManager. If we already
+// got one, then this will just return true. Otherwise, we'll try and init a
+// new instance of one, and store that.
+// - This will return false if we weren't able to initialize one, which I'm not
+// sure is actually possible.
+// Arguments:
+// -
+// Return Value:
+// - true iff _desktopManager points to a non-null instance of IVirtualDesktopManager
+bool AppHost::_LazyLoadDesktopManager()
+{
+ if (_desktopManager == nullptr)
+ {
+ try
+ {
+ _desktopManager = winrt::create_instance(__uuidof(VirtualDesktopManager));
+ }
+ CATCH_LOG();
+ }
+
+ return _desktopManager != nullptr;
+}
+
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
- const winrt::Windows::Foundation::IInspectable& /*args*/)
+ const Remoting::SummonWindowBehavior& args)
{
_window->SummonWindow();
+
+ if (args != nullptr && args.MoveToCurrentDesktop())
+ {
+ if (_LazyLoadDesktopManager())
+ {
+ GUID currentlyActiveDesktop{ 0 };
+ if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop))
+ {
+ LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop));
+ }
+ // If GetCurrentVirtualDesktopId failed, then just leave the window
+ // where it is. Nothing else to be done :/
+ }
+ }
}
+// Method Description:
+// - This gets the GUID of the desktop our window is currently on. It does NOT
+// get the GUID of the desktop that's currently active.
+// Arguments:
+// -
+// Return Value:
+// - the GUID of the desktop our window is currently on
GUID AppHost::_CurrentDesktopGuid()
{
GUID currentDesktopGuid{ 0 };
- try
+ const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager));
+ if (_LazyLoadDesktopManager())
{
- const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager));
- if (manager)
- {
- LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
- }
+ LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
}
- CATCH_LOG();
return currentDesktopGuid;
}
diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h
index 9288ee560df..6abfb511b30 100644
--- a/src/cascadia/WindowsTerminal/AppHost.h
+++ b/src/cascadia/WindowsTerminal/AppHost.h
@@ -31,6 +31,8 @@ class AppHost
std::vector _hotkeys{};
winrt::Windows::Foundation::Collections::IMap _hotkeyActions{ nullptr };
+ winrt::com_ptr _desktopManager{ nullptr };
+
void _HandleCommandlineArgs();
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
@@ -59,7 +61,7 @@ class AppHost
const winrt::Windows::Foundation::IInspectable& args);
void _GlobalHotkeyPressed(const long hotkeyIndex);
void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender,
- const winrt::Windows::Foundation::IInspectable& args);
+ const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args);
winrt::fire_and_forget _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable sender,
const winrt::Windows::Foundation::IInspectable args);
@@ -70,6 +72,8 @@ class AppHost
GUID _CurrentDesktopGuid();
+ bool _LazyLoadDesktopManager();
+
winrt::fire_and_forget _setupGlobalHotkeys();
winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args);
void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender,
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp
index 7a6d6f4bea9..a4c249a5bb7 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp
@@ -956,9 +956,9 @@ void IslandWindow::UnsetHotkeys(const std::vector(hotkeyList.size()); i++)
{
- LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), static_cast(i)));
+ LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), i));
}
}
diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
new file mode 100644
index 00000000000..a912e7e1b0f
--- /dev/null
+++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
@@ -0,0 +1,173 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+//
+// Shamelessly copied from microsoft/PowerToys, at
+// https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/lib/VirtualDesktopUtils.cpp
+//
+// The code style is left (relatively) untouched, as to make contributions
+// from/to the upstream source easier. `NewGetCurrentDesktopId` was added in
+// April 2021.
+
+#include "pch.h"
+
+#include "VirtualDesktopUtils.h"
+
+// Non-Localizable strings
+namespace NonLocalizable
+{
+ const wchar_t RegCurrentVirtualDesktop[] = L"CurrentVirtualDesktop";
+ const wchar_t RegVirtualDesktopIds[] = L"VirtualDesktopIDs";
+ const wchar_t RegKeyVirtualDesktops[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops";
+ const wchar_t RegKeyVirtualDesktopsFromSession[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops";
+}
+
+namespace VirtualDesktopUtils
+{
+ // Look for the guid stored as the value `CurrentVirtualDesktop` under the
+ // key
+ // `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops`
+ bool NewGetCurrentDesktopId(GUID* desktopId)
+ {
+ wil::unique_hkey key{};
+ if (RegOpenKeyExW(HKEY_CURRENT_USER, NonLocalizable::RegKeyVirtualDesktops, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
+ {
+ GUID value{};
+ DWORD size = sizeof(GUID);
+ if (RegQueryValueExW(key.get(), NonLocalizable::RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast(&value), &size) == ERROR_SUCCESS)
+ {
+ *desktopId = value;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool GetDesktopIdFromCurrentSession(GUID* desktopId)
+ {
+ DWORD sessionId;
+ if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId))
+ {
+ return false;
+ }
+
+ wchar_t sessionKeyPath[256]{};
+ if (FAILED(StringCchPrintfW(sessionKeyPath, ARRAYSIZE(sessionKeyPath), NonLocalizable::RegKeyVirtualDesktopsFromSession, sessionId)))
+ {
+ return false;
+ }
+
+ wil::unique_hkey key{};
+ if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
+ {
+ GUID value{};
+ DWORD size = sizeof(GUID);
+ if (RegQueryValueExW(key.get(), NonLocalizable::RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast(&value), &size) == ERROR_SUCCESS)
+ {
+ *desktopId = value;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool GetVirtualDesktopIds(HKEY hKey, std::vector& ids)
+ {
+ if (!hKey)
+ {
+ return false;
+ }
+ DWORD bufferCapacity;
+ // request regkey binary buffer capacity only
+ if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS)
+ {
+ return false;
+ }
+ std::unique_ptr buffer = std::make_unique(bufferCapacity);
+ // request regkey binary content
+ if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS)
+ {
+ return false;
+ }
+ const size_t guidSize = sizeof(GUID);
+ std::vector temp;
+ temp.reserve(bufferCapacity / guidSize);
+ for (size_t i = 0; i < bufferCapacity; i += guidSize)
+ {
+ GUID* guid = reinterpret_cast(buffer.get() + i);
+ temp.push_back(*guid);
+ }
+ ids = std::move(temp);
+ return true;
+ }
+
+ HKEY OpenVirtualDesktopsRegKey()
+ {
+ HKEY hKey{ nullptr };
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, NonLocalizable::RegKeyVirtualDesktops, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ {
+ return hKey;
+ }
+ return nullptr;
+ }
+
+ HKEY GetVirtualDesktopsRegKey()
+ {
+ static wil::unique_hkey virtualDesktopsKey{ OpenVirtualDesktopsRegKey() };
+ return virtualDesktopsKey.get();
+ }
+ bool GetVirtualDesktopIds(std::vector& ids)
+ {
+ return GetVirtualDesktopIds(GetVirtualDesktopsRegKey(), ids);
+ }
+
+ bool GetVirtualDesktopIds(std::vector& ids)
+ {
+ std::vector guids{};
+ if (GetVirtualDesktopIds(guids))
+ {
+ for (auto& guid : guids)
+ {
+ wil::unique_cotaskmem_string guidString;
+ if (SUCCEEDED(StringFromCLSID(guid, &guidString)))
+ {
+ ids.push_back(guidString.get());
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool GetCurrentVirtualDesktopId(GUID* desktopId)
+ {
+ // BODGY
+ // On newer Windows builds, the current virtual desktop is persisted to
+ // a totally different reg key. Look there first.
+ if (NewGetCurrentDesktopId(desktopId))
+ {
+ return true;
+ }
+
+ // Explorer persists current virtual desktop identifier to registry on a per session basis, but only
+ // after first virtual desktop switch happens. If the user hasn't switched virtual desktops in this
+ // session, value in registry will be empty.
+ if (GetDesktopIdFromCurrentSession(desktopId))
+ {
+ return true;
+ }
+ // Fallback scenario is to get array of virtual desktops stored in registry, but not kept per session.
+ // Note that we are taking first element from virtual desktop array, which is primary desktop.
+ // If user has more than one virtual desktop, previous function should return correct value, as desktop
+ // switch occurred in current session.
+ else
+ {
+ std::vector ids{};
+ if (GetVirtualDesktopIds(ids) && ids.size() > 0)
+ {
+ *desktopId = ids[0];
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
new file mode 100644
index 00000000000..cd0dd56faea
--- /dev/null
+++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+//
+// A helper function for determining the GUID of the current Virtual Desktop.
+
+#pragma once
+
+namespace VirtualDesktopUtils
+{
+ bool GetCurrentVirtualDesktopId(GUID* desktopId);
+}
diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
index fba24855f8e..806c83c0bf3 100644
--- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
+++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
@@ -48,6 +48,7 @@
+
@@ -57,6 +58,7 @@
+