Add desktop param to globalSummon; set _quake = toCurrent (#9954)

This adds support for the `desktop` param to the `globalSummon` action. It accepts 3 values:
* `toCurrent` (default): The window moves to the current desktop when it's summoned
* `any`: We don't care what desktop the window is on. We'll go to the desktop the window is on when we summon it.
* `onCurrent`: We'll only try to summon the MRU window on this desktop when summoning a window. 
  * When combined with `name`, if there's a window matching `name`, we'll move it to this desktop. 
  * If there's not a window on this desktop, and `name` is omitted, then we'll make a new window.

`quakeMode` was also updated to use `toCurrent` behavior by default.

## References
* Original thread: #653
* Spec: #9274 
* megathread: #8888

## PR Checklist
* [x] Checks some boxes in #8888
* [x] closes https://github.com/microsoft/terminal/projects/5#card-59030845
* [x] I work here
* [x] Tests added 
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

S/O to https://github.com/microsoft/PowerToys, who graciously let us use `VirtualDesktopUtils` for figuring out what desktop is the current desktop. Yea, that's all we needed that entire file for. No, there isn't an API for this (_surprised-pikachu.png_)

## Validation Steps Performed

Played with this for a while, and it's amazing.
This commit is contained in:
Mike Griese 2021-04-28 17:25:48 -05:00 committed by GitHub
parent d08271e734
commit 65b22b9abb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 960 additions and 26 deletions

View file

@ -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.
```

View file

@ -28,6 +28,9 @@
<ClInclude Include="SummonWindowSelectionArgs.h">
<DependentUpon>Monarch.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SummonWindowBehavior.h">
<DependentUpon>Peasant.idl</DependentUpon>
</ClInclude>
<ClInclude Include="RenameRequestArgs.h">
<DependentUpon>Peasant.idl</DependentUpon>
</ClInclude>
@ -60,6 +63,9 @@
<ClCompile Include="SummonWindowSelectionArgs.cpp">
<DependentUpon>Monarch.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SummonWindowBehavior.cpp">
<DependentUpon>Peasant.idl</DependentUpon>
</ClCompile>
<ClCompile Include="RenameRequestArgs.cpp">
<DependentUpon>Peasant.idl</DependentUpon>
</ClCompile>

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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
// - <none>
// Return Value:
// - <none>
void Peasant::Summon()
void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior)
{
_SummonRequestedHandlers(*this, nullptr);
auto localCopy = winrt::make<implementation::SummonWindowBehavior>(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:

View file

@ -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);

View file

@ -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<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View file

@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SummonWindowBehavior.h"
#include "SummonWindowBehavior.g.cpp"

View file

@ -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<SummonWindowBehavior>
{
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);
}

View file

@ -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);
};
}

View file

@ -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<GlobalSummonArgs>(); otherAsUs)
if (auto otherAsUs = other.try_as<GlobalSummonArgs>())
{
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<GlobalSummonArgs>();
JsonUtils::GetValueForKey(json, NameKey, args->_Name);
JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop);
return { *args, {} };
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<GlobalSummonArgs>() };
copy->_Name = _Name;
copy->_Desktop = _Desktop;
return *copy;
}
// SPECIAL! This deserializer creates a GlobalSummonArgs with the

View file

@ -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; };
};
}

View file

@ -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 },
};
};

View file

@ -36,6 +36,31 @@ using namespace winrt::Microsoft::Terminal;
namespace RemotingUnitTests
{
struct MockDesktopManager : implements<MockDesktopManager, IVirtualDesktopManager>
{
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<HRESULT(HWND, BOOL*)> 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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Peasant> 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<MockDesktopManager> manager;
manager.attach(new MockDesktopManager());
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
const uint64_t hwnd = reinterpret_cast<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Peasant> 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<MockDesktopManager> manager;
manager.attach(new MockDesktopManager());
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
const uint64_t hwnd = reinterpret_cast<uint64_t>(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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Peasant> 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<MockDesktopManager> manager;
manager.attach(new MockDesktopManager());
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
const uint64_t hwnd = reinterpret_cast<uint64_t>(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());
}
}

View file

@ -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;
}
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
// 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:
// - <none>
// Return Value:
// - true iff _desktopManager points to a non-null instance of IVirtualDesktopManager
bool AppHost::_LazyLoadDesktopManager()
{
_window->SummonWindow();
if (_desktopManager == nullptr)
{
try
{
_desktopManager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
}
CATCH_LOG();
}
return _desktopManager != nullptr;
}
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const Remoting::SummonWindowBehavior& args)
{
_window->SummonWindow();
if (args != nullptr && args.MoveToCurrentDesktop())
{
if (_LazyLoadDesktopManager())
{
GUID currentlyActiveDesktop{ 0 };
if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(&currentlyActiveDesktop))
{
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:
// - <none>
// 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<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
if (_LazyLoadDesktopManager())
{
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
if (manager)
{
LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), &currentDesktopGuid));
}
LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), &currentDesktopGuid));
}
CATCH_LOG();
return currentDesktopGuid;
}

View file

@ -31,6 +31,8 @@ private:
std::vector<winrt::Microsoft::Terminal::Control::KeyChord> _hotkeys{};
winrt::Windows::Foundation::Collections::IMap<winrt::Microsoft::Terminal::Control::KeyChord, winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _hotkeyActions{ nullptr };
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };
void _HandleCommandlineArgs();
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
@ -59,7 +61,7 @@ private:
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 @@ private:
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,

View file

@ -956,9 +956,9 @@ void IslandWindow::UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Co
TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to unset"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
for (size_t i = 0; i < hotkeyList.size(); i++)
for (int i = 0; i < ::base::saturated_cast<int>(hotkeyList.size()); i++)
{
LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), static_cast<int>(i)));
LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), i));
}
}

View file

@ -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<BYTE*>(&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<BYTE*>(&value), &size) == ERROR_SUCCESS)
{
*desktopId = value;
return true;
}
}
return false;
}
bool GetVirtualDesktopIds(HKEY hKey, std::vector<GUID>& 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<BYTE[]> buffer = std::make_unique<BYTE[]>(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<GUID> temp;
temp.reserve(bufferCapacity / guidSize);
for (size_t i = 0; i < bufferCapacity; i += guidSize)
{
GUID* guid = reinterpret_cast<GUID*>(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<GUID>& ids)
{
return GetVirtualDesktopIds(GetVirtualDesktopsRegKey(), ids);
}
bool GetVirtualDesktopIds(std::vector<std::wstring>& ids)
{
std::vector<GUID> 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<GUID> ids{};
if (GetVirtualDesktopIds(ids) && ids.size() > 0)
{
*desktopId = ids[0];
return true;
}
}
return false;
}
}

View file

@ -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);
}

View file

@ -48,6 +48,7 @@
<ClInclude Include="BaseWindow.h" />
<ClInclude Include="IslandWindow.h" />
<ClInclude Include="NonClientIslandWindow.h" />
<ClInclude Include="VirtualDesktopUtils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@ -57,6 +58,7 @@
<ClCompile Include="AppHost.cpp" />
<ClCompile Include="IslandWindow.cpp" />
<ClCompile Include="NonClientIslandWindow.cpp" />
<ClCompile Include="VirtualDesktopUtils.cpp" />
<ClCompile Include="icon.cpp" />
</ItemGroup>
<ItemGroup>