mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
FancyZones and Shortcut Guide initial commit
Co-authored-by: Alexis Campailla <alexis@janeasystems.com> Co-authored-by: Bret Anderson <bretan@microsoft.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com> Co-authored-by: Jaime Bernardo <jaime@janeasystems.com> Co-authored-by: Jeff Bogdan <jeffbog@microsoft.com> Co-authored-by: March Rogers <marchr@microsoft.com> Co-authored-by: Mike Harsh <mharsh@microsoft.com> Co-authored-by: Nachum Bundak <Nachum.Bundak@microsoft.com> Co-authored-by: Oliver Jones <ojones@microsoft.com> Co-authored-by: Patrick Little <plittle@microsoft.com>
This commit is contained in:
committed by
Bartosz Sosnowski
parent
10c5396099
commit
8431b80e48
704
src/modules/fancyzones/lib/FancyZones.cpp
Normal file
704
src/modules/fancyzones/lib/FancyZones.cpp
Normal file
@@ -0,0 +1,704 @@
|
||||
#include "pch.h"
|
||||
|
||||
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
||||
{
|
||||
public:
|
||||
FancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
|
||||
: m_hinstance(hinstance)
|
||||
{
|
||||
m_settings.attach(settings);
|
||||
m_settings->SetCallback(this);
|
||||
}
|
||||
|
||||
// IFancyZones
|
||||
IFACEMETHODIMP_(void) Run() noexcept;
|
||||
IFACEMETHODIMP_(void) Destroy() noexcept;
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(bool) InMoveSize() noexcept { std::shared_lock readLock(m_lock); return m_inMoveSize; }
|
||||
IFACEMETHODIMP_(void) MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
||||
IFACEMETHODIMP_(void) VirtualDesktopChanged() noexcept;
|
||||
IFACEMETHODIMP_(void) WindowCreated(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept;
|
||||
IFACEMETHODIMP_(void) ToggleEditor() noexcept;
|
||||
|
||||
// IZoneWindowHost
|
||||
IFACEMETHODIMP_(void) ToggleZoneViewers() noexcept;
|
||||
IFACEMETHODIMP_(void) MoveWindowsOnActiveZoneSetChange() noexcept;
|
||||
IFACEMETHODIMP_(COLORREF) GetZoneHighlightColor() noexcept
|
||||
{
|
||||
// Skip the leading # and convert to long
|
||||
const auto color = m_settings->GetSettings().zoneHightlightColor;
|
||||
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);
|
||||
}
|
||||
|
||||
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
void OnDisplayChange(DisplayChangeType changeType) noexcept;
|
||||
void ShowZoneEditorForMonitor(HMONITOR monitor) noexcept;
|
||||
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
|
||||
void MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
|
||||
|
||||
protected:
|
||||
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
|
||||
private:
|
||||
struct require_read_lock
|
||||
{
|
||||
template<typename T>
|
||||
require_read_lock(const std::shared_lock<T>& lock) { lock; }
|
||||
|
||||
template<typename T>
|
||||
require_read_lock(const std::unique_lock<T>& lock) { lock; }
|
||||
};
|
||||
|
||||
struct require_write_lock
|
||||
{
|
||||
template<typename T>
|
||||
require_write_lock(const std::unique_lock<T>& lock) { lock; }
|
||||
};
|
||||
|
||||
void UpdateZoneWindows() noexcept;
|
||||
void MoveWindowsOnDisplayChange() noexcept;
|
||||
void UpdateDragState(require_write_lock) noexcept;
|
||||
void CycleActiveZoneSet(DWORD vkCode) noexcept;
|
||||
void OnSnapHotkey(DWORD vkCode) noexcept;
|
||||
void MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept;
|
||||
void MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept;
|
||||
void MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept;
|
||||
|
||||
const HINSTANCE m_hinstance{};
|
||||
|
||||
mutable std::shared_mutex m_lock;
|
||||
HWND m_window{};
|
||||
HWND m_windowMoveSize{}; // The window that is being moved/sized
|
||||
bool m_editorsVisible{}; // Are we showing the zone editors?
|
||||
bool m_inMoveSize{}; // Whether or not a move/size operation is currently active
|
||||
bool m_dragEnabled{}; // True if we should be showing zone hints while dragging
|
||||
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
||||
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
|
||||
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||
GUID m_currentVirtualDesktopId{};
|
||||
wil::unique_handle m_terminateEditorEvent;
|
||||
|
||||
static UINT WM_PRIV_VDCHANGED;
|
||||
static UINT WM_PRIV_EDITOR;
|
||||
|
||||
enum class EditorExitKind : byte
|
||||
{
|
||||
Exit,
|
||||
Terminate
|
||||
};
|
||||
};
|
||||
|
||||
UINT FancyZones::WM_PRIV_VDCHANGED = RegisterWindowMessage(L"{128c2cb0-6bdf-493e-abbe-f8705e04aa95}");
|
||||
UINT FancyZones::WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}");
|
||||
|
||||
// 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, MOD_WIN, VK_OEM_3);
|
||||
VirtualDesktopChanged();
|
||||
}
|
||||
|
||||
// IFancyZones
|
||||
IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
|
||||
BufferedPaintUnInit();
|
||||
if (m_window)
|
||||
{
|
||||
DestroyWindow(m_window);
|
||||
m_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void) FancyZones::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
MoveSizeStartInternal(window, monitor, ptScreen, writeLock);
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void) FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
MoveSizeUpdateInternal(monitor, ptScreen, writeLock);
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void) FancyZones::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
MoveSizeEndInternal(window, ptScreen, writeLock);
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void) FancyZones::VirtualDesktopChanged() noexcept
|
||||
{
|
||||
// VirtualDesktopChanged is called from another thread but results in new windows being created.
|
||||
// Jump over to the UI thread to handle it.
|
||||
PostMessage(m_window, WM_PRIV_VDCHANGED, 0, 0);
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void) FancyZones::WindowCreated(HWND window) noexcept
|
||||
{
|
||||
if (m_settings->GetSettings().appLastZone_moveWindows)
|
||||
{
|
||||
wchar_t processPath[MAX_PATH] = { 0 };
|
||||
DWORD modulePathSize = GetProcessPath(window, processPath, static_cast<DWORD>(MAX_PATH));
|
||||
if (modulePathSize > 0)
|
||||
{
|
||||
INT zoneIndex = -1;
|
||||
LRESULT res = RegistryHelpers::GetAppLastZone(window, processPath, &zoneIndex);
|
||||
if ((res == ERROR_SUCCESS) && (zoneIndex != -1))
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, zoneIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (win && !shift)
|
||||
{
|
||||
if (!m_settings->GetSettings().overrideSnapHotkeys)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool const ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
|
||||
if (ctrl)
|
||||
{
|
||||
if ((info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
{
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /* inMoveSize */);
|
||||
CycleActiveZoneSet(info->vkCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT))
|
||||
{
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /* inMoveSize */);
|
||||
OnSnapHotkey(info->vkCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
{
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, false /* control */, true/* inMoveSize */);
|
||||
CycleActiveZoneSet(info->vkCode);
|
||||
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));
|
||||
}
|
||||
|
||||
// TODO: multimon support
|
||||
// Pass in args so that the editor shows up on the correct monitor
|
||||
// This can be an HWND, HMONITOR, or the X/Y/Width/Height of the monitor's work area, (whichever works best).
|
||||
if (const HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY))
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
// Pass command line args to the editor to tell it which layout it should pick by default
|
||||
auto activeZoneSet = iter->second->ActiveZoneSet();
|
||||
std::wstring params = iter->second->UniqueId() + L" " + std::to_wstring(activeZoneSet->LayoutId());
|
||||
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);
|
||||
|
||||
// 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<LPARAM>(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<LPARAM>(EditorExitKind::Terminate));
|
||||
}
|
||||
CloseHandle(processHandle);
|
||||
});
|
||||
waitForEditorThread.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IZoneWindowHost
|
||||
IFACEMETHODIMP_(void) FancyZones::ToggleZoneViewers() noexcept
|
||||
{
|
||||
bool alreadyVisible{};
|
||||
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
alreadyVisible = m_editorsVisible;
|
||||
m_editorsVisible = !alreadyVisible;
|
||||
}
|
||||
Trace::FancyZones::ToggleZoneViewers(!alreadyVisible);
|
||||
|
||||
if (!alreadyVisible)
|
||||
{
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT *, LPARAM data) -> BOOL
|
||||
{
|
||||
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
||||
strongThis->ShowZoneEditorForMonitor(monitor);
|
||||
return TRUE;
|
||||
};
|
||||
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
for (auto iter : m_zoneWindowMap)
|
||||
{
|
||||
iter.second->HideZoneWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IZoneWindowHost
|
||||
IFACEMETHODIMP_(void) FancyZones::MoveWindowsOnActiveZoneSetChange() noexcept
|
||||
{
|
||||
if (m_settings->GetSettings().zoneSetChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_HOTKEY:
|
||||
{
|
||||
if (wparam == 1)
|
||||
{
|
||||
if (m_settings->GetSettings().use_standalone_editor)
|
||||
{
|
||||
ToggleEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleZoneViewers();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SETTINGCHANGE:
|
||||
{
|
||||
if (wparam == SPI_SETWORKAREA)
|
||||
{
|
||||
OnDisplayChange(DisplayChangeType::WorkArea);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_DISPLAYCHANGE:
|
||||
{
|
||||
OnDisplayChange(DisplayChangeType::DisplayChange);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (message == WM_PRIV_VDCHANGED)
|
||||
{
|
||||
OnDisplayChange(DisplayChangeType::VirtualDesktop);
|
||||
}
|
||||
else if (message == WM_PRIV_EDITOR)
|
||||
{
|
||||
if (lparam == static_cast<LPARAM>(EditorExitKind::Exit))
|
||||
{
|
||||
// Don't reload settings if we terminated the editor
|
||||
OnDisplayChange(DisplayChangeType::Editor);
|
||||
}
|
||||
|
||||
{
|
||||
// Clean up the event either way
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_terminateEditorEvent.release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
{
|
||||
if (changeType == DisplayChangeType::VirtualDesktop)
|
||||
{
|
||||
// Explorer persists this value to the registry on a per session basis but only after
|
||||
// the first virtual desktop switch happens. If the user hasn't switched virtual desktops in this session
|
||||
// then this value will be empty. This means loading the first virtual desktop's configuration can be
|
||||
// funky the first time we load up at boot since the user will not have switched virtual desktops yet.
|
||||
std::shared_lock readLock(m_lock);
|
||||
GUID currentVirtualDesktopId{};
|
||||
if (SUCCEEDED(RegistryHelpers::GetCurrentVirtualDesktop(¤tVirtualDesktopId)))
|
||||
{
|
||||
m_currentVirtualDesktopId = currentVirtualDesktopId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Use the previous "Desktop 1" fallback
|
||||
// Need to maintain a map of desktop name to virtual desktop uuid
|
||||
}
|
||||
}
|
||||
|
||||
UpdateZoneWindows();
|
||||
|
||||
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
|
||||
{
|
||||
if (m_settings->GetSettings().displayChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
else if (changeType == DisplayChangeType::VirtualDesktop)
|
||||
{
|
||||
if (m_settings->GetSettings().virtualDesktopChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
else if (changeType == DisplayChangeType::Editor)
|
||||
{
|
||||
if (m_settings->GetSettings().zoneSetChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::ShowZoneEditorForMonitor(HMONITOR monitor) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
bool const activate = MonitorFromPoint(POINT(), MONITOR_DEFAULTTOPRIMARY) == monitor;
|
||||
iter->second->ShowZoneWindow(activate, false /*fadeIn*/);
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
{
|
||||
const bool flash = m_settings->GetSettings().zoneSetChange_flashZones;
|
||||
if (auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, deviceId, virtualDesktopId.get(), flash))
|
||||
{
|
||||
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
if (window != m_windowMoveSize)
|
||||
{
|
||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
iter->second->MoveWindowIntoZoneByIndex(window, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) :
|
||||
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<FancyZones*>(data);
|
||||
strongThis->AddZoneWindow(monitor, deviceId);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_zoneWindowMap.clear();
|
||||
}
|
||||
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(this));
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowsOnDisplayChange() noexcept
|
||||
{
|
||||
auto callback = [](HWND window, LPARAM data) -> BOOL
|
||||
{
|
||||
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);
|
||||
strongThis->MoveWindowIntoZoneByIndex(window, i-1);
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
|
||||
}
|
||||
|
||||
void FancyZones::UpdateDragState(require_write_lock) noexcept
|
||||
{
|
||||
const bool shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
|
||||
m_dragEnabled = m_settings->GetSettings().shiftDrag ? shift : !shift;
|
||||
}
|
||||
|
||||
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
||||
{
|
||||
if (const HWND window = GetForegroundWindow())
|
||||
{
|
||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
iter->second->CycleActiveZoneSet(vkCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
{
|
||||
if (const HWND window = GetForegroundWindow())
|
||||
{
|
||||
if (const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
iter->second->MoveWindowIntoZoneByDirection(window, vkCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
|
||||
{
|
||||
// Only enter move/size if the cursor is in the titlebar.
|
||||
// This prevents resize from triggering zones.
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
TITLEBARINFO titlebarInfo{ sizeof(titlebarInfo) };
|
||||
::GetTitleBarInfo(window, &titlebarInfo);
|
||||
|
||||
// Titlebar height is weird and apps can do custom drag areas.
|
||||
// Give it half of the height of the window to make sure.
|
||||
titlebarInfo.rcTitleBar.bottom += ((windowRect.bottom - windowRect.top) / 2);
|
||||
|
||||
if (PtInRect(&titlebarInfo.rcTitleBar, ptScreen))
|
||||
{
|
||||
m_inMoveSize = true;
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
m_windowMoveSize = window;
|
||||
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
|
||||
if (m_dragEnabled)
|
||||
{
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
|
||||
}
|
||||
else if (m_zoneWindowMoveSize)
|
||||
{
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeEndInternal(HWND window, POINT const& ptScreen, require_write_lock) noexcept
|
||||
{
|
||||
m_inMoveSize = false;
|
||||
m_dragEnabled = false;
|
||||
m_windowMoveSize = nullptr;
|
||||
if (m_zoneWindowMoveSize)
|
||||
{
|
||||
auto zoneWindow = std::move(m_zoneWindowMoveSize);
|
||||
zoneWindow->MoveSizeEnd(window, ptScreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
::RemoveProp(window, ZONE_STAMP);
|
||||
|
||||
wchar_t processPath[MAX_PATH]{};
|
||||
DWORD processPathSize = GetProcessPath(window, processPath, static_cast<DWORD>(MAX_PATH));
|
||||
if (processPathSize > 0)
|
||||
{
|
||||
RegistryHelpers::SaveAppLastZone(window, processPath, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
|
||||
{
|
||||
if (m_inMoveSize)
|
||||
{
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
|
||||
if (m_zoneWindowMoveSize)
|
||||
{
|
||||
// Update the ZoneWindow already handling move/size
|
||||
if (!m_dragEnabled)
|
||||
{
|
||||
// Drag got disabled, tell it to cancel and clear out m_zoneWindowMoveSize
|
||||
auto zoneWindow = std::move(m_zoneWindowMoveSize);
|
||||
zoneWindow->MoveSizeCancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
if (iter->second != m_zoneWindowMoveSize)
|
||||
{
|
||||
// The drag has moved to a different monitor.
|
||||
auto const isDragEnabled = m_zoneWindowMoveSize->IsDragEnabled();
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(m_windowMoveSize, isDragEnabled);
|
||||
}
|
||||
m_zoneWindowMoveSize->MoveSizeUpdate(ptScreen, m_dragEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_dragEnabled)
|
||||
{
|
||||
// We'll get here if the user presses/releases shift while dragging.
|
||||
// Restart the drag on the ZoneWindow that m_windowMoveSize is on
|
||||
MoveSizeStartInternal(m_windowMoveSize, monitor, ptScreen, writeLock);
|
||||
MoveSizeUpdateInternal(monitor, ptScreen, writeLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
|
||||
{
|
||||
return winrt::make_self<FancyZones>(hinstance, settings);
|
||||
}
|
||||
Reference in New Issue
Block a user