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,