Files
PowerToys/src/modules/fancyzones/dll/dllmain.cpp

338 lines
10 KiB
C++
Raw Normal View History

#include "pch.h"
#include <common/settings_objects.h>
#include <common/common.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>
#include <lib/trace.h>
#include <lib/Settings.h>
#include <lib/FancyZones.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
class FancyZonesModule : public PowertoyModuleIface
{
public:
// Return the display name of the powertoy, this will be cached
virtual PCWSTR get_name() override
{
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 retured 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
{
return m_settings->GetConfig(buffer, buffer_size);
}
// Passes JSON with the configuration settings for the powertoy.
// This is called when the user hits Save on the settings page.
virtual void set_config(PCWSTR config) override
{
m_settings->SetConfig(config);
}
// Signal from the Settings editor to call a custom action.
// This can be used to spawn more complex editors.
virtual void call_custom_action(const wchar_t* action) override
{
m_settings->CallCustomAction(action);
}
// Enable the powertoy
virtual void enable()
{
if (!m_app)
{
Trace::FancyZones::EnableFancyZones(true);
Migrate FancyZones data persisting from Registry to JSON file (#1194) * Migrate FancyZones data persisting from Registry to JSON file * Address PR comment: Remove redundant check * Addres PR comment: Remove unused Dpi and add CmdArgs enum * Address PR comment: Make methods const and inline * Address PR comments: Expose GenerateUniqueId function and use const ref instead of passing wstring by value * Address PR comment: Use lamdba as callback * Address PR comment: Move GenerateUniqueId to ZoneWindowUtils namespace * Address PR comment: Use regular comparison instead of std::wstring::compare * Address PR comment: Use std::wstring_view for tmp file paths * Address PR comment: Use scoped lock when accessing member data * Address PR comment: Remove typedefs to increase code readability * Address PR comment: removed nullptr checks with corresponding tests * Address PR comment: Move ZoneSet object instead of copying * Address PR comment: Make FancyZonesData instance const where possible * Remove unnecessary gutter variable during calculating zone coordinates * Remove uneeded subclass * Avoid unnecessary copying and reserve space for vector if possible * Save FancyZones data after exiting editor * App zone history (#18) * added window and zone set ids to app zone history * Rename JSON file * Remove AppZoneHistory migration * Move parsing of ZoneWindow independent temp files outside of it * Unit tests update (#19) * check device existence in map * updated ZoneSet tests * updated JsonHelpers tests * Use single zone count information * Remove uneeded tests * Remove one more test * Remove uneeded line * Address PR comments - Missing whitespace * Update zoneset data for new virtual desktops (#21) * update active zone set with actual data * Introduce Blank zone set (used to indicate that no layout applied yet). Move parsing completely outside of ZoneWindow. * Fix unit tests to match modifications in implementation * Fix applying layouts on startup (second monitor) Co-authored-by: vldmr11080 <57061786+vldmr11080@users.noreply.github.com> Co-authored-by: Seraphima <zykovas91@gmail.com>
2020-02-10 14:59:51 +01:00
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings);
s_llKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
if (!s_llKeyboardHook)
{
MessageBoxW(NULL, L"Cannot install keyboard listener.", L"PowerToys - FancyZones", MB_OK | MB_ICONERROR);
}
std::array<DWORD, 6> events_to_subscribe = {
EVENT_SYSTEM_MOVESIZESTART,
EVENT_SYSTEM_MOVESIZEEND,
EVENT_OBJECT_NAMECHANGE,
EVENT_OBJECT_UNCLOAKED,
EVENT_OBJECT_SHOW,
EVENT_OBJECT_CREATE
};
for (const auto event : events_to_subscribe)
{
auto hook = SetWinEventHook(event, event, nullptr, WinHookProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
if (hook)
{
m_staticWinEventHooks.emplace_back(hook);
}
else
{
MessageBoxW(NULL, L"Cannot install Windows event listener.", L"PowerToys - FancyZones", MB_OK | MB_ICONERROR);
}
}
if (m_app)
{
m_app->Run();
}
}
}
// Disable the powertoy
virtual void disable()
{
Disable(true);
}
// Returns if the powertoy is enabled
virtual bool is_enabled() override
{
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
{
Disable(false);
delete this;
}
FancyZonesModule()
{
app_name = GET_RESOURCE_STRING(IDS_FANCYZONES);
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name());
Migrate FancyZones data persisting from Registry to JSON file (#1194) * Migrate FancyZones data persisting from Registry to JSON file * Address PR comment: Remove redundant check * Addres PR comment: Remove unused Dpi and add CmdArgs enum * Address PR comment: Make methods const and inline * Address PR comments: Expose GenerateUniqueId function and use const ref instead of passing wstring by value * Address PR comment: Use lamdba as callback * Address PR comment: Move GenerateUniqueId to ZoneWindowUtils namespace * Address PR comment: Use regular comparison instead of std::wstring::compare * Address PR comment: Use std::wstring_view for tmp file paths * Address PR comment: Use scoped lock when accessing member data * Address PR comment: Remove typedefs to increase code readability * Address PR comment: removed nullptr checks with corresponding tests * Address PR comment: Move ZoneSet object instead of copying * Address PR comment: Make FancyZonesData instance const where possible * Remove unnecessary gutter variable during calculating zone coordinates * Remove uneeded subclass * Avoid unnecessary copying and reserve space for vector if possible * Save FancyZones data after exiting editor * App zone history (#18) * added window and zone set ids to app zone history * Rename JSON file * Remove AppZoneHistory migration * Move parsing of ZoneWindow independent temp files outside of it * Unit tests update (#19) * check device existence in map * updated ZoneSet tests * updated JsonHelpers tests * Use single zone count information * Remove uneeded tests * Remove one more test * Remove uneeded line * Address PR comments - Missing whitespace * Update zoneset data for new virtual desktops (#21) * update active zone set with actual data * Introduce Blank zone set (used to indicate that no layout applied yet). Move parsing completely outside of ZoneWindow. * Fix unit tests to match modifications in implementation * Fix applying layouts on startup (second monitor) Co-authored-by: vldmr11080 <57061786+vldmr11080@users.noreply.github.com> Co-authored-by: Seraphima <zykovas91@gmail.com>
2020-02-10 14:59:51 +01:00
JSONHelpers::FancyZonesDataInstance().LoadFancyZonesData();
s_instance = this;
}
private:
void Disable(bool const traceEvent)
{
if (m_app)
{
if (traceEvent)
{
Trace::FancyZones::EnableFancyZones(false);
}
m_app->Destroy();
m_app = nullptr;
m_settings->ResetCallback();
if (s_llKeyboardHook)
{
if (UnhookWindowsHookEx(s_llKeyboardHook))
{
s_llKeyboardHook = nullptr;
}
}
m_staticWinEventHooks.erase(std::remove_if(begin(m_staticWinEventHooks),
end(m_staticWinEventHooks),
[](const HWINEVENTHOOK hook) {
return UnhookWinEvent(hook);
}),
end(m_staticWinEventHooks));
if (m_objectLocationWinEventHook)
{
if (UnhookWinEvent(m_objectLocationWinEventHook))
{
m_objectLocationWinEventHook = nullptr;
}
}
}
}
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept;
void HandleWinHookEvent(WinHookEvent* data) noexcept;
void MoveSizeStart(HWND window, POINT const& ptScreen) noexcept;
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
void MoveSizeUpdate(POINT const& ptScreen) noexcept;
winrt::com_ptr<IFancyZones> m_app;
winrt::com_ptr<IFancyZonesSettings> m_settings;
std::wstring app_name;
static inline FancyZonesModule* s_instance;
static inline HHOOK s_llKeyboardHook;
std::vector<HWINEVENTHOOK> m_staticWinEventHooks;
HWINEVENTHOOK m_objectLocationWinEventHook;
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LowlevelKeyboardEvent event;
if (nCode == HC_ACTION)
{
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (s_instance)
{
return s_instance->HandleKeyboardHookEvent(&event);
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
static void CALLBACK WinHookProc(HWINEVENTHOOK winEventHook,
DWORD event,
HWND window,
LONG object,
LONG child,
DWORD eventThread,
DWORD eventTime)
{
WinHookEvent data{ event, window, object, child, eventThread, eventTime };
if (s_instance)
{
s_instance->HandleWinHookEvent(&data);
}
}
};
intptr_t FancyZonesModule::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
{
if (data->wParam == WM_KEYDOWN)
{
return m_app.as<IFancyZonesCallback>()->OnKeyDown(data->lParam) ? 1 : 0;
}
return 0;
}
void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
{
POINT ptScreen;
GetPhysicalCursorPos(&ptScreen);
switch (data->event)
{
case EVENT_SYSTEM_MOVESIZESTART:
{
MoveSizeStart(data->hwnd, ptScreen);
if (!m_objectLocationWinEventHook)
{
m_objectLocationWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,
EVENT_OBJECT_LOCATIONCHANGE,
nullptr,
WinHookProc,
0,
0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
}
break;
case EVENT_SYSTEM_MOVESIZEEND:
{
if (UnhookWinEvent(m_objectLocationWinEventHook))
{
m_objectLocationWinEventHook = nullptr;
}
MoveSizeEnd(data->hwnd, ptScreen);
}
break;
case EVENT_OBJECT_LOCATIONCHANGE:
{
if (m_app.as<IFancyZonesCallback>()->InMoveSize())
{
MoveSizeUpdate(ptScreen);
}
}
break;
case EVENT_OBJECT_NAMECHANGE:
{
// The accessibility name of the desktop window changes whenever the user
// switches virtual desktops.
if (data->hwnd == GetDesktopWindow())
{
Trace::VirtualDesktopChanged();
m_app.as<IFancyZonesCallback>()->VirtualDesktopChanged();
}
}
break;
case EVENT_OBJECT_UNCLOAKED:
case EVENT_OBJECT_SHOW:
case EVENT_OBJECT_CREATE:
{
if (data->idObject == OBJID_WINDOW)
{
m_app.as<IFancyZonesCallback>()->WindowCreated(data->hwnd);
}
}
break;
default:
break;
}
}
void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcept
{
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
{
m_app.as<IFancyZonesCallback>()->MoveSizeStart(window, monitor, ptScreen);
}
}
void FancyZonesModule::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
{
m_app.as<IFancyZonesCallback>()->MoveSizeEnd(window, ptScreen);
}
void FancyZonesModule::MoveSizeUpdate(POINT const& ptScreen) noexcept
{
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
{
m_app.as<IFancyZonesCallback>()->MoveSizeUpdate(monitor, ptScreen);
}
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new FancyZonesModule();
}