[Workspaces] fix hotkey behavior (#34497)

* [Workspaces] re-implementing hotkey handling

* [Workspaces] fix interop reference

* Reimplement message sending

* cleanup

* Do not recreate event

* bring back minimized logic

---------

Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
This commit is contained in:
Laszlo Nemeth
2024-08-30 21:29:31 +02:00
committed by GitHub
parent d42cd4bd3b
commit 39741f492f
7 changed files with 84 additions and 38 deletions

View File

@@ -151,4 +151,8 @@ namespace winrt::PowerToys::Interop::implementation
{ {
return CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT; return CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT;
} }
hstring Constants::WorkspacesHotkeyEvent()
{
return CommonSharedConstants::WORKSPACES_HOTKEY_EVENT;
}
} }

View File

@@ -41,6 +41,7 @@ namespace winrt::PowerToys::Interop::implementation
static hstring ShowEnvironmentVariablesSharedEvent(); static hstring ShowEnvironmentVariablesSharedEvent();
static hstring ShowEnvironmentVariablesAdminSharedEvent(); static hstring ShowEnvironmentVariablesAdminSharedEvent();
static hstring WorkspacesLaunchEditorEvent(); static hstring WorkspacesLaunchEditorEvent();
static hstring WorkspacesHotkeyEvent();
}; };
} }

View File

@@ -38,6 +38,7 @@ namespace PowerToys
static String ShowEnvironmentVariablesSharedEvent(); static String ShowEnvironmentVariablesSharedEvent();
static String ShowEnvironmentVariablesAdminSharedEvent(); static String ShowEnvironmentVariablesAdminSharedEvent();
static String WorkspacesLaunchEditorEvent(); static String WorkspacesLaunchEditorEvent();
static String WorkspacesHotkeyEvent();
} }
} }
} }

View File

@@ -43,7 +43,9 @@ namespace CommonSharedConstants
const wchar_t FANCY_ZONES_EDITOR_TOGGLE_EVENT[] = L"Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14"; 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_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"; 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_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 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. // Max DWORD for key code to disable keys.
const DWORD VK_DISABLED = 0x100; const DWORD VK_DISABLED = 0x100;
} }

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using ManagedCommon; using ManagedCommon;
@@ -14,10 +15,12 @@ namespace WorkspacesEditor
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
public partial class MainWindow : Window public partial class MainWindow : Window, IDisposable
{ {
public MainViewModel MainViewModel { get; set; } public MainViewModel MainViewModel { get; set; }
private CancellationTokenSource cancellationToken = new CancellationTokenSource();
private static MainPage _mainPage; private static MainPage _mainPage;
public MainWindow(MainViewModel mainViewModel) public MainWindow(MainViewModel mainViewModel)
@@ -41,10 +44,36 @@ namespace WorkspacesEditor
MaxWidth = SystemParameters.PrimaryScreenWidth; MaxWidth = SystemParameters.PrimaryScreenWidth;
MaxHeight = SystemParameters.PrimaryScreenHeight; 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) private void OnClosing(object sender, EventArgs e)
{ {
cancellationToken.Dispose();
App.Current.Shutdown(); App.Current.Shutdown();
} }
@@ -67,5 +96,25 @@ namespace WorkspacesEditor
{ {
ContentFrame.GoBack(); 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);
}
} }
} }

View File

@@ -30,7 +30,7 @@ namespace WorkspacesEditor.Utils
public static extern IntPtr GetForegroundWindow(); public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] [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")] [DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId(); public static extern uint GetCurrentThreadId();

View File

@@ -77,7 +77,7 @@ public:
{ {
if (is_process_running()) if (is_process_running())
{ {
bring_process_to_front(); sendHotkeyEvent();
} }
else else
{ {
@@ -164,6 +164,12 @@ public:
m_toggleEditorEvent = nullptr; m_toggleEditorEvent = nullptr;
} }
if (m_hotkeyEvent)
{
CloseHandle(m_hotkeyEvent);
m_hotkeyEvent = nullptr;
}
delete this; delete this;
} }
@@ -178,6 +184,12 @@ public:
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "Workspaces"); LoggerHelpers::init_logger(app_key, L"ModuleInterface", "Workspaces");
init_settings(); 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); m_toggleEditorEvent = CreateDefaultEvent(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT);
if (!m_toggleEditorEvent) if (!m_toggleEditorEvent)
{ {
@@ -210,23 +222,12 @@ private:
executable_args.append(std::to_wstring(powertoys_pid)); executable_args.append(std::to_wstring(powertoys_pid));
} }
void SendCloseEvent() void sendHotkeyEvent()
{ {
auto exitEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::WORKSPACES_EXIT_EVENT); Logger::trace(L"Signaled hotkey event");
if (!exitEvent) if (!SetEvent(m_hotkeyEvent))
{ {
Logger::warn(L"Failed to create exitEvent. {}", get_last_error_or_default(GetLastError())); Logger::warn(L"Failed to signal hotkey event. {}", 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);
} }
} }
@@ -244,10 +245,14 @@ private:
ResetEvent(m_toggleEditorEvent); ResetEvent(m_toggleEditorEvent);
} }
if (m_hotkeyEvent)
{
ResetEvent(m_hotkeyEvent);
}
if (m_hProcess) if (m_hProcess)
{ {
TerminateProcess(m_hProcess, 0); TerminateProcess(m_hProcess, 0);
SendCloseEvent();
m_hProcess = nullptr; m_hProcess = nullptr;
} }
} }
@@ -330,23 +335,6 @@ private:
m_hProcess = sei.hProcess; m_hProcess = sei.hProcess;
} }
void bring_process_to_front()
{
auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL {
HANDLE process_handle = reinterpret_cast<HANDLE>(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 bool is_process_running() const
{ {
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
@@ -362,6 +350,9 @@ private:
// Handle to event used to invoke Workspaces Editor // Handle to event used to invoke Workspaces Editor
HANDLE m_toggleEditorEvent; HANDLE m_toggleEditorEvent;
// Handle to event used when hotkey is invoked
HANDLE m_hotkeyEvent;
// Hotkey to invoke the module // Hotkey to invoke the module
HotkeyEx m_hotkey{ HotkeyEx m_hotkey{
.modifiersMask = MOD_CONTROL | MOD_WIN, .modifiersMask = MOD_CONTROL | MOD_WIN,