PowerToys interface: remove powertoys events and system_menu_helper functionality (#5323)

This commit is contained in:
Andrey Nekrasov 2020-07-31 14:06:13 +03:00 committed by GitHub
parent cff654ae69
commit 49b56d9b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 116 additions and 1163 deletions

View File

@ -11,9 +11,6 @@ public:
virtual void enable() = 0;
virtual void disable() = 0;
virtual bool is_enabled() = 0;
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) = 0;
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) = 0;
virtual void signal_system_menu_action(const wchar_t* name) = 0;
virtual void destroy() = 0;
};
@ -28,7 +25,6 @@ The PowerToys runner will, for each PowerToy DLL:
On the received object, the runner will call:
- [`get_name()`](#get_name) to get the name of the PowerToy,
- [`get_events()`](#get_events) to get the list of the events the PowerToy wants to subscribe to,
- [`enable()`](#enable) to initialize the PowerToy.
While running, the runner might call the following methods between create_powertoy()
@ -37,9 +33,6 @@ and destroy():
- [`get_config()`](#get_config) to get the available configuration settings,
- [`set_config()`](#set_config) to set settings after they have been edited in the Settings editor,
- [`call_custom_action()`](#call_custom_action) when the user selects a custom action in the Settings editor,
- [`signal_event()`](#signal_event) to send an event the PowerToy registered to.
- [`register_system_menu_helper()`](#register_system_menu_helper) to pass object, responsible for handling customized system menus, to module.
- [`signal_system_menu_action()`](#signal_system_menu_action) to send an event when action is taken on system menu item.
When terminating, the runner will:
- call [`disable()`](#disable),
@ -75,18 +68,6 @@ virtual const wchar_t* get_name()
Returns the name of the PowerToy, it will be cached by the runner.
## get_events
```cpp
virtual const wchar_t** get_events()
```
Returns a null-terminated table of the names of the events the PowerToy wants to subscribe to. Available events:
* ll_keyboard
* win_hook_event
A nullptr can be returned to signal that the PowerToy does not want to subscribe to any event.
## get_config
```
@ -140,38 +121,6 @@ Disables the PowerToy, should free as much memory as possible.
Returns the PowerToy state.
## signal_event
```cpp
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) = 0;
```
Handle event. Only the events the PowerToy subscribed to will be signaled.
The data argument and return value meaning are event-specific:
* ll_keyboard: see [`lowlevel_keyboard_event_data.h`](./lowlevel_keyboard_event_data.h).
* win_hook_event: see [`win_hook_event_data.h`](./win_hook_event_data.h)
Please note that some of the events are currently being signalled from a separate thread.
## register_system_menu_helper
```cpp
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) = 0;
```
Register helper class to handle all system menu items related actions. Creation, deletion
and all other actions taken on system menu item will be handled by provided class.
Module will be informed when action is taken on any item created on request of the module.
## signal_system_menu_action
```cpp
virtual void signal_system_menu_action(const wchar_t* name) = 0;
```
Runner invokes this API when action is taken on item created on request from the module.
Item name is passed as an argument, so that module can distinguish between different menu items.
## destroy
```cpp
@ -179,62 +128,7 @@ Item name is passed as an argument, so that module can distinguish between diffe
```
Destroy the PowerToy and free all memory.
## Powertoys system menu helper interface
Interface for helper class responsible for handling all system menu related actions.
```cpp
class PowertoySystemMenuIface {
public:
struct ItemInfo {
std::wstring name{};
bool enable{ false };
bool checkBox{ false };
};
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) = 0;
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) = 0;
};
```
## ItemInfo
```cpp
struct ItemInfo {
std::wstring name{};
bool enable{ false };
bool checkBox{ false };
};
```
Structure containing all relevant information for system menu item: name (and hotkey if available), item
status at creation (enabled/disabled) and whether check box will appear next to item name when action is taken.
## SetConfiguration
```cpp
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) = 0;
```
Module should use this interface to inform system menu helper class which custom system menu items to create.
## ProcessSelectedItem
```cpp
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) = 0;
```
Process action taken on specific system menu item.
# Code organization
### [`powertoy_module_interface.h`](/src/modules/interface/powertoy_module_interface.h)
Contains the PowerToys interface definition.
### [`powertoy_system_menu.h`](/src/modules/interface/powertoy_system_module.h)
Contains the PowerToys system menu helper interface definition.
### [`lowlevel_keyboard_event_data.h`](/src/modules/interface/lowlevel_keyboard_event_data.h)
Contains the `LowlevelKeyboardEvent` structure that's passed to `signal_event` for `ll_keyboard` events.
### [`win_hook_event_data.h`](/src/modules/interface/win_hook_event_data.h)
Contains the `WinHookEvent` structure that's passed to `signal_event` for `win_hook_event` events.

View File

@ -1,85 +0,0 @@
# Shared hooks
To minimize the performance impact on the machine only `runner` installs global hooks, passing the events to registered callbacks in each PowerToy module.
When a PowerToy module is loaded, the `runner` calls the [`get_events()`](/src/modules/interface/powertoy_module_interface.h#L40) method to get a NULL-terminated array of NULL-terminated strings with the names of the events that the PowerToy wants to subscribe to. A `const wchar_t*` string is provided for each of the event names.
Events are signalled by the `runner` calling the [`signal_event(name, data)`](/src/modules/interface/powertoy_module_interface.h#L53) method of the PowerToy module. The `name` parameter contains the NULL-terminated name of the event. The `data` parameter and the method return value are specific for each event.
Currently supported hooks:
* `"ll_keyboard"` - [Low Level Keyboard Hook](#low-level-keyboard-hook)
* `"win_hook_event"` - [Windows Event Hook](#windows-event-hook)
## Low Level Keyboard Hook
This event is signaled whenever the user presses or releases a key on the keyboard. To subscribe to this event, add `"ll_keyboard"` to the table returned by the `get_events()` method.
The PowerToys runner installs low-level keyboard hook using `SetWindowsHookEx(WH_KEYBOARD_LL, ...)`. See [this MSDN page](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v%3Dvs.85)) for details.
When a keyboard event is signaled and `ncCode` equals `HC_ACTION`, the `wParam` and `lParam` event parameters are passed to all subscribed clients in the [`LowlevelKeyboardEvent`](/src/modules/interface/lowlevel_keyboard_event_data.h#L38-L41) struct.
The `intptr_t data` event argument is a pointer to the `LowlevelKeyboardEvent` struct.
A non-zero return value from any of the subscribed PowerToys will cause the runner hook proc to return 1, thus swallowing the keyboard event.
Example usage, that makes Windows ignore the L key:
```c++
virtual const wchar_t** get_events() override {
static const wchar_t* events[2] = { ll_keyboard,
nullptr };
return events;
}
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
if (wcscmp(name, ll_keyboard) == 0) {
auto& event = *(reinterpret_cast<LowlevelKeyboardEvent*>(data));
// The L key has vkCode of 0x4C, see:
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
if (event.wParam == WM_KEYDOWN && event.lParam->vkCode == 0x4C) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
```
## Windows Event Hook
This event is signaled for [a range of events](https://docs.microsoft.com/pl-pl/windows/win32/winauto/event-constants). To subscribe to this event, add `"win_hook_event"` to the table returned by the `get_events()` method. See [this MSDN doc](https://docs.microsoft.com/pl-pl/windows/win32/api/winuser/nf-winuser-setwineventhook) for details.
The `intptr_t data` event argument is a pointer to the [`WinHookEvent`](/src/modules/interface/win_hook_event_data.h#L43-L50) struct.
The return value of the event handler is ignored.
Example usage, that detects a window being resized:
```c++
virtual const wchar_t** get_events() override {
static const wchar_t* events[2] = { win_hook_event,
nullptr };
return events;
}
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
if (wcscmp(name, win_hook_event) == 0) {
auto& event = *(reinterpret_cast<WinHookEvent*>(data));
switch (event.event) {
case EVENT_SYSTEM_MOVESIZESTART:
size_start(event.hwnd);
break;
case EVENT_SYSTEM_MOVESIZEEND:
size_end(event.hwnd);
break;
default:
break;
}
}
return 0;
}
```
Taking too long to process the events has negative impact on the whole system performance. To address this, the events are signaled from a different thread, not from the event hook callback itself.

View File

@ -0,0 +1,9 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct LowlevelKeyboardEvent
{
KBDLLHOOKSTRUCT* lParam;
WPARAM wParam;
};

14
src/common/WinHookEvent.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct WinHookEvent
{
DWORD event;
HWND hwnd;
LONG idObject;
LONG idChild;
DWORD idEventThread;
DWORD dwmsEventTime;
};

View File

@ -128,6 +128,7 @@
<ClInclude Include="com_object_factory.h" />
<ClInclude Include="keyboard_layout.h" />
<ClInclude Include="keyboard_layout_impl.h" />
<ClInclude Include="LowlevelKeyboardEvent.h" />
<ClInclude Include="notifications.h" />
<ClInclude Include="processApi.h" />
<ClInclude Include="RcResource.h" />
@ -153,6 +154,7 @@
<ClInclude Include="two_way_pipe_message_ipc_impl.h" />
<ClInclude Include="version.h" />
<ClInclude Include="windows_colors.h" />
<ClInclude Include="WinHookEvent.h" />
<ClInclude Include="winstore.h" />
</ItemGroup>
<ItemGroup>

View File

@ -129,6 +129,12 @@
<ClInclude Include="processApi.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LowlevelKeyboardEvent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WinHookEvent.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="d2d_svg.cpp">

View File

@ -1,6 +1,5 @@
#pragma once
#include "keyboard_layout.h"
#include "..\modules\interface\lowlevel_keyboard_event_data.h"
#include <string>
#include <map>
#include <mutex>

View File

@ -9,10 +9,9 @@
extern "C" IMAGE_DOS_HEADER __ImageBase;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
@ -27,7 +26,8 @@ BOOL APIENTRY DllMain( HMODULE hModule,
return TRUE;
}
struct ModuleSettings{
struct ModuleSettings
{
} g_settings;
class ColorPicker : public PowertoyModuleIface
@ -39,8 +39,9 @@ private:
HANDLE m_hProcess;
// Time to wait for process to close after sending WM_CLOSE signal
// Time to wait for process to close after sending WM_CLOSE signal
static const int MAX_WAIT_MILLISEC = 10000;
public:
ColorPicker()
{
@ -67,13 +68,6 @@ public:
return app_name.c_str();
}
virtual const wchar_t** get_events() override
{
static const wchar_t* events[] = { nullptr };
return events;
}
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
@ -111,8 +105,8 @@ public:
}
}
virtual void enable(){
virtual void enable()
{
// use only with new settings?
if (UseNewSettings())
{
@ -134,7 +128,8 @@ public:
}
};
virtual void disable() {
virtual void disable()
{
if (m_enabled)
{
terminateProcess();
@ -148,11 +143,6 @@ public:
return m_enabled;
}
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
return 0;
}
static BOOL CALLBACK requestMainWindowClose(HWND nextWindow, LPARAM closePid)
{
DWORD windowPid;
@ -174,15 +164,9 @@ public:
TerminateProcess(m_hProcess, 1);
}
}
/* Register helper class to handle system menu items related actions. */
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) {}
/* Handle action on system menu item. */
virtual void signal_system_menu_action(const wchar_t* name) {}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new ColorPicker();
}

View File

@ -2,9 +2,8 @@
#include <common/settings_objects.h>
#include <common/common.h>
#include <common/debug_control.h>
#include <common/LowlevelKeyboardEvent.h>
#include <interface/powertoy_module_interface.h>
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <lib/ZoneSet.h>
#include <lib/resource.h>
@ -44,13 +43,6 @@ public:
return app_name.c_str();
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty list.
virtual PCWSTR* get_events() override
{
return nullptr;
}
// Return JSON with the configuration options.
// These are the settings shown on the settings page along with their current values.
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
@ -134,15 +126,6 @@ public:
return (m_app != nullptr);
}
// PowertoyModuleIface method, unused
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
return 0;
}
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
// Destroy the powertoy and free memory
virtual void destroy() override
{

View File

@ -16,7 +16,6 @@
#include "VirtualDesktopUtils.h"
#include "MonitorWorkAreaHandler.h"
#include <interface/win_hook_event_data.h>
#include <lib/SecondaryMouseButtonsHook.h>
#include <lib/GenericKeyHook.h>
@ -365,7 +364,8 @@ bool FancyZones::ShouldProcessNewWindow(HWND window) noexcept
}
std::vector<int> FancyZones::GetZoneIndexSetFromWorkAreaHistory(
HWND window, winrt::com_ptr<IZoneWindow> workArea) noexcept
HWND window,
winrt::com_ptr<IZoneWindow> workArea) noexcept
{
const auto activeZoneSet = workArea->ActiveZoneSet();
if (activeZoneSet)
@ -458,9 +458,9 @@ RECT FitOnScreen(const RECT& windowRect, const RECT& originMonitorRect, const RE
H = min(H, RectHeight(destMonitorRect) - CUSTOM_POSITIONING_LEFT_TOP_PADDING);
}
return { .left = left,
.top = top,
.right = left + W,
return { .left = left,
.top = top,
.right = left + W,
.bottom = top + H };
}
@ -506,7 +506,7 @@ FancyZones::WindowCreated(HWND window) noexcept
// creation of new window. We need to check if window being processed is on currently active desktop.
return;
}
const bool moveToAppLastZone = m_settings->GetSettings()->appLastZone_moveWindows;
const bool moveToAppLastZone = m_settings->GetSettings()->appLastZone_moveWindows;
const bool openOnActiveMonitor = m_settings->GetSettings()->openWindowOnActiveMonitor;
if ((moveToAppLastZone || openOnActiveMonitor) && ShouldProcessNewWindow(window))
{

View File

@ -1,5 +1,7 @@
#pragma once
#include <common/WinHookEvent.h>
interface IZoneWindow;
interface IFancyZonesSettings;
interface IZoneSet;
@ -11,11 +13,13 @@ interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones
/**
* Start and initialize FancyZones.
*/
IFACEMETHOD_(void, Run)() = 0;
IFACEMETHOD_(void, Run)
() = 0;
/**
* Stop FancyZones and do the clean up.
*/
IFACEMETHOD_(void, Destroy)() = 0;
IFACEMETHOD_(void, Destroy)
() = 0;
};
/**
@ -26,13 +30,15 @@ interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZones
/**
* Inform FancyZones that user has switched between virtual desktops.
*/
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
IFACEMETHOD_(void, VirtualDesktopChanged)
() = 0;
/**
* Callback from WinEventHook to FancyZones
*
* @param data Handle of window being moved or resized.
*/
IFACEMETHOD_(void, HandleWinHookEvent)(const WinHookEvent* data) = 0;
IFACEMETHOD_(void, HandleWinHookEvent)
(const WinHookEvent* data) = 0;
/**
* Process keyboard event.
*
@ -40,15 +46,18 @@ interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZones
* @returns Boolean indicating if this event should be passed on further to other applications
* in event chain, or should it be suppressed.
*/
IFACEMETHOD_(bool, OnKeyDown)(PKBDLLHOOKSTRUCT info) = 0;
IFACEMETHOD_(bool, OnKeyDown)
(PKBDLLHOOKSTRUCT info) = 0;
/**
* Toggle FancyZones editor application.
*/
IFACEMETHOD_(void, ToggleEditor)() = 0;
IFACEMETHOD_(void, ToggleEditor)
() = 0;
/**
* Callback triggered when user changes FancyZones settings.
*/
IFACEMETHOD_(void, SettingsChanged)() = 0;
IFACEMETHOD_(void, SettingsChanged)
() = 0;
};
/**
@ -59,31 +68,38 @@ interface __declspec(uuid("{5C8D99D6-34B2-4F4A-A8E5-7483F6869775}")) IZoneWindow
/**
* Assign window to appropriate zone inside new zone layout.
*/
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
/**
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)
() = 0;
/**
* @returns Basic zone color.
*/
IFACEMETHOD_(COLORREF, GetZoneColor)() = 0;
/**
IFACEMETHOD_(COLORREF, GetZoneColor)
() = 0;
/**
* @returns Zone border color.
*/
IFACEMETHOD_(COLORREF, GetZoneBorderColor)() = 0;
IFACEMETHOD_(COLORREF, GetZoneBorderColor)
() = 0;
/**
* @returns Color used to highlight zone while giving zone layout hints.
*/
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)() = 0;
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)
() = 0;
/**
* @returns Integer in range [0, 100] indicating opacity of highlighted zone (while giving zone layout hints).
*/
IFACEMETHOD_(int, GetZoneHighlightOpacity)() = 0;
IFACEMETHOD_(int, GetZoneHighlightOpacity)
() = 0;
/**
* @returns Boolean indicating if dragged window should be transparent.
*/
IFACEMETHOD_(bool, isMakeDraggedWindowTransparentActive) () = 0;
IFACEMETHOD_(bool, isMakeDraggedWindowTransparentActive)
() = 0;
/**
* @returns Boolean indicating if move/size operation is currently active.
*/
IFACEMETHOD_(bool, InMoveSize) () = 0;
IFACEMETHOD_(bool, InMoveSize)
() = 0;
};
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept;

View File

@ -53,15 +53,6 @@ public:
return app_name.c_str();
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty
// list.
virtual const wchar_t** get_events() override
{
static const wchar_t* events[] = { nullptr };
return events;
}
// Return JSON with the configuration options.
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
@ -104,15 +95,6 @@ public:
{
return m_enabled;
}
// Handle incoming event, data is event-specific
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
return 0;
}
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()

View File

@ -1,45 +0,0 @@
#pragma once
#include <Windows.h>
/*
ll_keyboard - Lowlevel Keyboard Hook
The PowerToys runner installs low-level keyboard hook using
SetWindowsHookEx(WH_KEYBOARD_LL, ...)
See https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v%3Dvs.85)
for details.
When a keyboard event is signaled and ncCode equals HC_ACTION, the wParam
and lParam event parameters are passed to all subscribed clients in
the LowlevelKeyboardEvent struct.
The intptr_t data event argument is a pointer to the LowlevelKeyboardEvent struct.
A non-zero return value from any of the subscribed PowerToys will cause
the runner hook proc to return 1, thus swallowing the keyboard event.
Example usage, that makes Windows ignore the L key:
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
if (wcscmp(name, ll_keyboard) == 0) {
auto& event = *(reinterpret_cast<LowlevelKeyboardEvent*>(data));
// The L key has vkCode of 0x4C
if (event.wParam == WM_KEYDOWN && event.lParam->vkCode == 0x4C) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
*/
namespace {
const wchar_t* ll_keyboard = L"ll_keyboard";
}
struct LowlevelKeyboardEvent {
KBDLLHOOKSTRUCT* lParam;
WPARAM wParam;
};

View File

@ -12,7 +12,6 @@
On the received object, the runner will call:
- get_name() to get the name of the PowerToy,
- get_events() to get the list of the events the PowerToy wants to subscribe to,
- enable() to initialize the PowerToy.
While running, the runner might call the following methods between create_powertoy()
@ -21,58 +20,35 @@
- get_config() to get the available configuration settings,
- set_config() to set various settings,
- call_custom_action() when the user selects clicks a custom action in settings,
- signal_event() to send an event the PowerToy registered to.
When terminating, the runner will:
- call destroy() which should free all the memory and delete the PowerToy object,
- unload the DLL.
*/
class PowertoySystemMenuIface;
class PowertoyModuleIface {
class PowertoyModuleIface
{
public:
/* Returns the name of the PowerToy, this will be cached by the runner. */
virtual const wchar_t* get_name() = 0;
/* Returns a null-terminated table of the names of the events the PowerToy wants to
subscribe to. Available events:
* ll_keyboard
* win_hook_event
A nullptr can be returned to signal that the PowerToy does not want to subscribe
to any event.
*/
virtual const wchar_t** get_events() = 0;
/* Fills a buffer with the available configuration settings.
* If 'buffer' is a null ptr or the buffer size is not large enough
* sets the required buffer size in 'buffer_size' and return false.
* Returns true if successful.
*/
virtual bool get_config(wchar_t* buffer, int *buffer_size) = 0;
/* Sets the configuration values. */
virtual void set_config(const wchar_t* config) = 0;
/* Call custom action from settings screen. */
virtual void call_custom_action(const wchar_t* action) {};
/* Enables the PowerToy. */
virtual void enable() = 0;
/* Disables the PowerToy, should free as much memory as possible. */
virtual void disable() = 0;
/* Should return if the PowerToys is enabled or disabled. */
virtual bool is_enabled() = 0;
/* Handle event. Only the events the PowerToy subscribed to will be signaled.
The data argument and return value meaning are event-specific:
* ll_keyboard: see lowlevel_keyboard_event_data.h.
* win_hook_event: see win_hook_event_data.h
*/
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) = 0;
/* Register helper class to handle system menu items related actions. */
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) = 0;
/* Handle action on system menu item. */
virtual void signal_system_menu_action(const wchar_t* name) = 0;
/* Destroy the PowerToy and free all memory. */
virtual void destroy() = 0;
/* Returns the name of the PowerToy, this will be cached by the runner. */
virtual const wchar_t* get_name() = 0;
/* Fills a buffer with the available configuration settings.
* If 'buffer' is a null ptr or the buffer size is not large enough
* sets the required buffer size in 'buffer_size' and return false.
* Returns true if successful.
*/
virtual bool get_config(wchar_t* buffer, int* buffer_size) = 0;
/* Sets the configuration values. */
virtual void set_config(const wchar_t* config) = 0;
/* Call custom action from settings screen. */
virtual void call_custom_action(const wchar_t* action){};
/* Enables the PowerToy. */
virtual void enable() = 0;
/* Disables the PowerToy, should free as much memory as possible. */
virtual void disable() = 0;
/* Should return if the PowerToys is enabled or disabled. */
virtual bool is_enabled() = 0;
/* Destroy the PowerToy and free all memory. */
virtual void destroy() = 0;
};
/*
@ -90,4 +66,4 @@ public:
In case of errors return nullptr.
*/
typedef PowertoyModuleIface* (__cdecl *powertoy_create_func)();
typedef PowertoyModuleIface*(__cdecl* powertoy_create_func)();

View File

@ -1,22 +0,0 @@
#pragma once
#include <string>
class PowertoyModuleIface;
class PowertoySystemMenuIface {
public:
struct ItemInfo {
std::wstring name{};
bool enable{ false };
bool checkBox{ false };
};
/*
* Set configuration of system menu items for specific powertoy module. Configuration
* parameters include item name (and hotkey), item status at creation (enabled/disabled)
* and whether check box will appear next to item name when action is taken.
*/
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) = 0;
/* Process action on specific system menu item. */
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) = 0;
};

View File

@ -1,50 +0,0 @@
#pragma once
#include <Windows.h>
/*
win_hook_event - Windows Event Hook
The PowerToys runner installs event hook functions for a range of events. See
https://docs.microsoft.com/pl-pl/windows/win32/api/winuser/nf-winuser-setwineventhook
for details.
The intptr_t data event argument is a pointer to the WinHookEvent struct.
The return value of the event handler is ignored.
Example usage, that detects a window being resized:
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override {
if (wcscmp(name, win_hook_event) == 0) {
auto& event = *(reinterpret_cast<WinHookEvent*>(data));
switch (event.event) {
case EVENT_SYSTEM_MOVESIZESTART:
size_start(event.hwnd);
break;
case EVENT_SYSTEM_MOVESIZEEND:
size_end(event.hwnd);
break;
default:
break;
}
}
return 0;
}
Taking too long to process the events has negative impact on the whole system
performance. To address this, the events are signaled from a different
thread, not from the event hook callback itself.
*/
namespace {
const wchar_t* win_hook_event = L"win_hook_event";
}
struct WinHookEvent {
DWORD event;
HWND hwnd;
LONG idObject;
LONG idChild;
DWORD idEventThread;
DWORD dwmsEventTime;
};

View File

@ -3,8 +3,8 @@
#include <thread>
#include <queue>
#include <mutex>
#include <interface/lowlevel_keyboard_event_data.h>
#include <LowlevelKeyboardEvent.h>
// Available states for the KeyDelay state machine.
enum class KeyDelayState
{

View File

@ -2,8 +2,8 @@
#include <mutex>
#include "KeyboardManagerConstants.h"
#include "../common/keyboard_layout.h"
#include "../common/LowlevelKeyboardEvent.h"
#include <functional>
#include <interface/lowlevel_keyboard_event_data.h>
#include <variant>
#include "Shortcut.h"
#include "RemapShortcut.h"

View File

@ -2,7 +2,6 @@
#include "Shortcut.h"
#include "../common/keyboard_layout.h"
#include "../common/shared_constants.h"
#include <interface/lowlevel_keyboard_event_data.h>
#include "Helpers.h"
#include "InputInterface.h"

View File

@ -1,9 +1,10 @@
#pragma once
#include <interface/lowlevel_keyboard_event_data.h>
#include <map>
#include <mutex>
#include "keyboardmanager/common/KeyboardManagerConstants.h"
#include <common/LowlevelKeyboardEvent.h>
class InputInterface;
class KeyboardManagerState;
class Shortcut;

View File

@ -1,7 +1,5 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <common/settings_objects.h>
#include <common/shared_constants.h>
#include "resource.h"
@ -235,16 +233,6 @@ public:
return app_name.c_str();
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty
// list.
virtual const wchar_t** get_events() override
{
static const wchar_t* events[] = { ll_keyboard, nullptr };
return events;
}
// Return JSON with the configuration options.
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
@ -339,16 +327,6 @@ public:
return m_enabled;
}
// Handle incoming event, data is event-specific
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
return 0;
}
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
// Hook procedure definition
static LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
{

View File

@ -2,7 +2,8 @@
#include <keyboardmanager/common/InputInterface.h>
#include <vector>
#include <functional>
#include <interface/lowlevel_keyboard_event_data.h>
#include <common/LowlevelKeyboardEvent.h>
// Class for mocked keyboard input
class MockedInput :

View File

@ -1,7 +1,5 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <common/settings_objects.h>
#include <common/common.h>
#include "trace.h"
@ -80,23 +78,6 @@ public:
return app_name.c_str();
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty
// list.
virtual const wchar_t** get_events() override
{
static const wchar_t* events[] = { nullptr };
// Available events:
// - ll_keyboard
// - win_hook_event
//
// static const wchar_t* events[] = { ll_keyboard,
// win_hook_event,
// nullptr };
return events;
}
// Return JSON with the configuration options.
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
@ -232,25 +213,6 @@ public:
return m_enabled;
}
// Handle incoming event, data is event-specific
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
if (wcscmp(name, ll_keyboard) == 0)
{
auto& event = *(reinterpret_cast<LowlevelKeyboardEvent*>(data));
// Return 1 if the keypress is to be suppressed (not forwarded to Windows),
// otherwise return 0.
return 0;
}
else if (wcscmp(name, win_hook_event) == 0)
{
auto& event = *(reinterpret_cast<WinHookEvent*>(data));
// Return value is ignored
return 0;
}
return 0;
}
// Callback to send WM_CLOSE signal to each top level window.
static BOOL CALLBACK requestMainWindowClose(HWND nextWindow, LPARAM closePid)
{
@ -274,11 +236,6 @@ public:
TerminateProcess(m_hProcess, 1);
}
}
/* Register helper class to handle system menu items related actions. */
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) {}
/* Handle action on system menu item. */
virtual void signal_system_menu_action(const wchar_t* name) {}
};
// Load the settings file.

View File

@ -183,13 +183,6 @@ public:
return m_enabled;
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty list.
virtual PCWSTR* get_events() override
{
return nullptr;
}
// Return JSON with the configuration options.
// These are the settings shown on the settings page along with their current values.
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
@ -266,15 +259,6 @@ public:
{
}
// Handle incoming event, data is event-specific
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
return 0;
}
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
// Destroy the powertoy and free memory
virtual void destroy() override
{

View File

@ -1,6 +1,4 @@
#include "pch.h"
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <settings_objects.h>
#include <common.h>
#include "powerpreview.h"
@ -49,11 +47,6 @@ const wchar_t* PowerPreviewModule::get_name()
return m_moduleName.c_str();
}
const wchar_t** PowerPreviewModule::get_events()
{
return nullptr;
}
// Return JSON with the configuration options.
bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size)
{
@ -89,7 +82,6 @@ bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_siz
thumbnailProvider->GetToggleSettingState());
}
return settings.serialize_to_buffer(buffer, buffer_size);
}
@ -182,12 +174,6 @@ bool PowerPreviewModule::is_enabled()
return this->m_enabled;
}
// Handle incoming event, data is event-specific
intptr_t PowerPreviewModule::signal_event(const wchar_t* name, intptr_t data)
{
return 0;
}
// Load the settings file.
void PowerPreviewModule::init_settings()
{
@ -207,7 +193,6 @@ void PowerPreviewModule::init_settings()
{
thumbnailProvider->LoadState(settings);
}
}
catch (std::exception const& e)
{

View File

@ -16,7 +16,7 @@ private:
// The PowerToy state.
bool m_enabled = false;
std::wstring m_moduleName;
std::vector<FileExplorerPreviewSettings *> m_previewHandlers;
std::vector<FileExplorerPreviewSettings*> m_previewHandlers;
std::vector<FileExplorerPreviewSettings*> m_thumbnailProviders;
public:
@ -39,8 +39,7 @@ public:
GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION),
L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}",
L"Markdown Preview Handler",
new RegistryWrapper())
}),
new RegistryWrapper()) }),
m_thumbnailProviders(
{ // TODO: MOVE THIS SVG Thumbnail Provider settings object.
new FileExplorerPreviewSettings(
@ -49,22 +48,17 @@ public:
GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
L"{36B27788-A8BB-4698-A756-DF9F11F64F84}",
L"SVG Thumbnail Provider",
new RegistryWrapper())
})
new RegistryWrapper()) })
{
init_settings();
};
virtual void destroy();
virtual const wchar_t* get_name();
virtual const wchar_t** get_events();
virtual bool get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size);
virtual void set_config(const wchar_t* config);
virtual void enable();
virtual void disable();
virtual bool is_enabled();
virtual void init_settings();
virtual intptr_t signal_event(const wchar_t* name, intptr_t data);
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
};

View File

@ -42,11 +42,6 @@ const wchar_t* OverlayWindow::get_name()
return app_name.c_str();
}
const wchar_t** OverlayWindow::get_events()
{
return nullptr;
}
bool OverlayWindow::get_config(wchar_t* buffer, int* buffer_size)
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
@ -213,11 +208,6 @@ bool OverlayWindow::is_enabled()
return _enabled;
}
intptr_t OverlayWindow::signal_event(const wchar_t* name, intptr_t data)
{
return 0;
}
intptr_t OverlayWindow::signal_event(LowlevelKeyboardEvent* event)
{
if (!_enabled)

View File

@ -1,10 +1,11 @@
#pragma once
#include <interface/powertoy_module_interface.h>
#include <interface/lowlevel_keyboard_event_data.h>
#include "overlay_window.h"
#include "resource.h"
#include <common/LowlevelKeyboardEvent.h>
// We support only one instance of the overlay
extern class OverlayWindow* instance;
@ -16,7 +17,6 @@ public:
OverlayWindow();
virtual const wchar_t* get_name() override;
virtual const wchar_t** get_events() override;
virtual bool get_config(wchar_t* buffer, int* buffer_size) override;
virtual void set_config(const wchar_t* config) override;
@ -24,18 +24,11 @@ public:
virtual void disable() override;
virtual bool is_enabled() override;
// PowerToys interface method, not used
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override;
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
virtual void signal_system_menu_action(const wchar_t* name) override {}
void on_held();
void on_held_press(DWORD vkCode);
void quick_hide();
void was_hidden();
// Method called from LowLevelKeyboardProc
intptr_t signal_event(LowlevelKeyboardEvent* event);
virtual void destroy() override;

View File

@ -1,53 +0,0 @@
#include "pch.h"
#include "lowlevel_keyboard_event.h"
#include "powertoys_events.h"
#include <common/debug_control.h>
namespace
{
HHOOK hook_handle = nullptr;
HHOOK hook_handle_copy = nullptr; // make sure we do use nullptr in CallNextHookEx call
LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
{
LowlevelKeyboardEvent event;
if (nCode == HC_ACTION)
{
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (powertoys_events().signal_event(ll_keyboard, reinterpret_cast<intptr_t>(&event)) != 0)
{
return 1;
}
}
return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
}
}
void start_lowlevel_keyboard_hook()
{
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
if (IsDebuggerPresent())
{
return;
}
#endif
if (!hook_handle)
{
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
hook_handle_copy = hook_handle;
if (!hook_handle)
{
throw std::runtime_error("Cannot install keyboard listener");
}
}
}
void stop_lowlevel_keyboard_hook()
{
if (hook_handle)
{
UnhookWindowsHookEx(hook_handle);
hook_handle = nullptr;
}
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <interface/lowlevel_keyboard_event_data.h>
void start_lowlevel_keyboard_hook();
void stop_lowlevel_keyboard_hook();

View File

@ -400,8 +400,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
{
// Singletons initialization order needs to be preserved, first events and
// then modules to guarantee the reverse destruction order.
SystemMenuHelperInstance();
powertoys_events();
modules();
auto general_settings = load_general_settings();

View File

@ -22,7 +22,6 @@ PowertoyModule load_powertoy(const std::wstring_view filename)
FreeLibrary(handle);
winrt::throw_last_error();
}
module->register_system_menu_helper(&SystemMenuHelperInstance());
return PowertoyModule(module, handle);
}
@ -43,16 +42,4 @@ PowertoyModule::PowertoyModule(PowertoyModuleIface* module, HMODULE handle) :
{
throw std::runtime_error("Module not initialized");
}
auto want_signals = module->get_events();
if (want_signals)
{
for (; *want_signals; ++want_signals)
{
powertoys_events().register_receiver(*want_signals, module);
}
}
if (SystemMenuHelperInstance().HasCustomConfig(module))
{
powertoys_events().register_system_menu_action(module);
}
}

View File

@ -1,6 +1,4 @@
#pragma once
#include "powertoys_events.h"
#include "system_menu_helper.h"
#include <interface/powertoy_module_interface.h>
#include <string>
#include <memory>
@ -16,8 +14,6 @@ struct PowertoyModuleDeleter
{
if (module)
{
powertoys_events().unregister_system_menu_action(module);
powertoys_events().unregister_receiver(module);
module->destroy();
}
}

View File

@ -1,105 +0,0 @@
#include "pch.h"
#include "powertoys_events.h"
#include "lowlevel_keyboard_event.h"
#include "win_hook_event.h"
#include "system_menu_helper.h"
void first_subscribed(const std::wstring& event)
{
if (event == ll_keyboard)
start_lowlevel_keyboard_hook();
else if (event == win_hook_event)
start_win_hook_event();
}
void last_unsubscribed(const std::wstring& event)
{
if (event == ll_keyboard)
stop_lowlevel_keyboard_hook();
else if (event == win_hook_event)
stop_win_hook_event();
}
PowertoysEvents& powertoys_events()
{
static PowertoysEvents powertoys_events;
return powertoys_events;
}
void PowertoysEvents::register_receiver(const std::wstring& event, PowertoyModuleIface* module)
{
std::unique_lock lock(mutex);
auto& subscribers = receivers[event];
if (subscribers.empty())
{
first_subscribed(event);
}
subscribers.push_back(module);
}
void PowertoysEvents::unregister_receiver(PowertoyModuleIface* module)
{
std::unique_lock lock(mutex);
for (auto& [event, subscribers] : receivers)
{
subscribers.erase(remove(begin(subscribers), end(subscribers), module), end(subscribers));
if (subscribers.empty())
{
last_unsubscribed(event);
}
}
}
void PowertoysEvents::register_system_menu_action(PowertoyModuleIface* module)
{
std::unique_lock lock(mutex);
system_menu_receivers.insert(module);
}
void PowertoysEvents::unregister_system_menu_action(PowertoyModuleIface* module)
{
std::unique_lock lock(mutex);
auto it = system_menu_receivers.find(module);
if (it != system_menu_receivers.end())
{
SystemMenuHelperInstance().Reset(module);
system_menu_receivers.erase(it);
}
}
void PowertoysEvents::handle_system_menu_action(const WinHookEvent& data)
{
if (data.event == EVENT_SYSTEM_MENUSTART)
{
for (auto& module : system_menu_receivers)
{
SystemMenuHelperInstance().Customize(module, data.hwnd);
}
}
else if (data.event == EVENT_OBJECT_INVOKED)
{
if (PowertoyModuleIface * module{ SystemMenuHelperInstance().ModuleFromItemId(data.idChild) })
{
std::wstring itemName = SystemMenuHelperInstance().ItemNameFromItemId(data.idChild);
// Process event on specified system menu item by responsible module.
module->signal_system_menu_action(itemName.c_str());
// Process event on specified system menu item by system menu helper (check/uncheck if needed).
SystemMenuHelperInstance().ProcessSelectedItem(module, GetForegroundWindow(), itemName.c_str());
}
}
}
intptr_t PowertoysEvents::signal_event(const std::wstring& event, intptr_t data)
{
intptr_t rvalue = 0;
std::shared_lock lock(mutex);
if (auto it = receivers.find(event); it != end(receivers))
{
for (auto& module : it->second)
{
if (module)
rvalue |= module->signal_event(event.c_str(), data);
}
}
return rvalue;
}

View File

@ -1,29 +0,0 @@
#pragma once
#include <interface/powertoy_module_interface.h>
#include <interface/win_hook_event_data.h>
#include <string>
#include <shared_mutex>
class PowertoysEvents
{
public:
void register_receiver(const std::wstring& event, PowertoyModuleIface* module);
void unregister_receiver(PowertoyModuleIface* module);
void register_system_menu_action(PowertoyModuleIface* module);
void unregister_system_menu_action(PowertoyModuleIface* module);
void handle_system_menu_action(const WinHookEvent& data);
intptr_t signal_event(const std::wstring& event, intptr_t data);
private:
std::shared_mutex mutex;
std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers;
std::unordered_set<PowertoyModuleIface*> system_menu_receivers;
};
PowertoysEvents& powertoys_events();
void first_subscribed(const std::wstring& event);
void last_unsubscribed(const std::wstring& event);

View File

@ -111,41 +111,33 @@
<ClCompile Include="action_runner_utils.cpp" />
<ClCompile Include="auto_start_helper.cpp" />
<ClCompile Include="general_settings.cpp" />
<ClCompile Include="lowlevel_keyboard_event.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="powertoys_events.cpp" />
<ClCompile Include="powertoy_module.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="restart_elevated.cpp" />
<ClCompile Include="settings_window.cpp" />
<ClCompile Include="system_menu_helper.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="tray_icon.cpp" />
<ClCompile Include="unhandled_exception_handler.cpp" />
<ClCompile Include="update_utils.cpp" />
<ClCompile Include="update_state.cpp" />
<ClCompile Include="win_hook_event.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="action_runner_utils.h" />
<ClInclude Include="auto_start_helper.h" />
<ClInclude Include="general_settings.h" />
<ClInclude Include="lowlevel_keyboard_event.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="update_utils.h" />
<ClInclude Include="update_state.h" />
<ClInclude Include="powertoys_events.h" />
<ClInclude Include="powertoy_module.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="restart_elevated.h" />
<ClInclude Include="settings_window.h" />
<ClInclude Include="system_menu_helper.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="tray_icon.h" />
<ClInclude Include="unhandled_exception_handler.h" />
<ClInclude Include="win_hook_event.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="runner.rc" />

View File

@ -6,9 +6,6 @@
<ClCompile Include="unhandled_exception_handler.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="lowlevel_keyboard_event.cpp">
<Filter>Events</Filter>
</ClCompile>
<ClCompile Include="tray_icon.cpp">
<Filter>Utils</Filter>
</ClCompile>
@ -27,15 +24,6 @@
<ClCompile Include="general_settings.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="powertoys_events.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="win_hook_event.cpp">
<Filter>Events</Filter>
</ClCompile>
<ClCompile Include="system_menu_helper.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="restart_elevated.cpp">
<Filter>Utils</Filter>
</ClCompile>
@ -54,9 +42,6 @@
<ClInclude Include="unhandled_exception_handler.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="lowlevel_keyboard_event.h">
<Filter>Events</Filter>
</ClInclude>
<ClInclude Include="tray_icon.h">
<Filter>Utils</Filter>
</ClInclude>
@ -75,15 +60,6 @@
<ClInclude Include="general_settings.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="powertoys_events.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="win_hook_event.h">
<Filter>Events</Filter>
</ClInclude>
<ClInclude Include="system_menu_helper.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="restart_elevated.h">
<Filter>Utils</Filter>
</ClInclude>
@ -107,9 +83,6 @@
<Filter Include="svgs">
<UniqueIdentifier>{1123565f-43ba-4eb8-a6f5-2d5f8f3ae6af}</UniqueIdentifier>
</Filter>
<Filter Include="Events">
<UniqueIdentifier>{d409b25e-c0f1-4913-bb32-da4a21339c76}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{74d0b535-16ee-46cc-9adf-dc4668bbcfda}</UniqueIdentifier>
</Filter>

View File

@ -1,160 +0,0 @@
#include "pch.h"
#include "system_menu_helper.h"
#include <interface/powertoy_module_interface.h>
namespace
{
constexpr int KSeparatorPos = 1;
constexpr int KNewItemPos = 2;
unsigned int GenerateItemId()
{
static unsigned int generator = 0x70777479;
return ++generator;
}
}
SystemMenuHelper& SystemMenuHelperInstance()
{
static SystemMenuHelper instance;
return instance;
}
void SystemMenuHelper::SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config)
{
Reset(module);
Configurations[module] = config;
for (auto& [window, modules] : ProcessedModules)
{
// Unregister module. After system menu is opened again, new configuration will be applied.
modules.erase(std::remove(std::begin(modules), std::end(modules), module), std::end(modules));
}
}
void SystemMenuHelper::ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName)
{
for (const auto& item : Configurations[module])
{
if (itemName == item.name && item.checkBox)
{
// Handle check/uncheck action only if specified by module configuration.
for (const auto& [id, data] : IdMappings)
{
if (data.second == itemName)
{
HMENU systemMenu = GetSystemMenu(window, false);
int state = (GetMenuState(systemMenu, id, MF_BYCOMMAND) == MF_CHECKED) ? MF_UNCHECKED : MF_CHECKED;
CheckMenuItem(systemMenu, id, MF_BYCOMMAND | state);
break;
}
}
break;
}
}
}
bool SystemMenuHelper::Customize(PowertoyModuleIface* module, HWND window)
{
auto& modules = ProcessedModules[window];
for (const auto& m : modules)
{
if (module == m)
{
return false;
}
}
AddSeparator(module, window);
for (const auto& info : Configurations[module])
{
AddItem(module, window, info.name, info.enable);
}
modules.push_back(module);
return true;
}
void SystemMenuHelper::Reset(PowertoyModuleIface* module)
{
for (auto& [window, modules] : ProcessedModules)
{
if (HMENU systemMenu{ GetSystemMenu(window, false) })
{
for (auto& [id, data] : IdMappings)
{
if (data.first == module)
{
DeleteMenu(systemMenu, id, MF_BYCOMMAND);
}
}
}
}
}
bool SystemMenuHelper::HasCustomConfig(PowertoyModuleIface* module)
{
return Configurations.find(module) != Configurations.end();
}
bool SystemMenuHelper::AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable)
{
if (HMENU systemMenu{ GetSystemMenu(window, false) })
{
MENUITEMINFO item;
item.cbSize = sizeof(item);
item.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
item.fState = MF_UNCHECKED | MF_DISABLED; // Item is disabled by default.
item.wID = GenerateItemId();
item.dwTypeData = const_cast<WCHAR*>(name.c_str());
item.cch = (UINT)name.size() + 1;
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KNewItemPos, true, &item))
{
IdMappings[item.wID] = { module, name };
if (enable)
{
EnableMenuItem(systemMenu, item.wID, MF_BYCOMMAND | MF_ENABLED);
}
return true;
}
}
return false;
}
bool SystemMenuHelper::AddSeparator(PowertoyModuleIface* module, HWND window)
{
if (HMENU systemMenu{ GetSystemMenu(window, false) })
{
MENUITEMINFO separator;
separator.cbSize = sizeof(separator);
separator.fMask = MIIM_ID | MIIM_FTYPE;
separator.fType = MFT_SEPARATOR;
separator.wID = GenerateItemId();
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KSeparatorPos, true, &separator))
{
IdMappings[separator.wID] = { module, L"separator_dummy_name" };
return true;
}
}
return false;
}
PowertoyModuleIface* SystemMenuHelper::ModuleFromItemId(const int& id)
{
auto it = IdMappings.find(id);
if (it != IdMappings.end())
{
return it->second.first;
}
return nullptr;
}
const std::wstring SystemMenuHelper::ItemNameFromItemId(const int& id)
{
auto itemIt = IdMappings.find(id);
if (itemIt != IdMappings.end())
{
return itemIt->second.second;
}
return std::wstring{};
}

View File

@ -1,43 +0,0 @@
#pragma once
#pragma once
#include <interface/powertoy_system_menu.h>
#include <windows.h>
#include <string>
#include <vector>
#include <unordered_map>
class PowertoyModuleIface;
class SystemMenuHelper : public PowertoySystemMenuIface
{
public:
// PowertoySystemMenuIface
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) override;
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override;
bool Customize(PowertoyModuleIface* module, HWND window);
void Reset(PowertoyModuleIface* module);
bool HasCustomConfig(PowertoyModuleIface* module);
PowertoyModuleIface* ModuleFromItemId(const int& id);
const std::wstring ItemNameFromItemId(const int& id);
private:
bool AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable);
bool AddSeparator(PowertoyModuleIface* module, HWND window);
// Store processed modules per window to avoid handling it multiple times.
std::unordered_map<HWND, std::vector<PowertoyModuleIface*>> ProcessedModules{};
// Keep mappings form item id to the module who created it and item name for faster processing later.
std::unordered_map<int, std::pair<PowertoyModuleIface*, std::wstring>> IdMappings{};
// Store configurations provided by module.
// This will be used to create custom system menu items and to handle updates.
std::unordered_map<PowertoyModuleIface*, std::vector<ItemInfo>> Configurations{};
};
SystemMenuHelper& SystemMenuHelperInstance();

View File

@ -1,90 +0,0 @@
#include "pch.h"
#include "win_hook_event.h"
#include "powertoy_module.h"
#include <mutex>
#include <deque>
#include <thread>
static std::mutex mutex;
static std::deque<WinHookEvent> hook_events;
static std::condition_variable dispatch_cv;
void intercept_system_menu_action(intptr_t);
static void CALLBACK win_hook_event_proc(HWINEVENTHOOK winEventHook,
DWORD event,
HWND window,
LONG object,
LONG child,
DWORD eventThread,
DWORD eventTime)
{
std::unique_lock lock(mutex);
hook_events.push_back({ event,
window,
object,
child,
eventThread,
eventTime });
lock.unlock();
dispatch_cv.notify_one();
}
static bool running = false;
static std::thread dispatch_thread;
static void dispatch_thread_proc()
{
std::unique_lock lock(mutex);
while (running)
{
dispatch_cv.wait(lock, [] { return !running || !hook_events.empty(); });
if (!running)
return;
while (!hook_events.empty())
{
auto event = hook_events.front();
hook_events.pop_front();
lock.unlock();
intptr_t data = reinterpret_cast<intptr_t>(&event);
intercept_system_menu_action(data);
powertoys_events().signal_event(win_hook_event, data);
lock.lock();
}
}
}
static HWINEVENTHOOK hook_handle;
void start_win_hook_event()
{
std::lock_guard lock(mutex);
if (running)
return;
running = true;
dispatch_thread = std::thread(dispatch_thread_proc);
hook_handle = SetWinEventHook(EVENT_MIN, EVENT_MAX, nullptr, win_hook_event_proc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
void stop_win_hook_event()
{
std::unique_lock lock(mutex);
if (!running)
return;
running = false;
UnhookWinEvent(hook_handle);
lock.unlock();
dispatch_cv.notify_one();
dispatch_thread.join();
lock.lock();
hook_events.clear();
hook_events.shrink_to_fit();
}
void intercept_system_menu_action(intptr_t data)
{
WinHookEvent* evt = reinterpret_cast<WinHookEvent*>(data);
if (evt->event == EVENT_SYSTEM_MENUSTART || evt->event == EVENT_OBJECT_INVOKED)
{
powertoys_events().handle_system_menu_action(*evt);
}
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <interface/win_hook_event_data.h>
void start_win_hook_event();
void stop_win_hook_event();

View File

@ -1,7 +1,5 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <interface/lowlevel_keyboard_event_data.h>
#include <interface/win_hook_event_data.h>
#include <common/settings_objects.h>
#include "trace.h"
@ -74,23 +72,6 @@ public:
return MODULE_NAME;
}
// Return array of the names of all events that this powertoy listens for, with
// nullptr as the last element of the array. Nullptr can also be returned for empty
// list.
virtual const wchar_t** get_events() override
{
static const wchar_t* events[] = { nullptr };
// Available events:
// - ll_keyboard
// - win_hook_event
//
// static const wchar_t* events[] = { ll_keyboard,
// win_hook_event,
// nullptr };
return events;
}
// Return JSON with the configuration options.
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
@ -228,34 +209,6 @@ public:
{
return m_enabled;
}
// Handle incoming event, data is event-specific
virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
{
if (wcscmp(name, ll_keyboard) == 0)
{
auto& event = *(reinterpret_cast<LowlevelKeyboardEvent*>(data));
// Return 1 if the keypress is to be suppressed (not forwarded to Windows),
// otherwise return 0.
return 0;
}
else if (wcscmp(name, win_hook_event) == 0)
{
auto& event = *(reinterpret_cast<WinHookEvent*>(data));
// Return value is ignored
return 0;
}
return 0;
}
// This methods are part of an experimental features not fully supported yet
virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override
{
}
virtual void signal_system_menu_action(const wchar_t* name) override
{
}
};
// Load the settings file.