#include "pch.h" #include #include #include #include "FancyZones.h" #include "lib/Settings.h" #include "lib/ZoneWindow.h" #include "lib/FancyZonesData.h" #include "lib/ZoneSet.h" #include "lib/WindowMoveHandler.h" #include "lib/FancyZonesWinHookEventIDs.h" #include "lib/util.h" #include "trace.h" #include "VirtualDesktopUtils.h" #include "MonitorWorkAreaHandler.h" #include #include enum class DisplayChangeType { WorkArea, DisplayChange, VirtualDesktop, Initialization }; namespace { constexpr int CUSTOM_POSITIONING_LEFT_TOP_PADDING = 16; } struct FancyZones : public winrt::implements { public: FancyZones(HINSTANCE hinstance, const winrt::com_ptr& settings) noexcept : m_hinstance(hinstance), m_settings(settings), m_mouseHook(std::bind(&FancyZones::OnMouseDown, this)), m_shiftHook(std::bind(&FancyZones::OnShiftChangeState, this, std::placeholders::_1)), m_ctrlHook(std::bind(&FancyZones::OnCtrlChangeState, this, std::placeholders::_1)), m_windowMoveHandler(settings, &m_mouseHook, &m_shiftHook, &m_ctrlHook) { m_settings->SetCallback(this); } // IFancyZones IFACEMETHODIMP_(void) Run() noexcept; IFACEMETHODIMP_(void) Destroy() noexcept; void OnMouseDown() noexcept { m_windowMoveHandler.OnMouseDown(); PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); } void OnShiftChangeState(bool state) noexcept { m_windowMoveHandler.OnShiftChangeState(state); PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); } void OnCtrlChangeState(bool state) noexcept { m_windowMoveHandler.OnCtrlChangeState(state); PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); } void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept { std::unique_lock writeLock(m_lock); m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId)); } void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept { std::unique_lock writeLock(m_lock); m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId)); } void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept { std::unique_lock writeLock(m_lock); m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId)); } IFACEMETHODIMP_(void) HandleWinHookEvent(const WinHookEvent* data) noexcept { const auto wparam = reinterpret_cast(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; } } IFACEMETHODIMP_(void) VirtualDesktopChanged() noexcept; IFACEMETHODIMP_(void) VirtualDesktopInitialize() noexcept; IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept; IFACEMETHODIMP_(void) ToggleEditor() noexcept; IFACEMETHODIMP_(void) SettingsChanged() noexcept; void WindowCreated(HWND window) noexcept; // IZoneWindowHost IFACEMETHODIMP_(void) MoveWindowsOnActiveZoneSetChange() noexcept; IFACEMETHODIMP_(COLORREF) 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) GetZoneHighlightColor() noexcept { // Skip the leading # and convert to long const auto color = m_settings->GetSettings()->zoneHighlightColor; 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_(int) GetZoneHighlightOpacity() noexcept { return m_settings->GetSettings()->zoneHighlightOpacity; } IFACEMETHODIMP_(bool) isMakeDraggedWindowTransparentActive() noexcept { return m_settings->GetSettings()->makeDraggedWindowTransparent; } IFACEMETHODIMP_(bool) InMoveSize() noexcept { std::shared_lock readLock(m_lock); return m_windowMoveHandler.InMoveSize(); } LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; void OnDisplayChange(DisplayChangeType changeType) noexcept; void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept; protected: static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; private: struct require_read_lock { template require_read_lock(const std::shared_lock& lock) { lock; } template require_read_lock(const std::unique_lock& lock) { lock; } }; struct require_write_lock { template require_write_lock(const std::unique_lock& lock) { lock; } }; void UpdateZoneWindows() noexcept; void UpdateWindowsPositions() noexcept; void CycleActiveZoneSet(DWORD vkCode) noexcept; bool OnSnapHotkey(DWORD vkCode) noexcept; void RegisterVirtualDesktopUpdates(std::vector& ids) noexcept; bool IsSplashScreen(HWND window); bool ShouldProcessNewWindow(HWND window) noexcept; std::vector GetZoneIndexSetFromWorkAreaHistory(HWND window, winrt::com_ptr workArea) noexcept; std::pair, std::vector> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, std::unordered_map>& workAreaMap) noexcept; std::pair, std::vector> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, bool isPrimaryMonitor) noexcept; void MoveWindowIntoZone(HWND window, winrt::com_ptr zoneWindow, const std::vector& zoneIndexSet) noexcept; void OnEditorExitEvent() noexcept; bool ShouldProcessSnapHotkey() noexcept; std::vector> GetRawMonitorData() noexcept; std::vector GetMonitorsSorted() noexcept; const HINSTANCE m_hinstance{}; mutable std::shared_mutex m_lock; HWND m_window{}; WindowMoveHandler m_windowMoveHandler; MonitorWorkAreaHandler m_workAreaHandler; SecondaryMouseButtonsHook m_mouseHook; ShiftKeyHook m_shiftHook; CtrlKeyHook m_ctrlHook; winrt::com_ptr m_settings{}; GUID m_previousDesktopId{}; // UUID of previously active virtual desktop. GUID m_currentDesktopId{}; // UUID of the current virtual desktop. wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on wil::unique_handle m_terminateVirtualDesktopTrackerEvent; OnThreadExecutor m_dpiUnawareThread; OnThreadExecutor m_virtualDesktopTrackerThread; 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 // Did we terminate the editor or was it closed cleanly? enum class EditorExitKind : byte { Exit, Terminate }; }; 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}"); UINT FancyZones::WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}"); UINT FancyZones::WM_PRIV_LOWLEVELKB = RegisterWindowMessage(L"{763c03a3-03d9-4cde-8d71-f0358b0b4b52}"); // IFancyZones IFACEMETHODIMP_(void) FancyZones::Run() noexcept { 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); if (!m_window) return; RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code()); VirtualDesktopInitialize(); m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [] { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED); } }) .wait(); m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr)); m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] { VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } }); } // IFancyZones IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept { std::unique_lock writeLock(m_lock); m_workAreaHandler.Clear(); BufferedPaintUnInit(); if (m_window) { DestroyWindow(m_window); m_window = nullptr; } if (m_terminateVirtualDesktopTrackerEvent) { SetEvent(m_terminateVirtualDesktopTrackerEvent.get()); } } // IFancyZonesCallback IFACEMETHODIMP_(void) FancyZones::VirtualDesktopChanged() noexcept { // 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. PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0); } // IFancyZonesCallback IFACEMETHODIMP_(void) FancyZones::VirtualDesktopInitialize() noexcept { PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0); } bool FancyZones::ShouldProcessNewWindow(HWND window) noexcept { // Avoid processing splash screens, already stamped (zoned) windows, or those windows // that belong to excluded applications list. if (IsSplashScreen(window) || (reinterpret_cast(::GetProp(window, MULTI_ZONE_STAMP)) != 0) || !IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { return false; } return true; } std::vector FancyZones::GetZoneIndexSetFromWorkAreaHistory( HWND window, winrt::com_ptr workArea) noexcept { const auto activeZoneSet = workArea->ActiveZoneSet(); if (activeZoneSet) { wil::unique_cotaskmem_string zoneSetId; if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &zoneSetId))) { return FancyZonesDataInstance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), zoneSetId.get()); } } return {}; } std::pair, std::vector> FancyZones::GetAppZoneHistoryInfo( HWND window, HMONITOR monitor, std::unordered_map>& workAreaMap) noexcept { if (workAreaMap.contains(monitor)) { auto workArea = workAreaMap[monitor]; workAreaMap.erase(monitor); // monitor processed, remove entry from the map return { workArea, GetZoneIndexSetFromWorkAreaHistory(window, workArea) }; } return { nullptr, {} }; } std::pair, std::vector> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, bool isPrimaryMonitor) noexcept { std::pair, std::vector> appZoneHistoryInfo{ nullptr, {} }; auto workAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId); // Search application history on currently active monitor. appZoneHistoryInfo = GetAppZoneHistoryInfo(window, monitor, workAreaMap); if (isPrimaryMonitor && appZoneHistoryInfo.second.empty()) { // No application history on primary monitor, search on remaining monitors. for (const auto& [monitor, workArea] : workAreaMap) { auto zoneIndexSet = GetZoneIndexSetFromWorkAreaHistory(window, workArea); if (!zoneIndexSet.empty()) { return { workArea, zoneIndexSet }; } } } return appZoneHistoryInfo; } void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr zoneWindow, const std::vector& zoneIndexSet) noexcept { auto& fancyZonesData = FancyZonesDataInstance(); if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId())) { m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow); fancyZonesData.UpdateProcessIdToHandleMap(window, zoneWindow->UniqueId()); } } inline int RectWidth(const RECT& rect) { return rect.right - rect.left; } inline int RectHeight(const RECT& rect) { return rect.bottom - rect.top; } RECT FitOnScreen(const RECT& windowRect, const RECT& originMonitorRect, const RECT& destMonitorRect) { // New window position on active monitor. If window fits the screen, this will be final position. int left = destMonitorRect.left + (windowRect.left - originMonitorRect.left); int top = destMonitorRect.top + (windowRect.top - originMonitorRect.top); int W = RectWidth(windowRect); int H = RectHeight(windowRect); if ((left < destMonitorRect.left) || (left + W > destMonitorRect.right)) { // Set left window border to left border of screen (add padding). Resize window width if needed. left = destMonitorRect.left + CUSTOM_POSITIONING_LEFT_TOP_PADDING; W = min(W, RectWidth(destMonitorRect) - CUSTOM_POSITIONING_LEFT_TOP_PADDING); } if ((top < destMonitorRect.top) || (top + H > destMonitorRect.bottom)) { // Set top window border to top border of screen (add padding). Resize window height if needed. top = destMonitorRect.top + CUSTOM_POSITIONING_LEFT_TOP_PADDING; H = min(H, RectHeight(destMonitorRect) - CUSTOM_POSITIONING_LEFT_TOP_PADDING); } return { .left = left, .top = top, .right = left + W, .bottom = top + H }; } void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept { // By default Windows opens new window on primary monitor. // Try to preserve window width and height, adjust top-left corner if needed. HMONITOR origin = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); if (origin == monitor) { // Certain applications by design open in last known position, regardless of FancyZones. // If that position is on currently active monitor, skip custom positioning. return; } WINDOWPLACEMENT placement{}; if (GetWindowPlacement(window, &placement)) { MONITORINFOEX originMi; originMi.cbSize = sizeof(originMi); if (GetMonitorInfo(origin, &originMi)) { MONITORINFOEX destMi; destMi.cbSize = sizeof(destMi); if (GetMonitorInfo(monitor, &destMi)) { RECT newPosition = FitOnScreen(placement.rcNormalPosition, originMi.rcWork, destMi.rcWork); SizeWindowToRect(window, newPosition); } } } } // IFancyZonesCallback IFACEMETHODIMP_(void) FancyZones::WindowCreated(HWND window) noexcept { std::shared_lock readLock(m_lock); GUID desktopId{}; if (VirtualDesktopUtils::GetWindowDesktopId(window, &desktopId) && desktopId != m_currentDesktopId) { // Switch between virtual desktops results with posting same windows messages that also indicate // 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 openOnActiveMonitor = m_settings->GetSettings()->openWindowOnActiveMonitor; if ((moveToAppLastZone || openOnActiveMonitor) && ShouldProcessNewWindow(window)) { HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); HMONITOR active = primary; POINT cursorPosition{}; if (GetCursorPos(&cursorPosition)) { active = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY); } bool windowZoned{ false }; if (moveToAppLastZone) { const bool primaryActive = (primary == active); std::pair, std::vector> appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, primaryActive); if (!appZoneHistoryInfo.second.empty()) { MoveWindowIntoZone(window, appZoneHistoryInfo.first, appZoneHistoryInfo.second); windowZoned = true; } } if (!windowZoned && openOnActiveMonitor) { m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { OpenWindowOnActiveMonitor(window, active); } }).wait(); } } } // IFancyZonesCallback IFACEMETHODIMP_(bool) FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept { // Return true to swallow the keyboard event bool const shift = GetAsyncKeyState(VK_SHIFT) & 0x8000; bool const win = GetAsyncKeyState(VK_LWIN) & 0x8000 || GetAsyncKeyState(VK_RWIN) & 0x8000; if (win && !shift) { bool const ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000; if (ctrl) { // 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; //} } else if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT)) { if (ShouldProcessSnapHotkey()) { Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/); // 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; } } } // 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; //} if (m_windowMoveHandler.IsDragEnabled() && shift) { return true; } 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)); } HMONITOR monitor{}; HWND foregroundWindow{}; const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen; POINT currentCursorPos{}; if (use_cursorpos_editor_startupscreen) { GetCursorPos(¤tCursorPos); monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY); } else { foregroundWindow = GetForegroundWindow(); monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY); } if (!monitor) { return; } std::shared_lock readLock(m_lock); auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor); if (!zoneWindow) { return; } MONITORINFOEX mi; mi.cbSize = sizeof(mi); m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { GetMonitorInfo(monitor, &mi); } }) .wait(); const auto taskbar_x_offset = mi.rcWork.left - mi.rcMonitor.left; const auto taskbar_y_offset = mi.rcWork.top - mi.rcMonitor.top; 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; const std::wstring editorLocation = std::to_wstring(x) + L"_" + std::to_wstring(y) + L"_" + std::to_wstring(width) + L"_" + std::to_wstring(height); const auto& fancyZonesData = FancyZonesDataInstance(); if (!fancyZonesData.SerializeDeviceInfoToTmpFile(zoneWindow->UniqueId())) { return; } const std::wstring params = /*1*/ editorLocation + L" " + /*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\""; SHELLEXECUTEINFO sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; sei.lpFile = L"modules\\FancyZones\\FancyZonesEditor.exe"; sei.lpParameters = params.c_str(); sei.nShow = SW_SHOWNORMAL; ShellExecuteEx(&sei); Trace::FancyZones::EditorLaunched(1); // Launch the editor on a background thread // Wait for the editor's process to exit // Post back to the main thread to update std::thread waitForEditorThread([window = m_window, processHandle = sei.hProcess, terminateEditorEvent = m_terminateEditorEvent.get()]() { HANDLE waitEvents[2] = { processHandle, terminateEditorEvent }; auto result = WaitForMultipleObjects(2, waitEvents, false, INFINITE); if (result == WAIT_OBJECT_0 + 0) { // Editor exited // Update any changes it may have made PostMessage(window, WM_PRIV_EDITOR, 0, static_cast(EditorExitKind::Exit)); } 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(EditorExitKind::Terminate)); } CloseHandle(processHandle); }); waitForEditorThread.detach(); } void FancyZones::SettingsChanged() noexcept { std::shared_lock readLock(m_lock); // Update the hotkey UnregisterHotKey(m_window, 1); RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code()); } // IZoneWindowHost IFACEMETHODIMP_(void) FancyZones::MoveWindowsOnActiveZoneSetChange() noexcept { if (m_settings->GetSettings()->zoneSetChange_moveWindows) { UpdateWindowsPositions(); } } LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept { switch (message) { case WM_HOTKEY: { if (wparam == 1) { ToggleEditor(); } } break; case WM_SETTINGCHANGE: { if (wparam == SPI_SETWORKAREA) { // Changes in taskbar position resulted in different size of work area. // Invalidate cached work-areas so they can be recreated with latest information. m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::WorkArea); } } break; case WM_DISPLAYCHANGE: { // Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information. m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::DisplayChange); } break; default: { POINT ptScreen; GetPhysicalCursorPos(&ptScreen); if (message == WM_PRIV_LOWLEVELKB) { OnSnapHotkey(static_cast(lparam)); } else if (message == WM_PRIV_VD_INIT) { OnDisplayChange(DisplayChangeType::Initialization); } else if (message == WM_PRIV_VD_SWITCH) { OnDisplayChange(DisplayChangeType::VirtualDesktop); } else if (message == WM_PRIV_VD_UPDATE) { std::vector ids{}; if (VirtualDesktopUtils::GetVirtualDesktopIds(ids)) { RegisterVirtualDesktopUpdates(ids); } } else if (message == WM_PRIV_EDITOR) { if (lparam == static_cast(EditorExitKind::Exit)) { OnEditorExitEvent(); } { // Clean up the event either way std::unique_lock writeLock(m_lock); m_terminateEditorEvent.release(); } } else if (message == WM_PRIV_MOVESIZESTART) { auto hwnd = reinterpret_cast(wparam); if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) { MoveSizeStart(hwnd, monitor, ptScreen); } } else if (message == WM_PRIV_MOVESIZEEND) { auto hwnd = reinterpret_cast(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(wparam); WindowCreated(hwnd); } else { return DefWindowProc(window, message, wparam, lparam); } } break; } return 0; } void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept { if (changeType == DisplayChangeType::VirtualDesktop || changeType == DisplayChangeType::Initialization) { m_previousDesktopId = m_currentDesktopId; GUID currentVirtualDesktopId{}; if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tVirtualDesktopId)) { m_currentDesktopId = currentVirtualDesktopId; if (m_previousDesktopId != GUID_NULL && m_currentDesktopId != m_previousDesktopId) { Trace::VirtualDesktopChanged(); } } if (changeType == DisplayChangeType::Initialization) { std::vector ids{}; if (VirtualDesktopUtils::GetVirtualDesktopIds(ids) && !ids.empty()) { FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]); FancyZonesDataInstance().RemoveDeletedDesktops(ids); } } } UpdateZoneWindows(); if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange)) { if (m_settings->GetSettings()->displayChange_moveWindows) { UpdateWindowsPositions(); } } } void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept { std::unique_lock writeLock(m_lock); if (m_workAreaHandler.IsNewWorkArea(m_currentDesktopId, monitor)) { wil::unique_cotaskmem_string virtualDesktopId; if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId))) { std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get()); // "Turning FLASHING_ZONE option off" //const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones; const bool flash = false; std::wstring parentId{}; auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor); if (parentArea) { parentId = parentArea->UniqueId(); } auto workArea = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, parentId, flash); if (workArea) { m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea); FancyZonesDataInstance().SaveFancyZonesData(); } } } } LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept { auto thisRef = reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); if (!thisRef && (message == WM_CREATE)) { const auto createStruct = reinterpret_cast(lparam); thisRef = reinterpret_cast(createStruct->lpCreateParams); SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(thisRef)); } return thisRef ? thisRef->WndProc(window, message, wparam, lparam) : DefWindowProc(window, message, wparam, lparam); } void FancyZones::UpdateZoneWindows() noexcept { auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { 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) ? L"\\\\?\\DISPLAY#REMOTEDISPLAY#" : L"\\\\?\\DISPLAY#LOCALDISPLAY#"; } auto strongThis = reinterpret_cast(data); strongThis->AddZoneWindow(monitor, deviceId); } } return TRUE; }; EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(this)); } void FancyZones::UpdateWindowsPositions() noexcept { auto callback = [](HWND window, LPARAM data) -> BOOL { size_t bitmask = reinterpret_cast(::GetProp(window, MULTI_ZONE_STAMP)); if (bitmask != 0) { std::vector indexSet; for (int i = 0; i < std::numeric_limits::digits; i++) { if ((1ull << i) & bitmask) { indexSet.push_back(i); } } auto strongThis = reinterpret_cast(data); std::unique_lock writeLock(strongThis->m_lock); auto zoneWindow = strongThis->m_workAreaHandler.GetWorkArea(window); if (zoneWindow) { strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, indexSet, zoneWindow); } } return TRUE; }; EnumWindows(callback, reinterpret_cast(this)); } void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept { auto window = GetForegroundWindow(); if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (monitor) { std::shared_lock readLock(m_lock); auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor); if (zoneWindow) { zoneWindow->CycleActiveZoneSet(vkCode); } } } } bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept { auto window = GetForegroundWindow(); if (IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { const HMONITOR current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (current) { std::vector monitorInfo = GetMonitorsSorted(); if (monitorInfo.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors) { // Multi monitor environment. auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current); do { std::unique_lock writeLock(m_lock); if (m_windowMoveHandler.MoveWindowIntoZoneByDirection(window, vkCode, false /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, *currMonitorInfo))) { 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. std::unique_lock writeLock(m_lock); if (m_settings->GetSettings()->restoreSize) { bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirection(window, vkCode, false /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, current)); if (!moved) { RestoreWindowOrigin(window); RestoreWindowSize(window); } return true; } else { return m_windowMoveHandler.MoveWindowIntoZoneByDirection(window, vkCode, true /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, current)); } } } } return false; } void FancyZones::RegisterVirtualDesktopUpdates(std::vector& ids) noexcept { std::unique_lock writeLock(m_lock); m_workAreaHandler.RegisterUpdates(ids); std::vector active{}; if (VirtualDesktopUtils::GetVirtualDesktopIds(active)) { FancyZonesDataInstance().RemoveDeletedDesktops(active); } } bool FancyZones::IsSplashScreen(HWND window) { wchar_t splashClassName[] = L"MsoSplash"; // shouldn't be localized wchar_t className[MAX_PATH]; if (GetClassName(window, className, MAX_PATH) == 0) { return false; } return wcscmp(splashClassName, className) == 0; } void FancyZones::OnEditorExitEvent() noexcept { // Collect information about changes in zone layout after editor exited. FancyZonesDataInstance().ParseDataFromTmpFiles(); for (auto workArea : m_workAreaHandler.GetAllWorkAreas()) { workArea->UpdateActiveZoneSet(); } if (m_settings->GetSettings()->zoneSetChange_moveWindows) { UpdateWindowsPositions(); } } bool FancyZones::ShouldProcessSnapHotkey() noexcept { if (m_settings->GetSettings()->overrideSnapHotkeys) { const HMONITOR monitor = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONULL); if (monitor) { auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor); if (zoneWindow->ActiveZoneSet() != nullptr) { return true; } } } return false; } std::vector FancyZones::GetMonitorsSorted() noexcept { std::shared_lock readLock(m_lock); auto monitorInfo = GetRawMonitorData(); OrderMonitors(monitorInfo); std::vector output; std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; }); return output; } std::vector> FancyZones::GetRawMonitorData() noexcept { std::shared_lock readLock(m_lock); std::vector> monitorInfo; const auto& activeWorkAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId); for (const auto& [monitor, workArea] : activeWorkAreaMap) { if (workArea->ActiveZoneSet() != nullptr) { MONITORINFOEX mi; mi.cbSize = sizeof(mi); GetMonitorInfo(monitor, &mi); monitorInfo.push_back({ monitor, mi.rcMonitor }); } } return monitorInfo; } winrt::com_ptr MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr& settings) noexcept { if (!settings) { return nullptr; } return winrt::make_self(hinstance, settings); }