diff --git a/src/common/interop/Constants.cpp b/src/common/interop/Constants.cpp
index 63e9782346..dc4641822b 100644
--- a/src/common/interop/Constants.cpp
+++ b/src/common/interop/Constants.cpp
@@ -151,4 +151,8 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT;
}
+ hstring Constants::WorkspacesHotkeyEvent()
+ {
+ return CommonSharedConstants::WORKSPACES_HOTKEY_EVENT;
+ }
}
diff --git a/src/common/interop/Constants.h b/src/common/interop/Constants.h
index 978ca8ab60..b59cd3e62c 100644
--- a/src/common/interop/Constants.h
+++ b/src/common/interop/Constants.h
@@ -41,6 +41,7 @@ namespace winrt::PowerToys::Interop::implementation
static hstring ShowEnvironmentVariablesSharedEvent();
static hstring ShowEnvironmentVariablesAdminSharedEvent();
static hstring WorkspacesLaunchEditorEvent();
+ static hstring WorkspacesHotkeyEvent();
};
}
diff --git a/src/common/interop/Constants.idl b/src/common/interop/Constants.idl
index 4c4125b7db..a681ee5a81 100644
--- a/src/common/interop/Constants.idl
+++ b/src/common/interop/Constants.idl
@@ -38,6 +38,7 @@ namespace PowerToys
static String ShowEnvironmentVariablesSharedEvent();
static String ShowEnvironmentVariablesAdminSharedEvent();
static String WorkspacesLaunchEditorEvent();
+ static String WorkspacesHotkeyEvent();
}
}
}
\ No newline at end of file
diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h
index 5237c15737..3f9c350b7b 100644
--- a/src/common/interop/shared_constants.h
+++ b/src/common/interop/shared_constants.h
@@ -43,7 +43,9 @@ namespace CommonSharedConstants
const wchar_t FANCY_ZONES_EDITOR_TOGGLE_EVENT[] = L"Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14";
+ // Path to the event used by Workspaces
const wchar_t WORKSPACES_LAUNCH_EDITOR_EVENT[] = L"Local\\Workspaces-LaunchEditorEvent-a55ff427-cf62-4994-a2cd-9f72139296bf";
+ const wchar_t WORKSPACES_HOTKEY_EVENT[] = L"Local\\PowerToys-Workspaces-HotkeyEvent-2625C3C8-BAC9-4DB3-BCD6-3B4391A26FD0";
const wchar_t SHOW_HOSTS_EVENT[] = L"Local\\Hosts-ShowHostsEvent-5a0c0aae-5ff5-40f5-95c2-20e37ed671f0";
@@ -100,8 +102,6 @@ namespace CommonSharedConstants
const wchar_t SHOW_ENVIRONMENT_VARIABLES_EVENT[] = L"Local\\PowerToysEnvironmentVariables-ShowEnvironmentVariablesEvent-1021f616-e951-4d64-b231-a8f972159978";
const wchar_t SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT[] = L"Local\\PowerToysEnvironmentVariables-EnvironmentVariablesAdminEvent-8c95d2ad-047c-49a2-9e8b-b4656326cfb2";
- const wchar_t WORKSPACES_EXIT_EVENT[] = L"Local\\PowerToys-Workspaces-ExitEvent-29a1566f-f4f8-4d56-9435-d2a437f727c6";
-
// Max DWORD for key code to disable keys.
const DWORD VK_DISABLED = 0x100;
}
diff --git a/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs b/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs
index adaf9c9a52..2be2685d30 100644
--- a/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs
+++ b/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Threading;
using System.Windows;
using System.Windows.Interop;
using ManagedCommon;
@@ -14,10 +15,12 @@ namespace WorkspacesEditor
///
/// Interaction logic for MainWindow.xaml
///
- public partial class MainWindow : Window
+ public partial class MainWindow : Window, IDisposable
{
public MainViewModel MainViewModel { get; set; }
+ private CancellationTokenSource cancellationToken = new CancellationTokenSource();
+
private static MainPage _mainPage;
public MainWindow(MainViewModel mainViewModel)
@@ -41,10 +44,36 @@ namespace WorkspacesEditor
MaxWidth = SystemParameters.PrimaryScreenWidth;
MaxHeight = SystemParameters.PrimaryScreenHeight;
+
+ Common.UI.NativeEventWaiter.WaitForEventLoop(
+ PowerToys.Interop.Constants.WorkspacesHotkeyEvent(),
+ () =>
+ {
+ if (ApplicationIsInFocus())
+ {
+ Environment.Exit(0);
+ }
+ else
+ {
+ if (WindowState == WindowState.Minimized)
+ {
+ WindowState = WindowState.Normal;
+ }
+
+ // Get the window handle of the Workspaces Editor window
+ IntPtr handle = new WindowInteropHelper(this).Handle;
+ WindowHelpers.BringToForeground(handle);
+
+ InvalidateVisual();
+ }
+ },
+ Application.Current.Dispatcher,
+ cancellationToken.Token);
}
private void OnClosing(object sender, EventArgs e)
{
+ cancellationToken.Dispose();
App.Current.Shutdown();
}
@@ -67,5 +96,25 @@ namespace WorkspacesEditor
{
ContentFrame.GoBack();
}
+
+ public static bool ApplicationIsInFocus()
+ {
+ var activatedHandle = NativeMethods.GetForegroundWindow();
+ if (activatedHandle == IntPtr.Zero)
+ {
+ return false; // No window is currently activated
+ }
+
+ var procId = Environment.ProcessId;
+ int activeProcId;
+ _ = NativeMethods.GetWindowThreadProcessId(activatedHandle, out activeProcId);
+
+ return activeProcId == procId;
+ }
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs b/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs
index b504de59e0..4105cbe959 100644
--- a/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs
+++ b/src/modules/Workspaces/WorkspacesEditor/Utils/NativeMethods.cs
@@ -30,7 +30,7 @@ namespace WorkspacesEditor.Utils
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
- public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int processId);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
diff --git a/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp b/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp
index 090e646118..c0d2089b3e 100644
--- a/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp
+++ b/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp
@@ -77,7 +77,7 @@ public:
{
if (is_process_running())
{
- bring_process_to_front();
+ sendHotkeyEvent();
}
else
{
@@ -164,6 +164,12 @@ public:
m_toggleEditorEvent = nullptr;
}
+ if (m_hotkeyEvent)
+ {
+ CloseHandle(m_hotkeyEvent);
+ m_hotkeyEvent = nullptr;
+ }
+
delete this;
}
@@ -178,6 +184,12 @@ public:
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "Workspaces");
init_settings();
+ m_hotkeyEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::WORKSPACES_HOTKEY_EVENT);
+ if (!m_hotkeyEvent)
+ {
+ Logger::warn(L"Failed to create hotkey event. {}", get_last_error_or_default(GetLastError()));
+ }
+
m_toggleEditorEvent = CreateDefaultEvent(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT);
if (!m_toggleEditorEvent)
{
@@ -210,23 +222,12 @@ private:
executable_args.append(std::to_wstring(powertoys_pid));
}
- void SendCloseEvent()
+ void sendHotkeyEvent()
{
- auto exitEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::WORKSPACES_EXIT_EVENT);
- if (!exitEvent)
+ Logger::trace(L"Signaled hotkey event");
+ if (!SetEvent(m_hotkeyEvent))
{
- Logger::warn(L"Failed to create exitEvent. {}", get_last_error_or_default(GetLastError()));
- }
- else
- {
- Logger::trace(L"Signaled exitEvent");
- if (!SetEvent(exitEvent))
- {
- Logger::warn(L"Failed to signal exitEvent. {}", get_last_error_or_default(GetLastError()));
- }
-
- ResetEvent(exitEvent);
- CloseHandle(exitEvent);
+ Logger::warn(L"Failed to signal hotkey event. {}", get_last_error_or_default(GetLastError()));
}
}
@@ -244,10 +245,14 @@ private:
ResetEvent(m_toggleEditorEvent);
}
+ if (m_hotkeyEvent)
+ {
+ ResetEvent(m_hotkeyEvent);
+ }
+
if (m_hProcess)
{
TerminateProcess(m_hProcess, 0);
- SendCloseEvent();
m_hProcess = nullptr;
}
}
@@ -330,23 +335,6 @@ private:
m_hProcess = sei.hProcess;
}
- void bring_process_to_front()
- {
- auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL {
- HANDLE process_handle = reinterpret_cast(param);
- DWORD window_process_id = 0;
-
- GetWindowThreadProcessId(hwnd, &window_process_id);
- if (GetProcessId(process_handle) == window_process_id)
- {
- SetForegroundWindow(hwnd);
- }
- return TRUE;
- };
-
- EnumWindows(enum_windows, (LPARAM)m_hProcess);
- }
-
bool is_process_running() const
{
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
@@ -362,6 +350,9 @@ private:
// Handle to event used to invoke Workspaces Editor
HANDLE m_toggleEditorEvent;
+ // Handle to event used when hotkey is invoked
+ HANDLE m_hotkeyEvent;
+
// Hotkey to invoke the module
HotkeyEx m_hotkey{
.modifiersMask = MOD_CONTROL | MOD_WIN,