mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[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:
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user