2019-09-05 00:26:26 +08:00
|
|
|
#include "pch.h"
|
2020-05-04 22:29:48 +08:00
|
|
|
|
|
|
|
#include <common/dpi_aware.h>
|
|
|
|
#include <common/on_thread_executor.h>
|
|
|
|
#include <common/window_helpers.h>
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2019-12-17 16:21:46 +08:00
|
|
|
#include "FancyZones.h"
|
|
|
|
#include "lib/Settings.h"
|
|
|
|
#include "lib/ZoneWindow.h"
|
2020-02-10 21:59:51 +08:00
|
|
|
#include "lib/JsonHelpers.h"
|
|
|
|
#include "lib/ZoneSet.h"
|
2020-04-30 18:16:08 +08:00
|
|
|
#include "lib/WindowMoveHandler.h"
|
2020-05-04 22:29:48 +08:00
|
|
|
#include "lib/FancyZonesWinHookEventIDs.h"
|
|
|
|
#include "lib/util.h"
|
2019-12-17 16:21:46 +08:00
|
|
|
#include "trace.h"
|
2020-04-22 01:57:21 +08:00
|
|
|
#include "VirtualDesktopUtils.h"
|
2019-12-17 16:21:46 +08:00
|
|
|
|
2020-05-04 22:29:48 +08:00
|
|
|
#include <interface/win_hook_event_data.h>
|
2020-02-10 21:59:51 +08:00
|
|
|
|
|
|
|
enum class DisplayChangeType
|
|
|
|
{
|
|
|
|
WorkArea,
|
|
|
|
DisplayChange,
|
|
|
|
VirtualDesktop,
|
|
|
|
Editor,
|
|
|
|
Initialization
|
|
|
|
};
|
2019-12-12 17:10:55 +08:00
|
|
|
|
|
|
|
namespace std
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
template<>
|
|
|
|
struct hash<GUID>
|
2019-12-12 17:10:55 +08:00
|
|
|
{
|
|
|
|
size_t operator()(const GUID& Value) const
|
|
|
|
{
|
|
|
|
RPC_STATUS status = RPC_S_OK;
|
|
|
|
return ::UuidHash(&const_cast<GUID&>(Value), &status);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
|
|
|
{
|
|
|
|
public:
|
2020-02-10 21:59:51 +08:00
|
|
|
FancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept :
|
|
|
|
m_hinstance(hinstance),
|
2020-04-30 18:16:08 +08:00
|
|
|
m_settings(settings),
|
|
|
|
m_windowMoveHandler(settings)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
m_settings->SetCallback(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// IFancyZones
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
Run() noexcept;
|
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
Destroy() noexcept;
|
2019-09-05 00:26:26 +08:00
|
|
|
|
|
|
|
// IFancyZonesCallback
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(bool)
|
|
|
|
InMoveSize() noexcept
|
2020-02-10 21:59:51 +08:00
|
|
|
{
|
|
|
|
std::shared_lock readLock(m_lock);
|
2020-04-30 18:16:08 +08:00
|
|
|
return m_windowMoveHandler.InMoveSize();
|
2020-02-10 21:59:51 +08:00
|
|
|
}
|
2020-05-05 02:10:23 +08:00
|
|
|
|
|
|
|
void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept
|
2020-04-30 18:16:08 +08:00
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_zoneWindowMap);
|
|
|
|
}
|
2020-05-05 02:10:23 +08:00
|
|
|
|
|
|
|
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept
|
2020-04-30 18:16:08 +08:00
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
2020-05-04 22:29:48 +08:00
|
|
|
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap);
|
2020-04-30 18:16:08 +08:00
|
|
|
}
|
2020-05-05 02:10:23 +08:00
|
|
|
|
|
|
|
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
2020-04-30 18:16:08 +08:00
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
2020-05-04 22:29:48 +08:00
|
|
|
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_zoneWindowMap);
|
|
|
|
}
|
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
HandleWinHookEvent(const WinHookEvent* data) noexcept
|
|
|
|
{
|
|
|
|
const auto wparam = reinterpret_cast<WPARAM>(data->hwnd);
|
|
|
|
const LONG lparam = 0;
|
|
|
|
std::shared_lock readLock(m_lock);
|
|
|
|
switch (data->event)
|
|
|
|
{
|
|
|
|
case EVENT_SYSTEM_MOVESIZESTART:
|
|
|
|
PostMessageW(m_window, WM_PRIV_MOVESIZESTART, wparam, lparam);
|
|
|
|
break;
|
|
|
|
case EVENT_SYSTEM_MOVESIZEEND:
|
|
|
|
PostMessageW(m_window, WM_PRIV_MOVESIZEEND, wparam, lparam);
|
|
|
|
break;
|
|
|
|
case EVENT_OBJECT_LOCATIONCHANGE:
|
|
|
|
PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, wparam, lparam);
|
|
|
|
break;
|
|
|
|
case EVENT_OBJECT_NAMECHANGE:
|
|
|
|
PostMessageW(m_window, WM_PRIV_NAMECHANGE, wparam, lparam);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EVENT_OBJECT_UNCLOAKED:
|
|
|
|
case EVENT_OBJECT_SHOW:
|
|
|
|
case EVENT_OBJECT_CREATE:
|
|
|
|
if (data->idObject == OBJID_WINDOW)
|
|
|
|
{
|
|
|
|
PostMessageW(m_window, WM_PRIV_WINDOWCREATED, wparam, lparam);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-04-30 18:16:08 +08:00
|
|
|
}
|
2020-05-04 22:29:48 +08:00
|
|
|
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
VirtualDesktopChanged() noexcept;
|
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
VirtualDesktopInitialize() noexcept;
|
|
|
|
IFACEMETHODIMP_(bool)
|
|
|
|
OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept;
|
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
ToggleEditor() noexcept;
|
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
SettingsChanged() noexcept;
|
2020-05-05 02:10:23 +08:00
|
|
|
|
|
|
|
void WindowCreated(HWND window) noexcept;
|
2019-09-05 00:26:26 +08:00
|
|
|
|
|
|
|
// IZoneWindowHost
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
MoveWindowsOnActiveZoneSetChange() noexcept;
|
|
|
|
IFACEMETHODIMP_(COLORREF)
|
2020-03-25 22:38:44 +08:00
|
|
|
GetZoneColor() noexcept
|
|
|
|
{
|
|
|
|
// Skip the leading # and convert to long
|
|
|
|
const auto color = m_settings->GetSettings()->zoneColor;
|
|
|
|
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
|
|
|
const auto nR = (tmp & 0xFF0000) >> 16;
|
|
|
|
const auto nG = (tmp & 0xFF00) >> 8;
|
|
|
|
const auto nB = (tmp & 0xFF);
|
|
|
|
return RGB(nR, nG, nB);
|
|
|
|
}
|
|
|
|
IFACEMETHODIMP_(COLORREF)
|
|
|
|
GetZoneBorderColor() noexcept
|
|
|
|
{
|
|
|
|
// Skip the leading # and convert to long
|
|
|
|
const auto color = m_settings->GetSettings()->zoneBorderColor;
|
|
|
|
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
|
|
|
const auto nR = (tmp & 0xFF0000) >> 16;
|
|
|
|
const auto nG = (tmp & 0xFF00) >> 8;
|
|
|
|
const auto nB = (tmp & 0xFF);
|
|
|
|
return RGB(nR, nG, nB);
|
|
|
|
}
|
|
|
|
IFACEMETHODIMP_(COLORREF)
|
2020-02-17 23:28:49 +08:00
|
|
|
GetZoneHighlightColor() noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
// Skip the leading # and convert to long
|
2020-03-13 17:55:15 +08:00
|
|
|
const auto color = m_settings->GetSettings()->zoneHightlightColor;
|
2019-09-05 00:26:26 +08:00
|
|
|
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
|
|
|
const auto nR = (tmp & 0xFF0000) >> 16;
|
|
|
|
const auto nG = (tmp & 0xFF00) >> 8;
|
|
|
|
const auto nB = (tmp & 0xFF);
|
|
|
|
return RGB(nR, nG, nB);
|
|
|
|
}
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(IZoneWindow*)
|
|
|
|
GetParentZoneWindow(HMONITOR monitor) noexcept
|
2019-12-06 22:09:27 +08:00
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
//NOTE: as public method it's unsafe without lock, but it's called from AddZoneWindow through making ZoneWindow that causes deadlock
|
|
|
|
//TODO: needs refactoring
|
|
|
|
auto it = m_zoneWindowMap.find(monitor);
|
|
|
|
if (it != m_zoneWindowMap.end())
|
|
|
|
{
|
|
|
|
return it->second.get();
|
2019-12-06 22:09:27 +08:00
|
|
|
}
|
2020-02-10 21:59:51 +08:00
|
|
|
return nullptr;
|
2019-12-06 22:09:27 +08:00
|
|
|
}
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(int)
|
|
|
|
GetZoneHighlightOpacity() noexcept
|
2020-01-07 01:59:18 +08:00
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
return m_settings->GetSettings()->zoneHighlightOpacity;
|
2020-01-07 01:59:18 +08:00
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2020-03-25 22:38:44 +08:00
|
|
|
IFACEMETHODIMP_(bool)
|
|
|
|
isMakeDraggedWindowTransparentActive() noexcept
|
|
|
|
{
|
|
|
|
return m_settings->GetSettings()->makeDraggedWindowTransparent;
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
|
|
|
void OnDisplayChange(DisplayChangeType changeType) noexcept;
|
|
|
|
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
|
2020-03-10 16:59:34 +08:00
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
protected:
|
|
|
|
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct require_read_lock
|
|
|
|
{
|
|
|
|
template<typename T>
|
2020-02-10 21:59:51 +08:00
|
|
|
require_read_lock(const std::shared_lock<T>& lock)
|
|
|
|
{
|
|
|
|
lock;
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
|
|
|
|
template<typename T>
|
2020-02-10 21:59:51 +08:00
|
|
|
require_read_lock(const std::unique_lock<T>& lock)
|
|
|
|
{
|
|
|
|
lock;
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct require_write_lock
|
|
|
|
{
|
|
|
|
template<typename T>
|
2020-02-10 21:59:51 +08:00
|
|
|
require_write_lock(const std::unique_lock<T>& lock)
|
|
|
|
{
|
|
|
|
lock;
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void UpdateZoneWindows() noexcept;
|
|
|
|
void MoveWindowsOnDisplayChange() noexcept;
|
|
|
|
void CycleActiveZoneSet(DWORD vkCode) noexcept;
|
2020-02-06 20:12:59 +08:00
|
|
|
bool OnSnapHotkey(DWORD vkCode) noexcept;
|
2020-05-04 22:29:48 +08:00
|
|
|
|
2020-05-01 22:13:16 +08:00
|
|
|
void RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept;
|
2020-02-18 18:55:08 +08:00
|
|
|
void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
|
|
|
|
bool IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
|
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
void OnEditorExitEvent() noexcept;
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2020-03-25 01:50:26 +08:00
|
|
|
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
|
|
|
|
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
const HINSTANCE m_hinstance{};
|
|
|
|
|
|
|
|
mutable std::shared_mutex m_lock;
|
|
|
|
HWND m_window{};
|
2020-04-30 18:16:08 +08:00
|
|
|
WindowMoveHandler m_windowMoveHandler;
|
2020-05-04 22:29:48 +08:00
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
2020-02-10 21:59:51 +08:00
|
|
|
winrt::com_ptr<IFancyZonesSettings> m_settings{};
|
2019-11-19 07:29:42 +08:00
|
|
|
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
|
2020-02-18 18:55:08 +08:00
|
|
|
std::unordered_map<GUID, std::vector<HMONITOR>> m_processedWorkAreas; // Work area is defined by monitor and virtual desktop id.
|
2019-11-19 07:29:42 +08:00
|
|
|
wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on
|
2019-12-12 17:10:55 +08:00
|
|
|
wil::unique_handle m_terminateVirtualDesktopTrackerEvent;
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2019-11-08 02:56:32 +08:00
|
|
|
OnThreadExecutor m_dpiUnawareThread;
|
2019-12-12 17:10:55 +08:00
|
|
|
OnThreadExecutor m_virtualDesktopTrackerThread;
|
2019-11-08 02:56:32 +08:00
|
|
|
|
2020-05-05 02:10:23 +08:00
|
|
|
static UINT WM_PRIV_VD_INIT; // Scheduled when FancyZones is initialized
|
|
|
|
static UINT WM_PRIV_VD_SWITCH; // Scheduled when virtual desktop switch occurs
|
|
|
|
static UINT WM_PRIV_VD_UPDATE; // Scheduled on virtual desktops update (creation/deletion)
|
|
|
|
static UINT WM_PRIV_EDITOR; // Scheduled when the editor exits
|
|
|
|
|
|
|
|
static UINT WM_PRIV_LOWLEVELKB; // Scheduled when we receive a key down press
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2019-11-19 07:29:42 +08:00
|
|
|
// Did we terminate the editor or was it closed cleanly?
|
2019-09-05 00:26:26 +08:00
|
|
|
enum class EditorExitKind : byte
|
|
|
|
{
|
|
|
|
Exit,
|
|
|
|
Terminate
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-05-01 22:13:16 +08:00
|
|
|
UINT FancyZones::WM_PRIV_VD_INIT = RegisterWindowMessage(L"{469818a8-00fa-4069-b867-a1da484fcd9a}");
|
|
|
|
UINT FancyZones::WM_PRIV_VD_SWITCH = RegisterWindowMessage(L"{128c2cb0-6bdf-493e-abbe-f8705e04aa95}");
|
|
|
|
UINT FancyZones::WM_PRIV_VD_UPDATE = RegisterWindowMessage(L"{b8b72b46-f42f-4c26-9e20-29336cf2f22e}");
|
2019-09-05 00:26:26 +08:00
|
|
|
UINT FancyZones::WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}");
|
2020-05-05 02:10:23 +08:00
|
|
|
UINT FancyZones::WM_PRIV_LOWLEVELKB = RegisterWindowMessage(L"{763c03a3-03d9-4cde-8d71-f0358b0b4b52}");
|
2019-09-05 00:26:26 +08:00
|
|
|
|
|
|
|
// IFancyZones
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::Run() noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
|
|
|
|
WNDCLASSEXW wcex{};
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.lpfnWndProc = s_WndProc;
|
|
|
|
wcex.hInstance = m_hinstance;
|
|
|
|
wcex.lpszClassName = L"SuperFancyZones";
|
|
|
|
RegisterClassExW(&wcex);
|
|
|
|
|
|
|
|
BufferedPaintInit();
|
|
|
|
|
|
|
|
m_window = CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones", L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, m_hinstance, this);
|
2020-02-10 21:59:51 +08:00
|
|
|
if (!m_window)
|
|
|
|
return;
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2020-03-13 17:55:15 +08:00
|
|
|
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
|
2019-11-19 07:29:42 +08:00
|
|
|
|
2019-12-24 22:47:28 +08:00
|
|
|
VirtualDesktopInitialize();
|
2019-11-08 02:56:32 +08:00
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [] {
|
|
|
|
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
|
|
|
|
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
|
|
|
|
} })
|
|
|
|
.wait();
|
2019-12-12 17:10:55 +08:00
|
|
|
|
2020-05-01 22:13:16 +08:00
|
|
|
m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
2020-05-04 22:29:48 +08:00
|
|
|
m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] { VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } });
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// IFancyZones
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::Destroy() noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
2019-11-08 03:05:12 +08:00
|
|
|
m_zoneWindowMap.clear();
|
2019-09-05 00:26:26 +08:00
|
|
|
BufferedPaintUnInit();
|
|
|
|
if (m_window)
|
|
|
|
{
|
|
|
|
DestroyWindow(m_window);
|
|
|
|
m_window = nullptr;
|
|
|
|
}
|
2020-02-10 21:59:51 +08:00
|
|
|
if (m_terminateVirtualDesktopTrackerEvent)
|
|
|
|
{
|
2019-12-12 17:10:55 +08:00
|
|
|
SetEvent(m_terminateVirtualDesktopTrackerEvent.get());
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// IFancyZonesCallback
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::VirtualDesktopChanged() noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-05-04 22:29:48 +08:00
|
|
|
// VirtualDesktopChanged is called from a reentrant WinHookProc function, therefore we must postpone the actual logic
|
|
|
|
// until we're in FancyZones::WndProc, which is not reentrant.
|
2020-02-10 21:59:51 +08:00
|
|
|
std::shared_lock readLock(m_lock);
|
2020-05-01 22:13:16 +08:00
|
|
|
PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0);
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
2019-12-24 22:47:28 +08:00
|
|
|
// IFancyZonesCallback
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::VirtualDesktopInitialize() noexcept
|
2019-12-24 22:47:28 +08:00
|
|
|
{
|
2020-05-01 22:13:16 +08:00
|
|
|
PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0);
|
2019-12-24 22:47:28 +08:00
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
// IFancyZonesCallback
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::WindowCreated(HWND window) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-04-22 01:57:21 +08:00
|
|
|
std::shared_lock readLock(m_lock);
|
2020-04-30 18:16:08 +08:00
|
|
|
if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-10 16:59:34 +08:00
|
|
|
for (const auto& [monitor, zoneWindow] : m_zoneWindowMap)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-04-22 01:57:21 +08:00
|
|
|
// WindowCreated is also invoked when a virtual desktop switch occurs, we need a way
|
|
|
|
// to figure out when that happens to avoid moving windows that should not be moved.
|
|
|
|
GUID windowDesktopId{};
|
|
|
|
GUID zoneWindowDesktopId{};
|
|
|
|
if (VirtualDesktopUtils::GetWindowDesktopId(window, &windowDesktopId) &&
|
|
|
|
VirtualDesktopUtils::GetZoneWindowDesktopId(zoneWindow.get(), &zoneWindowDesktopId) &&
|
|
|
|
(windowDesktopId != zoneWindowDesktopId))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-03-10 16:59:34 +08:00
|
|
|
const auto activeZoneSet = zoneWindow->ActiveZoneSet();
|
|
|
|
if (activeZoneSet)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-10 16:59:34 +08:00
|
|
|
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
2020-02-10 21:59:51 +08:00
|
|
|
|
2020-03-10 16:59:34 +08:00
|
|
|
wil::unique_cotaskmem_string guidString;
|
2020-04-22 01:57:21 +08:00
|
|
|
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString)))
|
2020-03-10 16:59:34 +08:00
|
|
|
{
|
|
|
|
int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindow->UniqueId(), guidString.get());
|
|
|
|
if (zoneIndex != -1)
|
2020-02-10 21:59:51 +08:00
|
|
|
{
|
2020-04-30 18:16:08 +08:00
|
|
|
m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, monitor, zoneIndex, m_zoneWindowMap);
|
2020-03-10 16:59:34 +08:00
|
|
|
break;
|
2020-02-10 21:59:51 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IFancyZonesCallback
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(bool)
|
|
|
|
FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
// Return true to swallow the keyboard event
|
|
|
|
bool const shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
|
2020-05-05 15:21:55 +08:00
|
|
|
bool const win = GetAsyncKeyState(VK_LWIN) & 0x8000 || GetAsyncKeyState(VK_RWIN) & 0x8000;
|
2019-09-05 00:26:26 +08:00
|
|
|
if (win && !shift)
|
|
|
|
{
|
|
|
|
bool const ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
|
|
|
|
if (ctrl)
|
|
|
|
{
|
2020-03-26 01:32:33 +08:00
|
|
|
// Temporarily disable Win+Ctrl+Number functionality
|
|
|
|
//if ((info->vkCode >= '0') && (info->vkCode <= '9'))
|
|
|
|
//{
|
|
|
|
// // Win+Ctrl+Number will cycle through ZoneSets
|
|
|
|
// Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
|
|
|
|
// CycleActiveZoneSet(info->vkCode);
|
|
|
|
// return true;
|
|
|
|
//}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
else if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT))
|
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
if (m_settings->GetSettings()->overrideSnapHotkeys)
|
2019-11-19 07:29:42 +08:00
|
|
|
{
|
|
|
|
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
|
2020-05-05 02:10:23 +08:00
|
|
|
// Win+Left, Win+Right will cycle through Zones in the active ZoneSet when WM_PRIV_LOWLEVELKB's handled
|
|
|
|
PostMessageW(m_window, WM_PRIV_LOWLEVELKB, 0, info->vkCode);
|
|
|
|
return true;
|
2019-11-19 07:29:42 +08:00
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-26 01:32:33 +08:00
|
|
|
// Temporarily disable Win+Ctrl+Number functionality
|
|
|
|
//else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
|
|
|
|
//{
|
|
|
|
// // This allows you to cycle through ZoneSets while dragging a window
|
|
|
|
// Trace::FancyZones::OnKeyDown(info->vkCode, win, false /*control*/, true /*inMoveSize*/);
|
|
|
|
// CycleActiveZoneSet(info->vkCode);
|
|
|
|
// return false;
|
|
|
|
//}
|
2020-04-30 18:16:08 +08:00
|
|
|
|
|
|
|
if (m_windowMoveHandler.IsDragEnabled() && shift)
|
2020-03-05 15:01:58 +08:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// IFancyZonesCallback
|
|
|
|
void FancyZones::ToggleEditor() noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::shared_lock readLock(m_lock);
|
|
|
|
if (m_terminateEditorEvent)
|
|
|
|
{
|
|
|
|
SetEvent(m_terminateEditorEvent.get());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
|
|
|
|
}
|
|
|
|
|
2019-09-28 22:29:29 +08:00
|
|
|
HMONITOR monitor{};
|
2019-11-08 02:56:32 +08:00
|
|
|
HWND foregroundWindow{};
|
2019-09-28 22:29:29 +08:00
|
|
|
|
2020-03-13 17:55:15 +08:00
|
|
|
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
2019-11-08 02:56:32 +08:00
|
|
|
POINT currentCursorPos{};
|
|
|
|
if (use_cursorpos_editor_startupscreen)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2019-09-28 22:29:29 +08:00
|
|
|
GetCursorPos(¤tCursorPos);
|
|
|
|
monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-08 02:56:32 +08:00
|
|
|
foregroundWindow = GetForegroundWindow();
|
2019-09-28 22:29:29 +08:00
|
|
|
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!monitor)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_lock readLock(m_lock);
|
|
|
|
auto iter = m_zoneWindowMap.find(monitor);
|
|
|
|
if (iter == m_zoneWindowMap.end())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MONITORINFOEX mi;
|
|
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
|
|
|
GetMonitorInfo(monitor, &mi);
|
|
|
|
} })
|
|
|
|
.wait();
|
2019-11-08 02:56:32 +08:00
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
auto zoneWindow = iter->second;
|
|
|
|
|
|
|
|
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
|
|
|
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
|
|
|
|
2019-11-08 02:56:32 +08:00
|
|
|
// Do not scale window params by the dpi, that will be done in the editor - see LayoutModel.Apply
|
2020-04-07 17:03:57 +08:00
|
|
|
const auto taskbar_x_offset = mi.rcWork.left - mi.rcMonitor.left;
|
|
|
|
const auto taskbar_y_offset = mi.rcWork.top - mi.rcMonitor.top;
|
2019-11-08 02:56:32 +08:00
|
|
|
const auto x = mi.rcMonitor.left + taskbar_x_offset;
|
|
|
|
const auto y = mi.rcMonitor.top + taskbar_y_offset;
|
|
|
|
const auto width = mi.rcWork.right - mi.rcWork.left;
|
|
|
|
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
2020-02-10 21:59:51 +08:00
|
|
|
const std::wstring editorLocation =
|
2019-09-28 22:29:29 +08:00
|
|
|
std::to_wstring(x) + L"_" +
|
|
|
|
std::to_wstring(y) + L"_" +
|
2019-11-08 02:56:32 +08:00
|
|
|
std::to_wstring(width) + L"_" +
|
|
|
|
std::to_wstring(height);
|
2019-09-28 22:29:29 +08:00
|
|
|
|
2020-02-17 23:28:49 +08:00
|
|
|
const auto deviceInfo = fancyZonesData.FindDeviceInfo(zoneWindow->UniqueId());
|
|
|
|
if (!deviceInfo.has_value())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-02-10 21:59:51 +08:00
|
|
|
|
2020-02-17 23:28:49 +08:00
|
|
|
JSONHelpers::DeviceInfoJSON deviceInfoJson{ zoneWindow->UniqueId(), *deviceInfo };
|
2020-02-10 21:59:51 +08:00
|
|
|
fancyZonesData.SerializeDeviceInfoToTmpFile(deviceInfoJson, ZoneWindowUtils::GetActiveZoneSetTmpPath());
|
2019-12-06 22:09:27 +08:00
|
|
|
|
2019-09-28 22:29:29 +08:00
|
|
|
const std::wstring params =
|
2020-02-10 21:59:51 +08:00
|
|
|
/*1*/ std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
|
|
|
|
/*2*/ editorLocation + L" " +
|
|
|
|
/*3*/ zoneWindow->WorkAreaKey() + L" " +
|
2020-03-18 01:12:32 +08:00
|
|
|
/*4*/ L"\"" + ZoneWindowUtils::GetActiveZoneSetTmpPath() + L"\" " +
|
|
|
|
/*5*/ L"\"" + ZoneWindowUtils::GetAppliedZoneSetTmpPath() + L"\" " +
|
|
|
|
/*6*/ L"\"" + ZoneWindowUtils::GetCustomZoneSetsTmpPath() + L"\"";
|
2019-09-28 22:29:29 +08:00
|
|
|
|
|
|
|
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
|
|
|
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
|
|
|
sei.lpFile = L"modules\\FancyZonesEditor.exe";
|
|
|
|
sei.lpParameters = params.c_str();
|
|
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
ShellExecuteEx(&sei);
|
2020-03-26 18:54:12 +08:00
|
|
|
Trace::FancyZones::EditorLaunched(1);
|
2019-09-28 22:29:29 +08:00
|
|
|
|
|
|
|
// Launch the editor on a background thread
|
|
|
|
// Wait for the editor's process to exit
|
|
|
|
// Post back to the main thread to update
|
2020-02-10 21:59:51 +08:00
|
|
|
std::thread waitForEditorThread([window = m_window, processHandle = sei.hProcess, terminateEditorEvent = m_terminateEditorEvent.get()]() {
|
2019-09-28 22:29:29 +08:00
|
|
|
HANDLE waitEvents[2] = { processHandle, terminateEditorEvent };
|
|
|
|
auto result = WaitForMultipleObjects(2, waitEvents, false, INFINITE);
|
|
|
|
if (result == WAIT_OBJECT_0 + 0)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2019-09-28 22:29:29 +08:00
|
|
|
// Editor exited
|
|
|
|
// Update any changes it may have made
|
|
|
|
PostMessage(window, WM_PRIV_EDITOR, 0, static_cast<LPARAM>(EditorExitKind::Exit));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
2019-09-28 22:29:29 +08:00
|
|
|
else if (result == WAIT_OBJECT_0 + 1)
|
|
|
|
{
|
|
|
|
// User hit Win+~ while editor is already running
|
|
|
|
// Shut it down
|
|
|
|
TerminateProcess(processHandle, 2);
|
|
|
|
PostMessage(window, WM_PRIV_EDITOR, 0, static_cast<LPARAM>(EditorExitKind::Terminate));
|
|
|
|
}
|
|
|
|
CloseHandle(processHandle);
|
|
|
|
});
|
|
|
|
|
|
|
|
waitForEditorThread.detach();
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
2019-10-02 23:18:55 +08:00
|
|
|
void FancyZones::SettingsChanged() noexcept
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
std::shared_lock readLock(m_lock);
|
2019-10-02 23:18:55 +08:00
|
|
|
// Update the hotkey
|
|
|
|
UnregisterHotKey(m_window, 1);
|
2020-03-13 17:55:15 +08:00
|
|
|
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
|
2019-10-02 23:18:55 +08:00
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
// IZoneWindowHost
|
2020-02-17 23:28:49 +08:00
|
|
|
IFACEMETHODIMP_(void)
|
|
|
|
FancyZones::MoveWindowsOnActiveZoneSetChange() noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
MoveWindowsOnDisplayChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
|
|
|
{
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_HOTKEY:
|
|
|
|
{
|
|
|
|
if (wparam == 1)
|
|
|
|
{
|
2019-11-19 07:29:42 +08:00
|
|
|
ToggleEditor();
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SETTINGCHANGE:
|
|
|
|
{
|
|
|
|
if (wparam == SPI_SETWORKAREA)
|
|
|
|
{
|
|
|
|
OnDisplayChange(DisplayChangeType::WorkArea);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
|
|
{
|
|
|
|
OnDisplayChange(DisplayChangeType::DisplayChange);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
2020-05-04 22:29:48 +08:00
|
|
|
POINT ptScreen;
|
|
|
|
GetPhysicalCursorPos(&ptScreen);
|
|
|
|
|
2020-05-05 02:10:23 +08:00
|
|
|
if (message == WM_PRIV_LOWLEVELKB)
|
|
|
|
{
|
|
|
|
OnSnapHotkey(static_cast<DWORD>(lparam));
|
|
|
|
}
|
|
|
|
else if (message == WM_PRIV_VD_INIT)
|
2020-05-01 22:13:16 +08:00
|
|
|
{
|
|
|
|
OnDisplayChange(DisplayChangeType::Initialization);
|
|
|
|
}
|
|
|
|
else if (message == WM_PRIV_VD_SWITCH)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
OnDisplayChange(DisplayChangeType::VirtualDesktop);
|
|
|
|
}
|
2020-05-01 22:13:16 +08:00
|
|
|
else if (message == WM_PRIV_VD_UPDATE)
|
2019-12-24 22:47:28 +08:00
|
|
|
{
|
2020-05-01 22:13:16 +08:00
|
|
|
std::vector<GUID> ids{};
|
2020-05-05 16:13:50 +08:00
|
|
|
if (VirtualDesktopUtils::GetVirtualDesktopIds(ids))
|
2020-05-01 22:13:16 +08:00
|
|
|
{
|
|
|
|
RegisterVirtualDesktopUpdates(ids);
|
|
|
|
}
|
2019-12-24 22:47:28 +08:00
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
else if (message == WM_PRIV_EDITOR)
|
|
|
|
{
|
|
|
|
if (lparam == static_cast<LPARAM>(EditorExitKind::Exit))
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
OnEditorExitEvent();
|
2019-09-05 00:26:26 +08:00
|
|
|
OnDisplayChange(DisplayChangeType::Editor);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Clean up the event either way
|
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
m_terminateEditorEvent.release();
|
|
|
|
}
|
|
|
|
}
|
2020-05-04 22:29:48 +08:00
|
|
|
else if (message == WM_PRIV_MOVESIZESTART)
|
|
|
|
{
|
|
|
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
|
|
|
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
|
|
|
{
|
|
|
|
MoveSizeStart(hwnd, monitor, ptScreen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (message == WM_PRIV_MOVESIZEEND)
|
|
|
|
{
|
|
|
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
|
|
|
MoveSizeEnd(hwnd, ptScreen);
|
|
|
|
}
|
|
|
|
else if (message == WM_PRIV_LOCATIONCHANGE && InMoveSize())
|
|
|
|
{
|
|
|
|
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
|
|
|
{
|
|
|
|
MoveSizeUpdate(monitor, ptScreen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (message == WM_PRIV_WINDOWCREATED)
|
|
|
|
{
|
|
|
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
|
|
|
WindowCreated(hwnd);
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return DefWindowProc(window, message, wparam, lparam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-05 16:13:50 +08:00
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
|
|
|
{
|
2019-12-24 22:47:28 +08:00
|
|
|
if (changeType == DisplayChangeType::VirtualDesktop ||
|
|
|
|
changeType == DisplayChangeType::Initialization)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
GUID currentVirtualDesktopId{};
|
2020-04-30 17:16:25 +08:00
|
|
|
if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tVirtualDesktopId))
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
m_currentVirtualDesktopId = currentVirtualDesktopId;
|
2020-05-05 16:13:50 +08:00
|
|
|
}
|
|
|
|
if (changeType == DisplayChangeType::Initialization)
|
|
|
|
{
|
|
|
|
std::vector<std::wstring> ids{};
|
|
|
|
if (VirtualDesktopUtils::GetVirtualDesktopIds(ids) && !ids.empty())
|
2020-04-30 17:16:25 +08:00
|
|
|
{
|
2020-05-05 16:13:50 +08:00
|
|
|
JSONHelpers::FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]);
|
|
|
|
JSONHelpers::FancyZonesDataInstance().RemoveDeletedDesktops(ids);
|
2020-04-30 17:16:25 +08:00
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateZoneWindows();
|
|
|
|
|
|
|
|
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
|
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
if (m_settings->GetSettings()->displayChange_moveWindows)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
MoveWindowsOnDisplayChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (changeType == DisplayChangeType::VirtualDesktop)
|
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
if (m_settings->GetSettings()->virtualDesktopChange_moveWindows)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
MoveWindowsOnDisplayChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (changeType == DisplayChangeType::Editor)
|
|
|
|
{
|
2020-03-13 17:55:15 +08:00
|
|
|
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
MoveWindowsOnDisplayChange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
|
|
|
{
|
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
wil::unique_cotaskmem_string virtualDesktopId;
|
|
|
|
if (SUCCEEDED_LOG(StringFromCLSID(m_currentVirtualDesktopId, &virtualDesktopId)))
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
2020-02-18 18:55:08 +08:00
|
|
|
JSONHelpers::FancyZonesDataInstance().SetActiveDeviceId(uniqueId);
|
2020-02-10 21:59:51 +08:00
|
|
|
|
2020-02-18 18:55:08 +08:00
|
|
|
const bool newWorkArea = IsNewWorkArea(m_currentVirtualDesktopId, monitor);
|
2020-03-27 18:24:27 +08:00
|
|
|
// "Turning FLASHING_ZONE option off"
|
|
|
|
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
|
|
|
|
const bool flash = false;
|
2019-12-12 17:10:55 +08:00
|
|
|
|
2020-04-10 22:29:18 +08:00
|
|
|
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
|
2020-02-10 21:59:51 +08:00
|
|
|
if (zoneWindow)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
|
|
|
}
|
2020-03-05 15:01:58 +08:00
|
|
|
|
2020-02-18 18:55:08 +08:00
|
|
|
if (newWorkArea)
|
|
|
|
{
|
|
|
|
RegisterNewWorkArea(m_currentVirtualDesktopId, monitor);
|
|
|
|
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
|
|
|
{
|
|
|
|
auto thisRef = reinterpret_cast<FancyZones*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
|
|
|
if (!thisRef && (message == WM_CREATE))
|
|
|
|
{
|
|
|
|
const auto createStruct = reinterpret_cast<LPCREATESTRUCT>(lparam);
|
|
|
|
thisRef = reinterpret_cast<FancyZones*>(createStruct->lpCreateParams);
|
|
|
|
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(thisRef));
|
|
|
|
}
|
|
|
|
|
|
|
|
return thisRef ? thisRef->WndProc(window, message, wparam, lparam) :
|
2020-02-10 21:59:51 +08:00
|
|
|
DefWindowProc(window, message, wparam, lparam);
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FancyZones::UpdateZoneWindows() noexcept
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
2019-09-05 00:26:26 +08:00
|
|
|
MONITORINFOEX mi;
|
|
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
if (GetMonitorInfo(monitor, &mi))
|
|
|
|
{
|
|
|
|
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
|
|
|
PCWSTR deviceId = nullptr;
|
|
|
|
|
|
|
|
bool validMonitor = true;
|
|
|
|
if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1))
|
|
|
|
{
|
|
|
|
if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER))
|
|
|
|
{
|
|
|
|
validMonitor = FALSE;
|
|
|
|
}
|
|
|
|
else if (displayDevice.DeviceID[0] != L'\0')
|
|
|
|
{
|
|
|
|
deviceId = displayDevice.DeviceID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validMonitor)
|
|
|
|
{
|
|
|
|
if (!deviceId)
|
|
|
|
{
|
|
|
|
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
2020-02-10 21:59:51 +08:00
|
|
|
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
|
|
|
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
|
|
|
strongThis->AddZoneWindow(monitor, deviceId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void FancyZones::MoveWindowsOnDisplayChange() noexcept
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
auto callback = [](HWND window, LPARAM data) -> BOOL {
|
2019-09-05 00:26:26 +08:00
|
|
|
int i = static_cast<int>(reinterpret_cast<UINT_PTR>(::GetProp(window, ZONE_STAMP)));
|
|
|
|
if (i != 0)
|
|
|
|
{
|
|
|
|
// i is off by 1 since 0 is special.
|
|
|
|
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
2020-04-30 18:16:08 +08:00
|
|
|
std::unique_lock writeLock(strongThis->m_lock);
|
|
|
|
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndex(window, nullptr, i - 1, strongThis->m_zoneWindowMap);
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
|
|
|
{
|
2020-02-03 16:32:38 +08:00
|
|
|
auto window = GetForegroundWindow();
|
2020-04-30 18:16:08 +08:00
|
|
|
if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
|
|
|
if (monitor)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
std::shared_lock readLock(m_lock);
|
2020-02-10 21:59:51 +08:00
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
auto iter = m_zoneWindowMap.find(monitor);
|
|
|
|
if (iter != m_zoneWindowMap.end())
|
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
const auto& zoneWindowPtr = iter->second;
|
|
|
|
zoneWindowPtr->CycleActiveZoneSet(vkCode);
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 20:12:59 +08:00
|
|
|
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-02-03 16:32:38 +08:00
|
|
|
auto window = GetForegroundWindow();
|
2020-04-30 18:16:08 +08:00
|
|
|
if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-25 01:50:26 +08:00
|
|
|
const HMONITOR current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
|
|
|
if (current)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-25 01:50:26 +08:00
|
|
|
std::vector<HMONITOR> monitorInfo = GetMonitorsSorted();
|
2020-05-01 22:17:16 +08:00
|
|
|
if (monitorInfo.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-03-25 01:50:26 +08:00
|
|
|
// Multi monitor environment.
|
|
|
|
auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current);
|
|
|
|
do
|
|
|
|
{
|
2020-04-30 18:16:08 +08:00
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
if (m_windowMoveHandler.MoveWindowIntoZoneByDirection(*currMonitorInfo, window, vkCode, false /* cycle through zones */, m_zoneWindowMap))
|
2020-03-25 01:50:26 +08:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
|
|
|
|
if (vkCode == VK_RIGHT)
|
|
|
|
{
|
|
|
|
currMonitorInfo = std::next(currMonitorInfo);
|
|
|
|
if (currMonitorInfo == std::end(monitorInfo))
|
|
|
|
{
|
|
|
|
currMonitorInfo = std::begin(monitorInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vkCode == VK_LEFT)
|
|
|
|
{
|
|
|
|
if (currMonitorInfo == std::begin(monitorInfo))
|
|
|
|
{
|
|
|
|
currMonitorInfo = std::end(monitorInfo);
|
|
|
|
}
|
|
|
|
currMonitorInfo = std::prev(currMonitorInfo);
|
|
|
|
}
|
|
|
|
} while (*currMonitorInfo != current);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Single monitor environment.
|
2020-04-30 18:16:08 +08:00
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
return m_windowMoveHandler.MoveWindowIntoZoneByDirection(current, window, vkCode, true /* cycle through zones */, m_zoneWindowMap);
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-06 20:12:59 +08:00
|
|
|
return false;
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
2020-05-01 22:13:16 +08:00
|
|
|
void FancyZones::RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept
|
2020-02-18 18:55:08 +08:00
|
|
|
{
|
2020-05-01 22:13:16 +08:00
|
|
|
std::unordered_set<GUID> activeVirtualDesktops(std::begin(ids), std::end(ids));
|
2020-02-18 18:55:08 +08:00
|
|
|
std::unique_lock writeLock(m_lock);
|
|
|
|
bool modified{ false };
|
2020-05-01 22:13:16 +08:00
|
|
|
for (auto it = std::begin(m_processedWorkAreas); it != std::end(m_processedWorkAreas);)
|
2020-02-18 18:55:08 +08:00
|
|
|
{
|
2020-05-01 22:13:16 +08:00
|
|
|
auto iter = activeVirtualDesktops.find(it->first);
|
|
|
|
if (iter == activeVirtualDesktops.end())
|
2020-02-10 21:59:51 +08:00
|
|
|
{
|
2020-02-18 18:55:08 +08:00
|
|
|
// if we couldn't find the GUID in currentVirtualDesktopIds, we must remove it from both m_processedWorkAreas and deviceInfoMap
|
|
|
|
wil::unique_cotaskmem_string virtualDesktopId;
|
|
|
|
if (SUCCEEDED_LOG(StringFromCLSID(it->first, &virtualDesktopId)))
|
2020-02-10 21:59:51 +08:00
|
|
|
{
|
2020-02-18 18:55:08 +08:00
|
|
|
modified |= JSONHelpers::FancyZonesDataInstance().RemoveDevicesByVirtualDesktopId(virtualDesktopId.get());
|
2019-12-12 17:10:55 +08:00
|
|
|
}
|
2020-02-18 18:55:08 +08:00
|
|
|
it = m_processedWorkAreas.erase(it);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-01 22:13:16 +08:00
|
|
|
activeVirtualDesktops.erase(it->first); // virtual desktop already in map, skip it
|
2020-02-18 18:55:08 +08:00
|
|
|
++it;
|
2019-12-12 17:10:55 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-18 18:55:08 +08:00
|
|
|
if (modified)
|
|
|
|
{
|
|
|
|
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
|
|
|
}
|
|
|
|
// register new virtual desktops, if any
|
2020-05-01 22:13:16 +08:00
|
|
|
for (const auto& id : activeVirtualDesktops)
|
2020-02-18 18:55:08 +08:00
|
|
|
{
|
|
|
|
m_processedWorkAreas[id] = std::vector<HMONITOR>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FancyZones::RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept
|
|
|
|
{
|
|
|
|
if (!m_processedWorkAreas.contains(virtualDesktopId))
|
|
|
|
{
|
|
|
|
m_processedWorkAreas[virtualDesktopId] = { monitor };
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_processedWorkAreas[virtualDesktopId].push_back(monitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FancyZones::IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept
|
|
|
|
{
|
|
|
|
auto it = m_processedWorkAreas.find(virtualDesktopId);
|
|
|
|
if (it != m_processedWorkAreas.end())
|
|
|
|
{
|
|
|
|
// virtual desktop exists, check if it's processed on given monitor
|
|
|
|
return std::find(it->second.begin(), it->second.end(), monitor) == it->second.end();
|
|
|
|
}
|
|
|
|
return true;
|
2019-12-12 17:10:55 +08:00
|
|
|
}
|
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
void FancyZones::OnEditorExitEvent() noexcept
|
|
|
|
{
|
|
|
|
// Colect information about changes in zone layout after editor exited.
|
|
|
|
JSONHelpers::FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(ZoneWindowUtils::GetActiveZoneSetTmpPath());
|
|
|
|
JSONHelpers::FancyZonesDataInstance().ParseDeletedCustomZoneSetsFromTmpFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
|
|
|
JSONHelpers::FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
|
|
|
|
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
|
|
|
}
|
|
|
|
|
2020-03-25 01:50:26 +08:00
|
|
|
std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
|
|
|
|
{
|
|
|
|
std::shared_lock readLock(m_lock);
|
|
|
|
|
|
|
|
auto monitorInfo = GetRawMonitorData();
|
|
|
|
OrderMonitors(monitorInfo);
|
|
|
|
std::vector<HMONITOR> output;
|
|
|
|
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
|
|
|
|
{
|
|
|
|
std::shared_lock readLock(m_lock);
|
|
|
|
|
|
|
|
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
|
|
|
|
for (const auto& [monitor, window] : m_zoneWindowMap)
|
|
|
|
{
|
|
|
|
if (window->ActiveZoneSet() != nullptr)
|
|
|
|
{
|
|
|
|
MONITORINFOEX mi;
|
|
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
GetMonitorInfo(monitor, &mi);
|
|
|
|
monitorInfo.push_back({ monitor, mi.rcMonitor });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return monitorInfo;
|
|
|
|
}
|
|
|
|
|
2020-02-10 21:59:51 +08:00
|
|
|
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-02-10 21:59:51 +08:00
|
|
|
if (!settings)
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
return winrt::make_self<FancyZones>(hinstance, settings);
|
2020-03-24 22:17:25 +08:00
|
|
|
}
|