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:
parent
d08271e734
commit
65b22b9abb
30
NOTICE.md
30
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.
|
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.
|
||||||
|
```
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
<ClInclude Include="SummonWindowSelectionArgs.h">
|
<ClInclude Include="SummonWindowSelectionArgs.h">
|
||||||
<DependentUpon>Monarch.idl</DependentUpon>
|
<DependentUpon>Monarch.idl</DependentUpon>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="SummonWindowBehavior.h">
|
||||||
|
<DependentUpon>Peasant.idl</DependentUpon>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="RenameRequestArgs.h">
|
<ClInclude Include="RenameRequestArgs.h">
|
||||||
<DependentUpon>Peasant.idl</DependentUpon>
|
<DependentUpon>Peasant.idl</DependentUpon>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -60,6 +63,9 @@
|
||||||
<ClCompile Include="SummonWindowSelectionArgs.cpp">
|
<ClCompile Include="SummonWindowSelectionArgs.cpp">
|
||||||
<DependentUpon>Monarch.idl</DependentUpon>
|
<DependentUpon>Monarch.idl</DependentUpon>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="SummonWindowBehavior.cpp">
|
||||||
|
<DependentUpon>Peasant.idl</DependentUpon>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="RenameRequestArgs.cpp">
|
<ClCompile Include="RenameRequestArgs.cpp">
|
||||||
<DependentUpon>Peasant.idl</DependentUpon>
|
<DependentUpon>Peasant.idl</DependentUpon>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -711,7 +711,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||||
// If no name was provided, then just summon the MRU window.
|
// If no name was provided, then just summon the MRU window.
|
||||||
if (searchedForName.empty())
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -720,7 +723,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||||
}
|
}
|
||||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||||
{
|
{
|
||||||
targetPeasant.Summon();
|
targetPeasant.Summon(args.SummonBehavior());
|
||||||
args.FoundMatch(true);
|
args.FoundMatch(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,15 @@ namespace Microsoft.Terminal.Remoting
|
||||||
SummonWindowSelectionArgs();
|
SummonWindowSelectionArgs();
|
||||||
SummonWindowSelectionArgs(String windowName);
|
SummonWindowSelectionArgs(String windowName);
|
||||||
String WindowName;
|
String WindowName;
|
||||||
|
Boolean OnCurrentDesktop;
|
||||||
// TODO GH#8888 Other options:
|
// TODO GH#8888 Other options:
|
||||||
// * CurrentDesktop
|
|
||||||
// * CurrentMonitor
|
// * CurrentMonitor
|
||||||
|
|
||||||
Boolean FoundMatch;
|
Boolean FoundMatch;
|
||||||
|
SummonWindowBehavior SummonBehavior;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[default_interface] runtimeclass Monarch {
|
[default_interface] runtimeclass Monarch {
|
||||||
Monarch();
|
Monarch();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "Peasant.h"
|
#include "Peasant.h"
|
||||||
#include "CommandlineArgs.h"
|
#include "CommandlineArgs.h"
|
||||||
|
#include "SummonWindowBehavior.h"
|
||||||
#include "Peasant.g.cpp"
|
#include "Peasant.g.cpp"
|
||||||
#include "../../types/inc/utils.hpp"
|
#include "../../types/inc/utils.hpp"
|
||||||
|
|
||||||
|
@ -126,14 +127,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||||
// - <none>
|
// - <none>
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - <none>
|
// - <none>
|
||||||
void Peasant::Summon()
|
void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior)
|
||||||
{
|
{
|
||||||
_SummonRequestedHandlers(*this, nullptr);
|
auto localCopy = winrt::make<implementation::SummonWindowBehavior>(summonBehavior);
|
||||||
|
|
||||||
TraceLoggingWrite(g_hRemotingProvider,
|
TraceLoggingWrite(g_hRemotingProvider,
|
||||||
"Peasant_Summon",
|
"Peasant_Summon",
|
||||||
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
||||||
|
TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"),
|
||||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||||
|
|
||||||
|
_SummonRequestedHandlers(*this, localCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method Description:
|
// Method Description:
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||||
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
||||||
void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||||
|
|
||||||
void Summon();
|
void Summon(const Remoting::SummonWindowBehavior& summonBehavior);
|
||||||
void RequestIdentifyWindows();
|
void RequestIdentifyWindows();
|
||||||
void DisplayWindowId();
|
void DisplayWindowId();
|
||||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
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(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||||
TYPED_EVENT(DisplayWindowIdRequested, 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(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:
|
private:
|
||||||
Peasant(const uint64_t testPID);
|
Peasant(const uint64_t testPID);
|
||||||
|
|
|
@ -30,6 +30,13 @@ namespace Microsoft.Terminal.Remoting
|
||||||
Windows.Foundation.DateTime ActivatedTime { get; };
|
Windows.Foundation.DateTime ActivatedTime { get; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[default_interface] runtimeclass SummonWindowBehavior {
|
||||||
|
SummonWindowBehavior();
|
||||||
|
Boolean MoveToCurrentDesktop;
|
||||||
|
// Other options:
|
||||||
|
// * CurrentMonitor
|
||||||
|
}
|
||||||
|
|
||||||
interface IPeasant
|
interface IPeasant
|
||||||
{
|
{
|
||||||
CommandlineArgs InitialArgs { get; };
|
CommandlineArgs InitialArgs { get; };
|
||||||
|
@ -46,14 +53,14 @@ namespace Microsoft.Terminal.Remoting
|
||||||
String WindowName { get; };
|
String WindowName { get; };
|
||||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
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, WindowActivatedArgs> WindowActivated;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
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
|
[default_interface] runtimeclass Peasant : IPeasant
|
||||||
|
|
5
src/cascadia/Remoting/SummonWindowBehavior.cpp
Normal file
5
src/cascadia/Remoting/SummonWindowBehavior.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
#include "pch.h"
|
||||||
|
#include "SummonWindowBehavior.h"
|
||||||
|
#include "SummonWindowBehavior.g.cpp"
|
35
src/cascadia/Remoting/SummonWindowBehavior.h
Normal file
35
src/cascadia/Remoting/SummonWindowBehavior.h
Normal 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);
|
||||||
|
}
|
|
@ -30,7 +30,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||||
_WindowName{ name } {};
|
_WindowName{ name } {};
|
||||||
|
|
||||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||||
|
|
||||||
WINRT_PROPERTY(bool, FoundMatch, false);
|
WINRT_PROPERTY(bool, FoundMatch, false);
|
||||||
|
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
|
||||||
|
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1044,17 +1044,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||||
{
|
{
|
||||||
GlobalSummonArgs() = default;
|
GlobalSummonArgs() = default;
|
||||||
WINRT_PROPERTY(winrt::hstring, Name, L"");
|
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 NameKey{ "name" };
|
||||||
|
static constexpr std::string_view DesktopKey{ "desktop" };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
hstring GenerateName() const;
|
hstring GenerateName() const;
|
||||||
|
|
||||||
bool Equals(const IActionArgs& other)
|
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;
|
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!
|
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||||
auto args = winrt::make_self<GlobalSummonArgs>();
|
auto args = winrt::make_self<GlobalSummonArgs>();
|
||||||
JsonUtils::GetValueForKey(json, NameKey, args->_Name);
|
JsonUtils::GetValueForKey(json, NameKey, args->_Name);
|
||||||
|
JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop);
|
||||||
return { *args, {} };
|
return { *args, {} };
|
||||||
}
|
}
|
||||||
IActionArgs Copy() const
|
IActionArgs Copy() const
|
||||||
{
|
{
|
||||||
auto copy{ winrt::make_self<GlobalSummonArgs>() };
|
auto copy{ winrt::make_self<GlobalSummonArgs>() };
|
||||||
copy->_Name = _Name;
|
copy->_Name = _Name;
|
||||||
|
copy->_Desktop = _Desktop;
|
||||||
return *copy;
|
return *copy;
|
||||||
}
|
}
|
||||||
// SPECIAL! This deserializer creates a GlobalSummonArgs with the
|
// SPECIAL! This deserializer creates a GlobalSummonArgs with the
|
||||||
|
|
|
@ -84,6 +84,13 @@ namespace Microsoft.Terminal.Settings.Model
|
||||||
Disabled,
|
Disabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DesktopBehavior
|
||||||
|
{
|
||||||
|
Any,
|
||||||
|
ToCurrent,
|
||||||
|
OnCurrent,
|
||||||
|
};
|
||||||
|
|
||||||
[default_interface] runtimeclass NewTerminalArgs {
|
[default_interface] runtimeclass NewTerminalArgs {
|
||||||
NewTerminalArgs();
|
NewTerminalArgs();
|
||||||
NewTerminalArgs(Int32 profileIndex);
|
NewTerminalArgs(Int32 profileIndex);
|
||||||
|
@ -256,5 +263,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||||
[default_interface] runtimeclass GlobalSummonArgs : IActionArgs
|
[default_interface] runtimeclass GlobalSummonArgs : IActionArgs
|
||||||
{
|
{
|
||||||
String Name { get; };
|
String Name { get; };
|
||||||
|
DesktopBehavior Desktop { get; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,3 +448,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode)
|
||||||
pair_type{ "useExisting", ValueType::UseExisting },
|
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 },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -36,6 +36,31 @@ using namespace winrt::Microsoft::Terminal;
|
||||||
|
|
||||||
namespace RemotingUnitTests
|
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.
|
// This is a silly helper struct.
|
||||||
// It will always throw an hresult_error on any of its methods.
|
// 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::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; }
|
||||||
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
|
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
|
||||||
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { 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(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
|
||||||
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
|
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
|
||||||
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||||
TYPED_EVENT(DisplayWindowIdRequested, 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(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
|
class RemotingTests
|
||||||
|
@ -114,6 +139,10 @@ namespace RemotingUnitTests
|
||||||
TEST_METHOD(TestSummonNamedDeadWindow);
|
TEST_METHOD(TestSummonNamedDeadWindow);
|
||||||
TEST_METHOD(TestSummonMostRecentDeadWindow);
|
TEST_METHOD(TestSummonMostRecentDeadWindow);
|
||||||
|
|
||||||
|
TEST_METHOD(TestSummonOnCurrent);
|
||||||
|
TEST_METHOD(TestSummonOnCurrentWithName);
|
||||||
|
TEST_METHOD(TestSummonOnCurrentDeadWindow);
|
||||||
|
|
||||||
TEST_CLASS_SETUP(ClassSetup)
|
TEST_CLASS_SETUP(ClassSetup)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -1945,4 +1974,554 @@ namespace RemotingUnitTests
|
||||||
VERIFY_IS_TRUE(args.FoundMatch());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "../types/inc/User32Utils.hpp"
|
#include "../types/inc/User32Utils.hpp"
|
||||||
#include "../WinRTUtils/inc/WtExeUtils.h"
|
#include "../WinRTUtils/inc/WtExeUtils.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
#include "VirtualDesktopUtils.h"
|
||||||
|
|
||||||
using namespace winrt::Windows::UI;
|
using namespace winrt::Windows::UI;
|
||||||
using namespace winrt::Windows::UI::Composition;
|
using namespace winrt::Windows::UI::Composition;
|
||||||
|
@ -686,6 +687,12 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex)
|
||||||
{
|
{
|
||||||
Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() };
|
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);
|
_windowManager.SummonWindow(args);
|
||||||
if (args.FoundMatch())
|
if (args.FoundMatch())
|
||||||
{
|
{
|
||||||
|
@ -740,24 +747,65 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
// Method Description:
|
||||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
// - 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(¤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:
|
||||||
|
// - <none>
|
||||||
|
// Return Value:
|
||||||
|
// - the GUID of the desktop our window is currently on
|
||||||
GUID AppHost::_CurrentDesktopGuid()
|
GUID AppHost::_CurrentDesktopGuid()
|
||||||
{
|
{
|
||||||
GUID currentDesktopGuid{ 0 };
|
GUID currentDesktopGuid{ 0 };
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
|
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
|
||||||
if (manager)
|
if (_LazyLoadDesktopManager())
|
||||||
{
|
{
|
||||||
LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
|
LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
CATCH_LOG();
|
|
||||||
return currentDesktopGuid;
|
return currentDesktopGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ private:
|
||||||
std::vector<winrt::Microsoft::Terminal::Control::KeyChord> _hotkeys{};
|
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::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 _HandleCommandlineArgs();
|
||||||
|
|
||||||
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
|
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);
|
const winrt::Windows::Foundation::IInspectable& args);
|
||||||
void _GlobalHotkeyPressed(const long hotkeyIndex);
|
void _GlobalHotkeyPressed(const long hotkeyIndex);
|
||||||
void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender,
|
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,
|
winrt::fire_and_forget _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable sender,
|
||||||
const winrt::Windows::Foundation::IInspectable args);
|
const winrt::Windows::Foundation::IInspectable args);
|
||||||
|
@ -70,6 +72,8 @@ private:
|
||||||
|
|
||||||
GUID _CurrentDesktopGuid();
|
GUID _CurrentDesktopGuid();
|
||||||
|
|
||||||
|
bool _LazyLoadDesktopManager();
|
||||||
|
|
||||||
winrt::fire_and_forget _setupGlobalHotkeys();
|
winrt::fire_and_forget _setupGlobalHotkeys();
|
||||||
winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args);
|
winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args);
|
||||||
void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender,
|
void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender,
|
||||||
|
|
|
@ -956,9 +956,9 @@ void IslandWindow::UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Co
|
||||||
TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to unset"),
|
TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to unset"),
|
||||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
173
src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
Normal file
173
src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
11
src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
Normal file
11
src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
Normal 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);
|
||||||
|
}
|
|
@ -48,6 +48,7 @@
|
||||||
<ClInclude Include="BaseWindow.h" />
|
<ClInclude Include="BaseWindow.h" />
|
||||||
<ClInclude Include="IslandWindow.h" />
|
<ClInclude Include="IslandWindow.h" />
|
||||||
<ClInclude Include="NonClientIslandWindow.h" />
|
<ClInclude Include="NonClientIslandWindow.h" />
|
||||||
|
<ClInclude Include="VirtualDesktopUtils.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
<ClCompile Include="AppHost.cpp" />
|
<ClCompile Include="AppHost.cpp" />
|
||||||
<ClCompile Include="IslandWindow.cpp" />
|
<ClCompile Include="IslandWindow.cpp" />
|
||||||
<ClCompile Include="NonClientIslandWindow.cpp" />
|
<ClCompile Include="NonClientIslandWindow.cpp" />
|
||||||
|
<ClCompile Include="VirtualDesktopUtils.cpp" />
|
||||||
<ClCompile Include="icon.cpp" />
|
<ClCompile Include="icon.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Reference in a new issue