Compare commits

...

5 commits

Author SHA1 Message Date
Andrey Nekrasov f51ee065c2
[VCM] Activate the module (#13662) 2021-10-12 19:05:08 +03:00
Seraphima Zykova afdefa2428 [FancyZones] Reset layouts after screen locking fix (#13703) 2021-10-11 11:43:11 +01:00
Seraphima Zykova 45d2752dbb [FancyZones] Change layout using a shortcut (#13616) 2021-10-11 11:42:59 +01:00
Jaime Bernardo 52c9f35369 [PT Run] Add setting to use centralized keyboard hook (#13557)
* [PT Run] Add setting to use centralized hook

* Add UI separator
2021-10-11 11:39:46 +01:00
Jaime Bernardo 03e5e77171 [Shortcut Guide] Suppress Windows Menu appearing (#13553)
* Get the new settings values

* Suppress windows menu appearing

* Send new settings telemetry

* Address PR comments
2021-10-11 11:39:30 +01:00
24 changed files with 494 additions and 93 deletions

View file

@ -62,11 +62,10 @@ build:
- 'x64/**/*.pdb'
exclude:
- 'x64/Release/obj/**/*.pdb'
# TODO(yuyoyuppe): uncomment when VCM should be enabled
#- from: 'x86/Release'
# to: 'Build_Output'
# include:
# - 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
- from: 'x86/Release'
to: 'Build_Output'
include:
- 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
- from: 'x64/Release'
to: 'Build_Output'
include:
@ -170,10 +169,9 @@ build:
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide\PowerToys.ShortcutGuide.exe'
- 'modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.dll'
# TODO(yuyoyuppe): uncomment when VCM should be enabled
#- 'modules\VideoConference\VideoConferenceModule.dll'
#- 'modules\VideoConference\VideoConferenceProxyFilter_x64.dll'
#- 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
- 'modules\VideoConference\VideoConferenceModule.dll'
- 'modules\VideoConference\VideoConferenceProxyFilter_x64.dll'
- 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
- 'Settings\ManagedTelemetry.dll'
- 'Settings\Microsoft.PowerToys.Settings.UI.exe'
- 'Settings\Microsoft.PowerToys.Settings.UI.Lib.dll'

View file

@ -242,10 +242,9 @@
</Directory>
<Directory Id="ShortcutGuideModuleInterfaceInstallFolder" Name="ShortcutGuideModuleInterface"/>
</Directory>
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <Directory Id="VideoConferenceInstallFolder" Name="$(var.VideoConferenceProjectName)">
<Directory Id="VideoConferenceInstallFolder" Name="$(var.VideoConferenceProjectName)">
<Directory Id="VideoConferenceIconsFolder" Name="Icons" />
</Directory> -->
</Directory>
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
<Directory Id="AwakeInstallFolder" Name="$(var.AwakeProjectName)">
@ -707,8 +706,7 @@
</Component>
</DirectoryRef>
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <DirectoryRef Id="VideoConferenceInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\">
<DirectoryRef Id="VideoConferenceInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\">
<Component Id="Module_VideoConference" Guid="5996527a-40fc-432e-b3ac-abc0b4bd3887" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 18362</Condition>
<File SelfRegCost="1" Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceProxyFilter_x64.dll" KeyPath="yes">
@ -736,7 +734,7 @@
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Light.png" />
</Component>
</DirectoryRef> -->
</DirectoryRef>
<DirectoryRef Id="ShortcutGuideExecutableInstallFolder" FileSource="$(var.ShortcutGuideExecutable)">
<Component Id="Module_ShortcutGuideExecutable" Guid="DA6E5710-F1DF-44EB-A316-300FA39544E9" Win64="yes">
@ -993,9 +991,8 @@
<ComponentRef Id="ShortcutGuideSvgs" />
<ComponentRef Id="Module_ShortcutGuideModuleInterface" />
<ComponentRef Id="Module_ShortcutGuideExecutable" />
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" /> -->
<ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" />
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />

View file

@ -171,6 +171,10 @@ public
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
static String ^ PowerLauncherCentralizedHookSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_CENTRALIZED_HOOK_SHARED_EVENT);
}
static String ^ RunSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT);
}

View file

@ -15,6 +15,8 @@ namespace CommonSharedConstants
// Path to the event used by PowerLauncher
const wchar_t POWER_LAUNCHER_SHARED_EVENT[] = L"Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab";
const wchar_t POWER_LAUNCHER_CENTRALIZED_HOOK_SHARED_EVENT[] = L"Local\\PowerToysRunCentralizedHookInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab";
const wchar_t RUN_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\PowerToysRunInvokeEvent-638ec522-0018-4b96-837d-6bd88e06f0d6";
const wchar_t RUN_EXIT_EVENT[] = L"Local\\PowerToysRunExitEvent-3e38e49d-a762-4ef1-88f2-fd4bc7481516";

View file

@ -7,4 +7,6 @@ struct ShortcutGuideSettings
int overlayOpacity = 90;
std::wstring theme = L"system";
std::wstring disabledApps = L"";
bool shouldReactToPressedWinKey = false;
int windowsKeyPressTime = 900;
};

View file

@ -221,6 +221,15 @@ void OverlayWindow::CloseWindow(HideWindowType type, int mainThreadId)
if (this->winkey_popup)
{
if (shouldReactToPressedWinKey.value)
{
// Send a dummy key to prevent Start Menu from activating
INPUT dummyEvent[1] = {};
dummyEvent[0].type = INPUT_KEYBOARD;
dummyEvent[0].ki.wVk = 0xFF;
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, dummyEvent, sizeof(INPUT));
}
this->winkey_popup->SetWindowCloseType(ToWstring(type));
Logger::trace(L"Terminating process");
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
@ -300,6 +309,7 @@ void OverlayWindow::init_settings()
overlayOpacity.value = settings.overlayOpacity;
theme.value = settings.theme;
disabledApps.value = settings.disabledApps;
shouldReactToPressedWinKey.value = settings.shouldReactToPressedWinKey;
update_disabled_apps();
}
@ -399,6 +409,22 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept
{
}
try
{
settings.shouldReactToPressedWinKey = properties.GetNamedObject(ShouldReactToPressedWinKey::name).GetNamedBoolean(L"value");
}
catch (...)
{
}
try
{
settings.windowsKeyPressTime = (int)properties.GetNamedObject(WindowsKeyPressTime::name).GetNamedNumber(L"value");
}
catch (...)
{
}
try
{
settings.theme = (std::wstring)properties.GetNamedObject(Theme::name).GetNamedString(L"value");

View file

@ -78,6 +78,17 @@ private:
std::wstring value;
} disabledApps;
struct ShouldReactToPressedWinKey
{
static inline PCWSTR name = L"use_legacy_press_win_key_behavior";
bool value;
} shouldReactToPressedWinKey;
struct WindowsKeyPressTime
{
static inline PCWSTR name = L"press_time";
} windowsKeyPressTime;
struct OpenShortcut
{
static inline PCWSTR name = L"open_shortcutguide";

View file

@ -39,6 +39,8 @@ void Trace::SendSettings(ShortcutGuideSettings settings) noexcept
TraceLoggingInt32(settings.overlayOpacity, "OverlayOpacity"),
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
TraceLoggingWideString(settings.disabledApps.c_str(), "DisabledApps"),
TraceLoggingBoolean(settings.shouldReactToPressedWinKey, "ShouldReactToPressedWinKey"),
TraceLoggingInt32(settings.windowsKeyPressTime, "WindowsKeyPressTime"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));

View file

@ -180,11 +180,18 @@ const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneH
return appZoneHistoryMap;
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& id) const
{
std::scoped_lock lock{ dataLock };
auto it = deviceInfoMap.find(zoneWindowId);
return it != end(deviceInfoMap) ? std::optional{ it->second } : std::nullopt;
for (const auto& [deviceId, deviceInfo] : deviceInfoMap)
{
if (id.isEqualWithNullVirtualDesktopId(deviceId))
{
return deviceInfo;
}
}
return std::nullopt;
}
std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustomZoneSet(const std::wstring& guid) const
@ -199,8 +206,11 @@ bool FancyZonesData::AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId
_TRACER_;
using namespace FancyZonesDataTypes;
auto deviceInfo = FindDeviceInfo(deviceId);
std::scoped_lock lock{ dataLock };
if (!deviceInfoMap.contains(deviceId))
if (!deviceInfo.has_value())
{
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(deviceId.virtualDesktopId, &virtualDesktopId)))
@ -217,13 +227,12 @@ bool FancyZonesData::AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId
const ZoneSetData zoneSetData{ guidString.get(), ZoneSetLayoutType::PriorityGrid };
DeviceInfoData defaultDeviceInfoData{ zoneSetData, DefaultValues::ShowSpacing, DefaultValues::Spacing, DefaultValues::ZoneCount, DefaultValues::SensitivityRadius };
deviceInfoMap[deviceId] = std::move(defaultDeviceInfoData);
return true;
}
else
{
deviceInfoMap[deviceId] = DeviceInfoData{ ZoneSetData{ NonLocalizable::NullStr, ZoneSetLayoutType::Blank } };
Logger::error("Failed to create an ID for the new layout");
}
return true;
}
return false;
@ -238,7 +247,7 @@ void FancyZonesData::CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& so
std::scoped_lock lock{ dataLock };
// The source virtual desktop is deleted, simply ignore it.
if (!deviceInfoMap.contains(source))
if (!FindDeviceInfo(source).has_value())
{
return;
}
@ -559,31 +568,33 @@ void FancyZonesData::SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& d
{
std::scoped_lock lock{ dataLock };
auto deviceIt = deviceInfoMap.find(deviceId);
if (deviceIt == deviceInfoMap.end())
for (auto& [deviceIdData, deviceInfo] : deviceInfoMap)
{
return;
}
deviceIt->second.activeZoneSet = data;
// If the zone set is custom, we need to copy its properties to the device
auto zonesetIt = customZoneSetsMap.find(data.uuid);
if (zonesetIt != customZoneSetsMap.end())
{
if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Grid)
if (deviceId.isEqualWithNullVirtualDesktopId(deviceIdData))
{
auto layoutInfo = std::get<FancyZonesDataTypes::GridLayoutInfo>(zonesetIt->second.info);
deviceIt->second.sensitivityRadius = layoutInfo.sensitivityRadius();
deviceIt->second.showSpacing = layoutInfo.showSpacing();
deviceIt->second.spacing = layoutInfo.spacing();
deviceIt->second.zoneCount = layoutInfo.zoneCount();
}
else if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Canvas)
{
auto layoutInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zonesetIt->second.info);
deviceIt->second.sensitivityRadius = layoutInfo.sensitivityRadius;
deviceIt->second.zoneCount = (int)layoutInfo.zones.size();
deviceInfo.activeZoneSet = data;
// If the zone set is custom, we need to copy its properties to the device
auto zonesetIt = customZoneSetsMap.find(data.uuid);
if (zonesetIt != customZoneSetsMap.end())
{
if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Grid)
{
auto layoutInfo = std::get<FancyZonesDataTypes::GridLayoutInfo>(zonesetIt->second.info);
deviceInfo.sensitivityRadius = layoutInfo.sensitivityRadius();
deviceInfo.showSpacing = layoutInfo.showSpacing();
deviceInfo.spacing = layoutInfo.spacing();
deviceInfo.zoneCount = layoutInfo.zoneCount();
}
else if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Canvas)
{
auto layoutInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zonesetIt->second.info);
deviceInfo.sensitivityRadius = layoutInfo.sensitivityRadius;
deviceInfo.zoneCount = (int)layoutInfo.zones.size();
}
}
break;
}
}
}

View file

@ -46,7 +46,7 @@ public:
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const;
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& id) const;
std::optional<FancyZonesDataTypes::CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
const JSONHelpers::TDeviceInfoMap& GetDeviceInfoMap() const;

View file

@ -146,6 +146,11 @@ namespace FancyZonesDataTypes
int sensitivityRadius;
};
inline bool operator==(const ZoneSetData& lhs, const ZoneSetData& rhs)
{
return lhs.type == rhs.type && lhs.uuid == rhs.uuid;
}
inline bool operator==(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) == 0 && lhs.width == rhs.width && lhs.height == rhs.height && lhs.virtualDesktopId == rhs.virtualDesktopId && lhs.monitorId.compare(rhs.monitorId) == 0;
@ -160,6 +165,11 @@ namespace FancyZonesDataTypes
{
return lhs.deviceName.compare(rhs.deviceName) < 0 || lhs.width < rhs.width || lhs.height < rhs.height || lhs.monitorId.compare(rhs.monitorId) < 0;
}
inline bool operator==(const DeviceInfoData& lhs, const DeviceInfoData& rhs)
{
return lhs.activeZoneSet == rhs.activeZoneSet && lhs.showSpacing == rhs.showSpacing && lhs.spacing == rhs.spacing && lhs.zoneCount == rhs.zoneCount && lhs.sensitivityRadius == rhs.sensitivityRadius;
}
}
namespace std

View file

@ -2042,6 +2042,258 @@ namespace FancyZonesUnitTests
Assert::IsFalse(data.RemoveAppLastZone(nullptr, deviceId, zoneSetId));
}
TEST_METHOD (AddDevice)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceWithNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicate)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(expected);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceWithNullVirtualDesktopIdDuplicated)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(expected);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicatedComparedWithNillVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData device1{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData device2{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(device1);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(device2);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(device1) == actualMap.end());
Assert::IsTrue(actualMap.find(device2) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicatedComparedWithNillVirtualDesktopId2)
{
FancyZonesDataTypes::DeviceIdData device1{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData device2{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result2 = m_fzData.AddDevice(device2);
Assert::IsTrue(result2);
auto result1 = m_fzData.AddDevice(device1);
Assert::IsFalse(result1);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(device2) == actualMap.end());
Assert::IsTrue(actualMap.find(device1) == actualMap.end());
}
TEST_METHOD(CloneDeviceInfo)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
TEST_METHOD (CloneDeviceInfoIntoUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
TEST_METHOD (CloneDeviceInfoFromUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsTrue(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
Assert::IsFalse(m_fzData.FindDeviceInfo(deviceSrc).has_value());
Assert::IsTrue(m_fzData.FindDeviceInfo(deviceDst).has_value());
}
TEST_METHOD(CloneDeviceInfoNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
};
TEST_CLASS(EditorArgsUnitTests)

View file

@ -25,6 +25,7 @@ namespace
const wchar_t JSON_KEY_SHIFT[] = L"shift";
const wchar_t JSON_KEY_CODE[] = L"code";
const wchar_t JSON_KEY_OPEN_POWERLAUNCHER[] = L"open_powerlauncher";
const wchar_t JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK[] = L"use_centralized_keyboard_hook";
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
@ -73,11 +74,15 @@ private:
// Hotkey to invoke the module
Hotkey m_hotkey = { .key = 0 };
// If the centralized keyboard hook should be used to activate PowerToys Run
bool m_use_centralized_keyboard_hook = false;
// Helper function to extract the hotkey from the settings
void parse_hotkey(PowerToysSettings::PowerToyValues& settings);
// Handle to event used to invoke the Runner
HANDLE m_hEvent;
HANDLE m_hCentralizedKeyboardHookEvent;
HANDLE send_telemetry_event;
@ -115,6 +120,8 @@ public:
init_settings();
m_hEvent = CreateDefaultEvent(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
m_hCentralizedKeyboardHookEvent = CreateDefaultEvent(CommonSharedConstants::POWER_LAUNCHER_CENTRALIZED_HOOK_SHARED_EVENT);
send_telemetry_event = CreateDefaultEvent(CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT);
};
@ -199,7 +206,7 @@ public:
{
Logger::info("Microsoft_Launcher::enable()");
m_enabled = true;
ResetEvent(m_hEvent);
ResetEvent(m_hCentralizedKeyboardHookEvent);
ResetEvent(send_telemetry_event);
unsigned long powertoys_pid = GetCurrentProcessId();
@ -210,7 +217,7 @@ public:
std::wstring executable_args;
executable_args += L" -powerToysPid ";
executable_args += std::to_wstring(powertoys_pid);
executable_args += L" --centralized-kb-hook";
executable_args += L" --started-from-runner";
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
@ -234,7 +241,7 @@ public:
std::wstring runExecutablePath = get_module_folderpath();
std::wstring params;
params += L" -powerToysPid " + std::to_wstring(powertoys_pid) + L" ";
params += L"--centralized-kb-hook ";
params += L"--started-from-runner ";
runExecutablePath += L"\\modules\\launcher\\PowerLauncher.exe";
if (RunNonElevatedEx(runExecutablePath, params))
{
@ -268,6 +275,7 @@ public:
TerminateRunningInstance();
processStarted = false;
ResetEvent(m_hEvent);
ResetEvent(m_hCentralizedKeyboardHookEvent);
ResetEvent(send_telemetry_event);
}
@ -310,13 +318,13 @@ public:
enable();
}
/* Before we used the central keyboard hook to trigger PowerToys Run.
* Now, PowerToys Run uses a global hotkey so that it can get focus.
* This means we can't return true so that the hotkey can propagate to PowerToys Run.
Logger::trace("Set POWER_LAUNCHER_SHARED_EVENT");
SetEvent(m_hEvent);
return true;
*/
/* Now, PowerToys Run uses a global hotkey so that it can get focus.
* Activate it with the centralized keyboard hook only if the setting is on.*/
if (m_use_centralized_keyboard_hook) {
Logger::trace("Set POWER_LAUNCHER_SHARED_EVENT");
SetEvent(m_hCentralizedKeyboardHookEvent);
return true;
}
}
return false;
@ -360,6 +368,7 @@ void Microsoft_Launcher::init_settings()
void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& settings)
{
m_use_centralized_keyboard_hook = false;
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
@ -376,6 +385,15 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting
{
Logger::error("Failed to initialize PT Run start shortcut");
}
try
{
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
m_use_centralized_keyboard_hook = (bool)jsonPropertiesObject.GetNamedBoolean(JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK);
}
catch (...)
{
Logger::warn("Failed to get centralized keyboard hook setting");
}
}
else
{

View file

@ -109,7 +109,7 @@ namespace PowerLauncher
_settingsVM = new SettingWindowViewModel();
_settings = _settingsVM.Settings;
_settings.UsePowerToysRunnerKeyboardHook = e.Args.Contains("--centralized-kb-hook");
_settings.StartedFromPowerToysRunner = e.Args.Contains("--started-from-runner");
_stringMatcher = new StringMatcher();
StringMatcher.Instance = _stringMatcher;

View file

@ -100,6 +100,11 @@ namespace PowerLauncher
_settings.Hotkey = openPowerlauncher;
}
if (_settings.UseCentralizedKeyboardHook != overloadSettings.Properties.UseCentralizedKeyboardHook)
{
_settings.UseCentralizedKeyboardHook = overloadSettings.Properties.UseCentralizedKeyboardHook;
}
if (_settings.MaxResultsToShow != overloadSettings.Properties.MaximumNumberOfResults)
{
_settings.MaxResultsToShow = overloadSettings.Properties.MaximumNumberOfResults;

View file

@ -88,9 +88,15 @@ namespace PowerLauncher.ViewModel
// Allow OOBE to call PowerToys Run.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey);
if (_settings.StartedFromPowerToysRunner)
{
// Allow runner to call PowerToys Run from the centralized keyboard hook.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey);
}
_settings.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(PowerToysRunSettings.Hotkey))
if (e.PropertyName == nameof(PowerToysRunSettings.Hotkey) || e.PropertyName == nameof(PowerToysRunSettings.UseCentralizedKeyboardHook))
{
Application.Current.Dispatcher.Invoke(() =>
{
@ -741,26 +747,33 @@ namespace PowerLauncher.ViewModel
Log.Info("Unregistering previous low level key handler", GetType());
}
_globalHotKeyVK = hotkey.Key;
_globalHotKeyFSModifiers = VKModifiersFromHotKey(hotkey);
if (NativeMethods.RegisterHotKey(hwnd, _globalHotKeyId, _globalHotKeyFSModifiers, _globalHotKeyVK))
if (_settings.StartedFromPowerToysRunner && _settings.UseCentralizedKeyboardHook)
{
// Using global hotkey registered through the native RegisterHotKey method.
_globalHotKeyHwnd = hwnd;
_usingGlobalHotKey = true;
Log.Info("Registered global hotkey", GetType());
return;
Log.Info("Using the Centralized Keyboard Hook for the HotKey.", GetType());
}
Log.Warn("Registering global shortcut failed. Will use low-level keyboard hook instead.", GetType());
// Using fallback low-level keyboard hook through HotkeyManager.
if (HotkeyManager == null)
else
{
HotkeyManager = new HotkeyManager();
}
_globalHotKeyVK = hotkey.Key;
_globalHotKeyFSModifiers = VKModifiersFromHotKey(hotkey);
if (NativeMethods.RegisterHotKey(hwnd, _globalHotKeyId, _globalHotKeyFSModifiers, _globalHotKeyVK))
{
// Using global hotkey registered through the native RegisterHotKey method.
_globalHotKeyHwnd = hwnd;
_usingGlobalHotKey = true;
Log.Info("Registered global hotkey", GetType());
return;
}
_hotkeyHandle = HotkeyManager.RegisterHotkey(hotkey, action);
Log.Warn("Registering global shortcut failed. Will use low-level keyboard hook instead.", GetType());
// Using fallback low-level keyboard hook through HotkeyManager.
if (HotkeyManager == null)
{
HotkeyManager = new HotkeyManager();
}
_hotkeyHandle = HotkeyManager.RegisterHotkey(hotkey, action);
}
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception)
@ -817,6 +830,14 @@ namespace PowerLauncher.ViewModel
}
*/
private void OnCentralizedKeyboardHookHotKey()
{
if (_settings.StartedFromPowerToysRunner && _settings.UseCentralizedKeyboardHook)
{
OnHotkey();
}
}
private void OnHotkey()
{
Application.Current.Dispatcher.Invoke(() =>

View file

@ -42,6 +42,25 @@ namespace Wox.Infrastructure.UserSettings
}
}
private bool _useCentralizedKeyboardHook;
public bool UseCentralizedKeyboardHook
{
get
{
return _useCentralizedKeyboardHook;
}
set
{
if (_useCentralizedKeyboardHook != value)
{
_useCentralizedKeyboardHook = value;
OnPropertyChanged(nameof(UseCentralizedKeyboardHook));
}
}
}
public string Language { get; set; } = "en";
public Theme Theme { get; set; } = Theme.System;
@ -172,7 +191,7 @@ namespace Wox.Infrastructure.UserSettings
public bool IgnoreHotkeysOnFullscreen { get; set; }
public bool UsePowerToysRunnerKeyboardHook { get; set; }
public bool StartedFromPowerToysRunner { get; set; }
public HttpProxy Proxy { get; set; } = new HttpProxy();

View file

@ -144,13 +144,12 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
L"modules/Awake/AwakeModuleInterface.dll"
};
// TODO(yuyoyuppe): uncomment when VCM should be enabled
//const auto VCM_PATH = L"modules/VideoConference/VideoConferenceModule.dll";
//if (const auto mf = LoadLibraryA("mf.dll"))
//{
// FreeLibrary(mf);
// knownModules.emplace_back(VCM_PATH);
//}
const auto VCM_PATH = L"modules/VideoConference/VideoConferenceModule.dll";
if (const auto mf = LoadLibraryA("mf.dll"))
{
FreeLibrary(mf);
knownModules.emplace_back(VCM_PATH);
}
for (const auto& moduleSubdir : knownModules)
{

View file

@ -48,6 +48,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("startupPosition")]
public StartupPosition Position { get; set; }
[JsonPropertyName("use_centralized_keyboard_hook")]
public bool UseCentralizedKeyboardHook { get; set; }
public PowerLauncherProperties()
{
OpenPowerLauncher = new HotkeySettings(false, false, true, false, 32);
@ -61,6 +64,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
MaximumNumberOfResults = 4;
Theme = Theme.System;
Position = StartupPosition.Cursor;
UseCentralizedKeyboardHook = false;
}
}
}

View file

@ -275,6 +275,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
public bool UseCentralizedKeyboardHook
{
get
{
return settings.Properties.UseCentralizedKeyboardHook;
}
set
{
if (settings.Properties.UseCentralizedKeyboardHook != value)
{
settings.Properties.UseCentralizedKeyboardHook = value;
UpdateSettings();
}
}
}
public HotkeySettings OpenFileLocation
{
get

View file

@ -180,8 +180,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Link = "https://aka.ms/PowerToysOverview_ShortcutGuide",
});
// TODO(yuyoyuppe): uncomment when VCM should be enabled
/* Modules.Insert((int)PowerToysModulesEnum.VideoConference, new OobePowerToysModule()
Modules.Insert((int)PowerToysModulesEnum.VideoConference, new OobePowerToysModule()
{
ModuleName = loader.GetString("Oobe_VideoConference"),
Tag = "VideoConference",
@ -192,7 +191,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
Description = loader.GetString("Oobe_VideoConference_Description"),
PreviewImageSource = "ms-appx:///Assets/Modules/OOBE/VideoConferenceMute.png",
Link = "https://aka.ms/PowerToysOverview_VideoConference",
}); */
});
}
public void OnClosing()

View file

@ -401,6 +401,9 @@
<data name="PowerLauncher_IgnoreHotkeysInFullScreen.Content" xml:space="preserve">
<value>Ignore shortcuts in fullscreen mode</value>
</data>
<data name="PowerLauncher_UseCentralizedKeyboardHook.Content" xml:space="preserve">
<value>Use centralized keyboard hook (try it if there are issues with the shortcut)</value>
</data>
<data name="PowerLauncher_ClearInputOnLaunch.Content" xml:space="preserve">
<value>Clear the previous query on launch</value>
</data>

View file

@ -45,6 +45,8 @@
</controls:SettingExpander.Header>
<controls:SettingExpander.Content>
<StackPanel>
<CheckBox x:Uid="PowerLauncher_UseCentralizedKeyboardHook" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.UseCentralizedKeyboardHook}" Margin="{StaticResource ExpanderSettingMargin}" />
<Rectangle Style="{StaticResource ExpanderSeparatorStyle}" />
<CheckBox x:Uid="PowerLauncher_IgnoreHotkeysInFullScreen" IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.IgnoreHotkeysInFullScreen}" Margin="{StaticResource ExpanderSettingMargin}" />
</StackPanel>
</controls:SettingExpander.Content>

View file

@ -119,15 +119,14 @@
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
<!--TODO(yuyoyuppe): uncomment when VCM should be enabled-->
<!--<muxc:NavigationViewItem x:Uid="Shell_VideoConference"
<muxc:NavigationViewItem x:Uid="Shell_VideoConference"
helpers:NavHelper.NavigateTo="views:VideoConferencePage"
IsEnabled="{x:Bind ViewModel.IsVideoConferenceBuild, Mode=OneWay}">
<muxc:NavigationViewItem.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/FluentIcons/FluentIconsVideoConferenceMute.png"
ShowAsMonochrome="False" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>-->
</muxc:NavigationViewItem>
</muxc:NavigationView.MenuItems>
<muxc:NavigationView.PaneFooter>
@ -154,4 +153,4 @@
<Frame x:Name="shellFrame" />
</muxc:NavigationView>
</Grid>
</UserControl>
</UserControl>