mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +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);
|
||||
}
|
||||
BIN
src/modules/fancyzones/lib/FancyZones.h
Normal file
BIN
src/modules/fancyzones/lib/FancyZones.h
Normal file
Binary file not shown.
133
src/modules/fancyzones/lib/FancyZonesLib.vcxproj
Normal file
133
src/modules/fancyzones/lib/FancyZonesLib.vcxproj
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>lib</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>FancyZonesLib</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="FancyZones.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="RegistryHelpers.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Settings.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
<ClInclude Include="Zone.h" />
|
||||
<ClInclude Include="ZoneSet.h" />
|
||||
<ClInclude Include="ZoneWindow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="FancyZones.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="Zone.cpp" />
|
||||
<ClCompile Include="ZoneSet.cpp" />
|
||||
<ClCompile Include="ZoneWindow.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="fancyzones.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
81
src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters
Normal file
81
src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Zone.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZoneSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZoneWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZones.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RegistryHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Zone.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZoneSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ZoneWindow.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FancyZones.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="fancyzones.rc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="fancyzones.def" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
164
src/modules/fancyzones/lib/RegistryHelpers.h
Normal file
164
src/modules/fancyzones/lib/RegistryHelpers.h
Normal file
@@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
|
||||
#include <shlwapi.h>
|
||||
|
||||
namespace RegistryHelpers
|
||||
{
|
||||
static PCWSTR REG_SETTINGS = L"Software\\SuperFancyZones";
|
||||
static PCWSTR APP_ZONE_HISTORY_SUBKEY = L"AppZoneHistory";
|
||||
|
||||
inline PCWSTR GetKey(_In_opt_ PCWSTR monitorId, PWSTR key, size_t keyLength)
|
||||
{
|
||||
if (monitorId)
|
||||
{
|
||||
StringCchPrintf(key, keyLength, L"%s\\%s", REG_SETTINGS, monitorId);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintf(key, keyLength, L"%s", REG_SETTINGS);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
inline HKEY OpenKey(_In_opt_ PCWSTR monitorId)
|
||||
{
|
||||
HKEY hkey;
|
||||
wchar_t key[256];
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
||||
{
|
||||
return hkey;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline HKEY CreateKey(PCWSTR monitorId)
|
||||
{
|
||||
HKEY hkey;
|
||||
wchar_t key[256]{};
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, key, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hkey, nullptr) == ERROR_SUCCESS)
|
||||
{
|
||||
return hkey;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline LSTATUS GetAppLastZone(HWND window, PCWSTR appPath, _Out_ PINT iZoneIndex)
|
||||
{
|
||||
*iZoneIndex = -1;
|
||||
|
||||
LSTATUS res{};
|
||||
if (auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
wchar_t keyPath[256]{};
|
||||
StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"%s\\%s\\%x", REG_SETTINGS, APP_ZONE_HISTORY_SUBKEY, monitor);
|
||||
|
||||
DWORD zoneIndex;
|
||||
DWORD dataType = REG_DWORD;
|
||||
DWORD dataSize = sizeof(DWORD);
|
||||
res = SHRegGetUSValueW(keyPath, appPath, &dataType, &zoneIndex, &dataSize, FALSE, nullptr, 0);
|
||||
if (res == ERROR_SUCCESS)
|
||||
{
|
||||
*iZoneIndex = static_cast<INT>(zoneIndex);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Pass -1 for the zoneIndex to delete the entry from the registry
|
||||
inline void SaveAppLastZone(HWND window, PCWSTR appPath, DWORD zoneIndex)
|
||||
{
|
||||
LSTATUS res{};
|
||||
if (auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
wchar_t keyPath[256]{};
|
||||
StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"%s\\%s\\%x", REG_SETTINGS, APP_ZONE_HISTORY_SUBKEY, monitor);
|
||||
if (zoneIndex == -1)
|
||||
{
|
||||
SHDeleteValueW(HKEY_CURRENT_USER, keyPath, appPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
SHRegSetUSValueW(keyPath, appPath, REG_DWORD, &zoneIndex, sizeof(zoneIndex), SHREGSET_FORCE_HKCU);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetString(PCWSTR uniqueId, PCWSTR setting, PWSTR value, DWORD cbValue)
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(uniqueId, key, ARRAYSIZE(key));
|
||||
SHRegGetUSValueW(key, setting, nullptr, value, &cbValue, FALSE, nullptr, 0);
|
||||
}
|
||||
|
||||
inline void SetString(PCWSTR uniqueId, PCWSTR setting, PCWSTR value)
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(uniqueId, key, ARRAYSIZE(key));
|
||||
SHRegSetUSValueW(key, setting, REG_SZ, value, sizeof(value) * static_cast<DWORD>(wcslen(value)), SHREGSET_FORCE_HKCU);
|
||||
}
|
||||
|
||||
template<typename t>
|
||||
inline void GetValue(PCWSTR monitorId, PCWSTR setting, t* value, DWORD size)
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
SHRegGetUSValueW(key, setting, nullptr, value, &size, FALSE, nullptr, 0);
|
||||
}
|
||||
|
||||
template<typename t>
|
||||
inline void SetValue(PCWSTR monitorId, PCWSTR setting, t value, DWORD size)
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
SHRegSetUSValueW(key, setting, REG_BINARY, &value, size, SHREGSET_FORCE_HKCU);
|
||||
}
|
||||
|
||||
inline void DeleteZoneSet(PCWSTR monitorId, GUID guid)
|
||||
{
|
||||
wil::unique_cotaskmem_string zoneSetId;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(guid, &zoneSetId)))
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
SHDeleteValueW(HKEY_CURRENT_USER, key, zoneSetId.get());
|
||||
}
|
||||
}
|
||||
|
||||
inline void DeleteAllZoneSets(PCWSTR monitorId)
|
||||
{
|
||||
wchar_t key[256]{};
|
||||
GetKey(monitorId, key, ARRAYSIZE(key));
|
||||
SHDeleteKey(HKEY_CURRENT_USER, key);
|
||||
}
|
||||
|
||||
inline HRESULT GetCurrentVirtualDesktop(_Out_ GUID* id)
|
||||
{
|
||||
*id = GUID_NULL;
|
||||
|
||||
DWORD sessionId;
|
||||
ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
|
||||
|
||||
wchar_t sessionKeyPath[256]{};
|
||||
RETURN_IF_FAILED(
|
||||
StringCchPrintfW(
|
||||
sessionKeyPath,
|
||||
ARRAYSIZE(sessionKeyPath),
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops",
|
||||
sessionId));
|
||||
wil::unique_hkey key{};
|
||||
GUID value{};
|
||||
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
|
||||
{
|
||||
DWORD size = sizeof(value);
|
||||
if (RegQueryValueExW(key.get(), L"CurrentVirtualDesktop", 0, nullptr, reinterpret_cast<BYTE*>(&value), &size) == ERROR_SUCCESS)
|
||||
{
|
||||
*id = value;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
155
src/modules/fancyzones/lib/Settings.cpp
Normal file
155
src/modules/fancyzones/lib/Settings.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "pch.h"
|
||||
#include <common/settings_objects.h>
|
||||
|
||||
struct FancyZonesSettings : winrt::implements<FancyZonesSettings, IFancyZonesSettings>
|
||||
{
|
||||
public:
|
||||
FancyZonesSettings(HINSTANCE hinstance, PCWSTR name)
|
||||
: m_hinstance(hinstance)
|
||||
, m_name(name)
|
||||
{
|
||||
LoadSettings(name, true /*fromFile*/);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback.attach(callback); }
|
||||
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
|
||||
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
|
||||
IFACEMETHODIMP_(void) CallCustomAction(PCWSTR action) noexcept;
|
||||
IFACEMETHODIMP_(Settings) GetSettings() noexcept { return m_settings; }
|
||||
|
||||
private:
|
||||
void LoadSettings(PCWSTR config, bool fromFile) noexcept;
|
||||
void SaveSettings() noexcept;
|
||||
|
||||
winrt::com_ptr<IFancyZonesCallback> m_callback;
|
||||
const HINSTANCE m_hinstance;
|
||||
PCWSTR m_name{};
|
||||
|
||||
Settings m_settings;
|
||||
|
||||
struct
|
||||
{
|
||||
PCWSTR name;
|
||||
bool* value;
|
||||
int resourceId;
|
||||
} m_configBools[8] = {
|
||||
{ L"fancyzones_shiftDrag", &m_settings.shiftDrag, IDS_SETTING_DESCRIPTION_SHIFTDRAG },
|
||||
{ L"fancyzones_overrideSnapHotkeys", &m_settings.overrideSnapHotkeys, IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS },
|
||||
{ L"fancyzones_zoneSetChange_flashZones", &m_settings.zoneSetChange_flashZones, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES },
|
||||
{ L"fancyzones_displayChange_moveWindows", &m_settings.displayChange_moveWindows, IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_zoneSetChange_moveWindows", &m_settings.zoneSetChange_moveWindows, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_virtualDesktopChange_moveWindows", &m_settings.virtualDesktopChange_moveWindows, IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_appLastZone_moveWindows", &m_settings.appLastZone_moveWindows, IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS },
|
||||
{ L"fancyzones_use_standalone_editor", &m_settings.use_standalone_editor, IDS_SETTING_DESCRIPTION_USE_STANDALONE_EDITOR },
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
PCWSTR name;
|
||||
std::wstring* value;
|
||||
int resourceId;
|
||||
} m_configStrings[1] = {
|
||||
{ L"fancyzones_zoneHighlightColor", &m_settings.zoneHightlightColor, IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR },
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
IFACEMETHODIMP_(bool) FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_size) noexcept
|
||||
{
|
||||
PowerToysSettings::Settings settings(m_hinstance, m_name);
|
||||
|
||||
// Pass a string literal or a resource id to Settings::set_description().
|
||||
settings.set_description(IDS_SETTING_DESCRIPTION);
|
||||
settings.set_icon_key(L"pt-fancy-zones");
|
||||
settings.set_overview_link(L"https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/README.md");
|
||||
settings.set_video_link(L"https://youtu.be/rTtGzZYAXgY");
|
||||
|
||||
// Add a custom action property. When using this settings type, the "PowertoyModuleIface::call_custom_action()"
|
||||
// method should be overriden as well.
|
||||
settings.add_custom_action(
|
||||
L"ToggledFZEditor", // action name.
|
||||
IDS_SETTING_LAUNCH_EDITOR_LABEL,
|
||||
IDS_SETTING_LAUNCH_EDITOR_BUTTON,
|
||||
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION
|
||||
);
|
||||
|
||||
for (auto const& setting : m_configBools)
|
||||
{
|
||||
settings.add_bool_toogle(setting.name, setting.resourceId, *setting.value);
|
||||
}
|
||||
|
||||
for (auto const& setting : m_configStrings)
|
||||
{
|
||||
settings.add_color_picker(setting.name, setting.resourceId, *setting.value);
|
||||
}
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) FancyZonesSettings::SetConfig(PCWSTR config) noexcept try
|
||||
{
|
||||
LoadSettings(config, false /*fromFile*/);
|
||||
SaveSettings();
|
||||
Trace::SettingsChanged(m_settings);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
IFACEMETHODIMP_(void) FancyZonesSettings::CallCustomAction(PCWSTR action) noexcept try
|
||||
{
|
||||
// Parse the action values, including name.
|
||||
PowerToysSettings::CustomActionObject action_object =
|
||||
PowerToysSettings::CustomActionObject::from_json_string(action);
|
||||
|
||||
if (action_object.get_name() == L"ToggledFZEditor")
|
||||
{
|
||||
m_callback->ToggleEditor();
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues values = fromFile ?
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(m_name) :
|
||||
PowerToysSettings::PowerToyValues::from_json_string(config);
|
||||
|
||||
for (auto const& setting : m_configBools)
|
||||
{
|
||||
if (values.is_bool_value(setting.name))
|
||||
{
|
||||
*setting.value = values.get_bool_value(setting.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& setting : m_configStrings)
|
||||
{
|
||||
if (values.is_string_value(setting.name))
|
||||
{
|
||||
*setting.value = values.get_string_value(setting.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void FancyZonesSettings::SaveSettings() noexcept try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues values(m_name);
|
||||
|
||||
for (auto const& setting : m_configBools)
|
||||
{
|
||||
values.add_property(setting.name, *setting.value);
|
||||
}
|
||||
|
||||
for (auto const& setting : m_configStrings)
|
||||
{
|
||||
values.add_property(setting.name, *setting.value);
|
||||
}
|
||||
|
||||
values.save_to_settings_file();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
winrt::com_ptr<IFancyZonesSettings> MakeFancyZonesSettings(HINSTANCE hinstance, PCWSTR name) noexcept
|
||||
{
|
||||
return winrt::make_self<FancyZonesSettings>(hinstance, name);
|
||||
}
|
||||
28
src/modules/fancyzones/lib/Settings.h
Normal file
28
src/modules/fancyzones/lib/Settings.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#define ZONE_STAMP L"FancyZones_zone"
|
||||
|
||||
struct Settings
|
||||
{
|
||||
// The values specified here are the defaults.
|
||||
bool shiftDrag = true;
|
||||
bool displayChange_moveWindows = false;
|
||||
bool virtualDesktopChange_moveWindows = false;
|
||||
bool zoneSetChange_flashZones = true;
|
||||
bool zoneSetChange_moveWindows = false;
|
||||
bool overrideSnapHotkeys = false;
|
||||
bool appLastZone_moveWindows = false;
|
||||
bool use_standalone_editor = true;
|
||||
std::wstring zoneHightlightColor = L"#0078D7";
|
||||
};
|
||||
|
||||
interface __declspec(uuid("{BA4E77C4-6F44-4C5D-93D3-CBDE880495C2}")) IFancyZonesSettings : public IUnknown
|
||||
{
|
||||
IFACEMETHOD_(void, SetCallback)(interface IFancyZonesCallback* callback) = 0;
|
||||
IFACEMETHOD_(bool, GetConfig)(_Out_ PWSTR buffer, _Out_ int *buffer_size) = 0;
|
||||
IFACEMETHOD_(void, SetConfig)(PCWSTR config) = 0;
|
||||
IFACEMETHOD_(void, CallCustomAction)(PCWSTR action) = 0;
|
||||
IFACEMETHOD_(Settings, GetSettings)() = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IFancyZonesSettings> MakeFancyZonesSettings(HINSTANCE hinstance, PCWSTR config) noexcept;
|
||||
95
src/modules/fancyzones/lib/Zone.cpp
Normal file
95
src/modules/fancyzones/lib/Zone.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "pch.h"
|
||||
|
||||
|
||||
struct Zone : winrt::implements<Zone, IZone>
|
||||
{
|
||||
public:
|
||||
Zone(RECT zoneRect) :
|
||||
m_zoneRect(zoneRect)
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(RECT) GetZoneRect() noexcept { return m_zoneRect; }
|
||||
IFACEMETHODIMP_(bool) IsEmpty() noexcept { return m_windows.empty(); };
|
||||
IFACEMETHODIMP_(bool) ContainsWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(void) AddWindowToZone(HWND window, HWND zoneWindow, bool stampZone) noexcept;
|
||||
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
|
||||
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
|
||||
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
|
||||
|
||||
private:
|
||||
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
|
||||
void StampZone(HWND window, bool stamp) noexcept;
|
||||
|
||||
RECT m_zoneRect{};
|
||||
size_t m_id{};
|
||||
std::map<HWND, RECT> m_windows{};
|
||||
};
|
||||
|
||||
IFACEMETHODIMP_(bool) Zone::ContainsWindow(HWND window) noexcept
|
||||
{
|
||||
return (m_windows.find(window) != m_windows.end());
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) Zone::AddWindowToZone(HWND window, HWND zoneWindow, bool stampZone) noexcept
|
||||
{
|
||||
WINDOWPLACEMENT placement;
|
||||
::GetWindowPlacement(window, &placement);
|
||||
::GetWindowRect(window, &placement.rcNormalPosition);
|
||||
m_windows.emplace(std::pair<HWND, RECT>(window, placement.rcNormalPosition));
|
||||
|
||||
SizeWindowToZone(window, zoneWindow);
|
||||
if (stampZone)
|
||||
{
|
||||
StampZone(window, true);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize) noexcept
|
||||
{
|
||||
auto iter = m_windows.find(window);
|
||||
if (iter != m_windows.end())
|
||||
{
|
||||
m_windows.erase(iter);
|
||||
StampZone(window, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
// Take care of 1px border
|
||||
RECT zoneRect = m_zoneRect;
|
||||
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
RECT frameRect{};
|
||||
// Failure is expected on down level systems.
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
|
||||
{
|
||||
zoneRect.bottom -= (frameRect.bottom - windowRect.bottom);
|
||||
zoneRect.right -= (frameRect.right - windowRect.right);
|
||||
zoneRect.left -= (frameRect.left - windowRect.left);
|
||||
}
|
||||
|
||||
// Map to screen coords
|
||||
MapWindowRect(zoneWindow, nullptr, &zoneRect);
|
||||
::SetWindowPos(window, nullptr, zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left, zoneRect.bottom - zoneRect.top, SWP_NOZORDER | SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
||||
}
|
||||
|
||||
void Zone::StampZone(HWND window, bool stamp) noexcept
|
||||
{
|
||||
if (stamp)
|
||||
{
|
||||
SetProp(window, ZONE_STAMP, reinterpret_cast<HANDLE>(m_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveProp(window, ZONE_STAMP);
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(RECT zoneRect) noexcept
|
||||
{
|
||||
return winrt::make_self<Zone>(zoneRect);
|
||||
}
|
||||
14
src/modules/fancyzones/lib/Zone.h
Normal file
14
src/modules/fancyzones/lib/Zone.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : public IUnknown
|
||||
{
|
||||
IFACEMETHOD_(RECT, GetZoneRect)() = 0;
|
||||
IFACEMETHOD_(bool, IsEmpty)() = 0;
|
||||
IFACEMETHOD_(bool, ContainsWindow)(HWND window) = 0;
|
||||
IFACEMETHOD_(void, AddWindowToZone)(HWND window, HWND zoneWindow, bool stampZone) = 0;
|
||||
IFACEMETHOD_(void, RemoveWindowFromZone)(HWND window, bool restoreSize) = 0;
|
||||
IFACEMETHOD_(void, SetId)(size_t id) = 0;
|
||||
IFACEMETHOD_(size_t, Id)() = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(RECT zoneRect) noexcept;
|
||||
385
src/modules/fancyzones/lib/ZoneSet.cpp
Normal file
385
src/modules/fancyzones/lib/ZoneSet.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
#include "pch.h"
|
||||
|
||||
struct ZoneSet : winrt::implements<ZoneSet, IZoneSet>
|
||||
{
|
||||
public:
|
||||
ZoneSet(ZoneSetConfig const& config) : m_config(config)
|
||||
{
|
||||
if (config.ZoneCount > 0)
|
||||
{
|
||||
InitialPopulateZones();
|
||||
}
|
||||
}
|
||||
|
||||
ZoneSet(ZoneSetConfig const& config, std::vector<winrt::com_ptr<IZone>> zones) :
|
||||
m_config(config),
|
||||
m_zones(zones)
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(GUID) Id() noexcept { return m_config.Id; }
|
||||
IFACEMETHODIMP_(WORD) LayoutId() noexcept { return m_config.LayoutId; }
|
||||
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone, bool front) noexcept;
|
||||
IFACEMETHODIMP RemoveZone(winrt::com_ptr<IZone> zone) noexcept;
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneFromPoint(POINT pt) noexcept;
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneFromWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(int) GetZoneIndexFromWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>) GetZones() noexcept { return m_zones; }
|
||||
IFACEMETHODIMP_(ZoneSetLayout) GetLayout() noexcept { return m_config.Layout; }
|
||||
IFACEMETHODIMP_(int) GetInnerPadding() noexcept { return m_config.PaddingInner; }
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZoneSet>) MakeCustomClone() noexcept;
|
||||
IFACEMETHODIMP_(void) Save() noexcept;
|
||||
IFACEMETHODIMP_(void) MoveZoneToFront(winrt::com_ptr<IZone> zone) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveZoneToBack(winrt::com_ptr<IZone> zone) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(void) MoveSizeEnd(HWND window, HWND zoneWindow, POINT ptClient) noexcept;
|
||||
|
||||
private:
|
||||
void InitialPopulateZones() noexcept;
|
||||
void GenerateGridZones(MONITORINFO const& mi) noexcept;
|
||||
void DoGridLayout(SIZE const& zoneArea, int numCols, int numRows) noexcept;
|
||||
void GenerateFocusZones(MONITORINFO const& mi) noexcept;
|
||||
void StampZone(HWND window, _In_opt_ winrt::com_ptr<IZone> zone) noexcept;
|
||||
|
||||
std::vector<winrt::com_ptr<IZone>> m_zones;
|
||||
ZoneSetConfig m_config;
|
||||
};
|
||||
|
||||
IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone, bool front) noexcept
|
||||
{
|
||||
// XXXX: need to reorder ids when inserting...
|
||||
if (front)
|
||||
{
|
||||
m_zones.insert(m_zones.begin(), zone);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_zones.emplace_back(zone);
|
||||
}
|
||||
|
||||
// Important not to set Id 0 since we store it in the HWND using SetProp.
|
||||
// SetProp(0) doesn't really work.
|
||||
zone->SetId(m_zones.size());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ZoneSet::RemoveZone(winrt::com_ptr<IZone> zone) noexcept
|
||||
{
|
||||
auto iter = std::find(m_zones.begin(), m_zones.end(), zone);
|
||||
if (iter != m_zones.end())
|
||||
{
|
||||
m_zones.erase(iter);
|
||||
return S_OK;
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
||||
{
|
||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
{
|
||||
if (PtInRect(&zone->GetZoneRect(), pt))
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>) ZoneSet::ZoneFromWindow(HWND window) noexcept
|
||||
{
|
||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
{
|
||||
if (zone->ContainsWindow(window))
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZoneSet>) ZoneSet::MakeCustomClone() noexcept
|
||||
{
|
||||
if (SUCCEEDED_LOG(CoCreateGuid(&m_config.Id)))
|
||||
{
|
||||
m_config.IsCustom = true;
|
||||
return winrt::make_self<ZoneSet>(m_config, m_zones);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::Save() noexcept
|
||||
{
|
||||
size_t const zoneCount = m_zones.size();
|
||||
if (zoneCount == 0)
|
||||
{
|
||||
RegistryHelpers::DeleteZoneSet(m_config.ResolutionKey, m_config.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneSetPersistedData data{};
|
||||
data.LayoutId = m_config.LayoutId;
|
||||
data.ZoneCount = static_cast<DWORD>(zoneCount);
|
||||
data.Layout = m_config.Layout;
|
||||
data.PaddingInner = m_config.PaddingInner;
|
||||
data.PaddingOuter = m_config.PaddingOuter;
|
||||
|
||||
int i = 0;
|
||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone = iter->as<IZone>();
|
||||
CopyRect(&data.Zones[i++], &zone->GetZoneRect());
|
||||
}
|
||||
|
||||
wil::unique_cotaskmem_string guid;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(m_config.Id, &guid)))
|
||||
{
|
||||
if (wil::unique_hkey hkey{ RegistryHelpers::CreateKey(m_config.ResolutionKey) })
|
||||
{
|
||||
RegSetValueExW(hkey.get(), guid.get(), 0, REG_BINARY, reinterpret_cast<BYTE*>(&data), sizeof(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::MoveZoneToFront(winrt::com_ptr<IZone> zone) noexcept
|
||||
{
|
||||
auto iter = std::find(m_zones.begin(), m_zones.end(), zone);
|
||||
if (iter != m_zones.end())
|
||||
{
|
||||
std::rotate(m_zones.begin(), iter, iter + 1);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::MoveZoneToBack(winrt::com_ptr<IZone> zone) noexcept
|
||||
{
|
||||
auto iter = std::find(m_zones.begin(), m_zones.end(), zone);
|
||||
if (iter != m_zones.end())
|
||||
{
|
||||
std::rotate(iter, iter + 1, m_zones.end());
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(int) ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
||||
{
|
||||
int zoneIndex = 0;
|
||||
for (auto iter = m_zones.begin(); iter != m_zones.end(); iter++, zoneIndex++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
{
|
||||
if (zone->ContainsWindow(window))
|
||||
{
|
||||
return zoneIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
|
||||
{
|
||||
if (index >= static_cast<int>(m_zones.size()))
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if (index < m_zones.size())
|
||||
{
|
||||
if (auto zone = m_zones.at(index))
|
||||
{
|
||||
zone->AddWindowToZone(window, windowZone, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCode) noexcept
|
||||
{
|
||||
winrt::com_ptr<IZone> oldZone;
|
||||
winrt::com_ptr<IZone> newZone;
|
||||
|
||||
auto iter = std::find(m_zones.begin(), m_zones.end(), ZoneFromWindow(window));
|
||||
if (iter == m_zones.end())
|
||||
{
|
||||
iter = (vkCode == VK_RIGHT) ? m_zones.begin() : m_zones.end() - 1;
|
||||
}
|
||||
else if (oldZone = iter->as<IZone>())
|
||||
{
|
||||
if (vkCode == VK_LEFT)
|
||||
{
|
||||
if (iter == m_zones.begin())
|
||||
{
|
||||
iter = m_zones.end();
|
||||
}
|
||||
iter--;
|
||||
}
|
||||
else if (vkCode == VK_RIGHT)
|
||||
{
|
||||
iter++;
|
||||
if (iter == m_zones.end())
|
||||
{
|
||||
iter = m_zones.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newZone = iter->as<IZone>())
|
||||
{
|
||||
if (oldZone)
|
||||
{
|
||||
oldZone->RemoveWindowFromZone(window, false);
|
||||
}
|
||||
newZone->AddWindowToZone(window, windowZone, true);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) ZoneSet::MoveSizeEnd(HWND window, HWND zoneWindow, POINT ptClient) noexcept
|
||||
{
|
||||
if (auto zoneDrop = ZoneFromWindow(window))
|
||||
{
|
||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||
}
|
||||
|
||||
if (auto zone = ZoneFromPoint(ptClient))
|
||||
{
|
||||
zone->AddWindowToZone(window, zoneWindow, true);
|
||||
|
||||
POINT pointAdjustedScreen = ptClient;
|
||||
MapWindowPoints(zoneWindow, nullptr, &pointAdjustedScreen, 1);
|
||||
SetCursorPos(pointAdjustedScreen.x, pointAdjustedScreen.y);
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneSet::InitialPopulateZones() noexcept
|
||||
{
|
||||
// TODO: reconcile the pregenerated FZ layouts with the editor
|
||||
|
||||
MONITORINFO mi{};
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfoW(m_config.Monitor, &mi))
|
||||
{
|
||||
if ((m_config.Layout == ZoneSetLayout::Grid) || (m_config.Layout == ZoneSetLayout::Row))
|
||||
{
|
||||
GenerateGridZones(mi);
|
||||
}
|
||||
else if (m_config.Layout == ZoneSetLayout::Focus)
|
||||
{
|
||||
GenerateFocusZones(mi);
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneSet::GenerateGridZones(MONITORINFO const& mi) noexcept
|
||||
{
|
||||
Rect workArea(mi.rcWork);
|
||||
|
||||
int numCols, numRows;
|
||||
if (m_config.Layout == ZoneSetLayout::Grid)
|
||||
{
|
||||
switch (m_config.ZoneCount)
|
||||
{
|
||||
case 1: numCols = 1; numRows = 1; break;
|
||||
case 2: numCols = 2; numRows = 1; break;
|
||||
case 3: numCols = 2; numRows = 2; break;
|
||||
case 4: numCols = 2; numRows = 2; break;
|
||||
case 5: numCols = 3; numRows = 3; break;
|
||||
case 6: numCols = 3; numRows = 3; break;
|
||||
case 7: numCols = 3; numRows = 3; break;
|
||||
case 8: numCols = 3; numRows = 3; break;
|
||||
case 9: numCols = 3; numRows = 3; break;
|
||||
}
|
||||
|
||||
if ((m_config.ZoneCount == 2) && (workArea.height() > workArea.width()))
|
||||
{
|
||||
numCols = 1;
|
||||
numRows = 2;
|
||||
}
|
||||
}
|
||||
else if (m_config.Layout == ZoneSetLayout::Row)
|
||||
{
|
||||
numCols = m_config.ZoneCount;
|
||||
numRows = 1;
|
||||
}
|
||||
|
||||
SIZE const zoneArea = {
|
||||
workArea.width() - ((m_config.PaddingOuter * 2) + (m_config.PaddingInner * (numCols - 1))),
|
||||
workArea.height() - ((m_config.PaddingOuter * 2) + (m_config.PaddingInner * (numRows - 1)))
|
||||
};
|
||||
|
||||
DoGridLayout(zoneArea, numCols, numRows);
|
||||
}
|
||||
|
||||
void ZoneSet::DoGridLayout(SIZE const& zoneArea, int numCols, int numRows) noexcept
|
||||
{
|
||||
auto x = m_config.PaddingOuter;
|
||||
auto y = m_config.PaddingOuter;
|
||||
auto const zoneWidth = (zoneArea.cx / numCols);
|
||||
auto const zoneHeight = (zoneArea.cy / numRows);
|
||||
for (auto i = 1; i <= m_config.ZoneCount; i++)
|
||||
{
|
||||
auto col = numCols - (i % numCols);
|
||||
RECT const zoneRect = { x, y, x + zoneWidth, y + zoneHeight };
|
||||
AddZone(MakeZone(zoneRect), false);
|
||||
|
||||
x += zoneWidth + m_config.PaddingInner;
|
||||
if (col == numCols)
|
||||
{
|
||||
x = m_config.PaddingOuter;
|
||||
y += zoneHeight + m_config.PaddingInner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneSet::GenerateFocusZones(MONITORINFO const& mi) noexcept
|
||||
{
|
||||
Rect const workArea(mi.rcWork);
|
||||
|
||||
SIZE const workHalf = { workArea.width() / 2, workArea.height() / 2 };
|
||||
RECT const safeZone = {
|
||||
m_config.PaddingOuter,
|
||||
m_config.PaddingOuter,
|
||||
workArea.width() - m_config.PaddingOuter,
|
||||
workArea.height() - m_config.PaddingOuter
|
||||
};
|
||||
|
||||
int const width = min(1920, workArea.width() * 60 / 100);
|
||||
int const height = min(1200, workArea.height() * 75 / 100);
|
||||
int const halfWidth = width / 2;
|
||||
int const halfHeight = height / 2;
|
||||
int x = workHalf.cx - halfWidth;
|
||||
int y = workHalf.cy - halfHeight;
|
||||
|
||||
RECT const focusRect = { x, y, x + width, y + height };
|
||||
AddZone(MakeZone(focusRect), false);
|
||||
|
||||
for (auto i = 2; i <= m_config.ZoneCount; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 2: x = focusRect.right - halfWidth; y = focusRect.top + m_config.PaddingInner; break; // right
|
||||
case 3: x = focusRect.left - halfWidth; y = focusRect.top + (m_config.PaddingInner * 2); break; // left
|
||||
case 4: x = focusRect.left + m_config.PaddingInner; y = focusRect.top - halfHeight; break; // up
|
||||
case 5: x = focusRect.left - m_config.PaddingInner; y = focusRect.bottom - halfHeight; break; // down
|
||||
}
|
||||
|
||||
// Bound into safe zone
|
||||
x = min(safeZone.right - width, max(safeZone.left, x));
|
||||
y = min(safeZone.bottom - height, max(safeZone.top, y));
|
||||
|
||||
RECT const zoneRect = { x, y, x + width, y + height };
|
||||
AddZone(MakeZone(zoneRect), false);
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept
|
||||
{
|
||||
return winrt::make_self<ZoneSet>(config);
|
||||
}
|
||||
79
src/modules/fancyzones/lib/ZoneSet.h
Normal file
79
src/modules/fancyzones/lib/ZoneSet.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "Zone.h"
|
||||
|
||||
enum class ZoneSetLayout
|
||||
{
|
||||
Grid,
|
||||
Row,
|
||||
Focus,
|
||||
Custom
|
||||
};
|
||||
|
||||
interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : public IUnknown
|
||||
{
|
||||
IFACEMETHOD_(GUID, Id)() = 0;
|
||||
IFACEMETHOD_(WORD, LayoutId)() = 0;
|
||||
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone, bool front) = 0;
|
||||
IFACEMETHOD(RemoveZone)(winrt::com_ptr<IZone> zone) = 0;
|
||||
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
|
||||
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromWindow)(HWND window) = 0;
|
||||
IFACEMETHOD_(int, GetZoneIndexFromWindow)(HWND window) = 0;
|
||||
IFACEMETHOD_(std::vector<winrt::com_ptr<IZone>>, GetZones)() = 0;
|
||||
IFACEMETHOD_(ZoneSetLayout, GetLayout)() = 0;
|
||||
IFACEMETHOD_(int, GetInnerPadding)() = 0;
|
||||
IFACEMETHOD_(winrt::com_ptr<IZoneSet>, MakeCustomClone)() = 0;
|
||||
IFACEMETHOD_(void, Save)() = 0;
|
||||
IFACEMETHOD_(void, MoveZoneToFront)(winrt::com_ptr<IZone> zone) = 0;
|
||||
IFACEMETHOD_(void, MoveZoneToBack)(winrt::com_ptr<IZone> zone) = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, HWND zoneWindow, DWORD vkCode) = 0;
|
||||
IFACEMETHOD_(void, MoveSizeEnd)(HWND window, HWND zoneWindow, POINT ptClient) = 0;
|
||||
};
|
||||
|
||||
#define VERSION_PERSISTEDDATA 0x0000F00D
|
||||
struct ZoneSetPersistedData
|
||||
{
|
||||
DWORD Version{VERSION_PERSISTEDDATA};
|
||||
WORD LayoutId{};
|
||||
DWORD ZoneCount{};
|
||||
ZoneSetLayout Layout{};
|
||||
DWORD PaddingInner{};
|
||||
DWORD PaddingOuter{};
|
||||
RECT Zones[40]{};
|
||||
};
|
||||
|
||||
struct ZoneSetConfig
|
||||
{
|
||||
ZoneSetConfig(
|
||||
GUID id,
|
||||
WORD layoutId,
|
||||
HMONITOR monitor,
|
||||
PCWSTR resolutionKey,
|
||||
ZoneSetLayout layout,
|
||||
int zoneCount,
|
||||
int paddingOuter,
|
||||
int paddingInner) noexcept :
|
||||
Id(id),
|
||||
LayoutId(layoutId),
|
||||
Monitor(monitor),
|
||||
ResolutionKey(resolutionKey),
|
||||
Layout(layout),
|
||||
ZoneCount(zoneCount),
|
||||
PaddingOuter(paddingOuter),
|
||||
PaddingInner(paddingInner)
|
||||
{
|
||||
}
|
||||
|
||||
GUID Id{};
|
||||
WORD LayoutId{};
|
||||
HMONITOR Monitor{};
|
||||
PCWSTR ResolutionKey{};
|
||||
ZoneSetLayout Layout{};
|
||||
int ZoneCount{};
|
||||
int PaddingOuter{};
|
||||
int PaddingInner{};
|
||||
bool IsCustom{};
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept;
|
||||
1308
src/modules/fancyzones/lib/ZoneWindow.cpp
Normal file
1308
src/modules/fancyzones/lib/ZoneWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
23
src/modules/fancyzones/lib/ZoneWindow.h
Normal file
23
src/modules/fancyzones/lib/ZoneWindow.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "FancyZones.h"
|
||||
|
||||
interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow : public IUnknown
|
||||
{
|
||||
IFACEMETHOD(ShowZoneWindow)(bool activate, bool fadeIn) = 0;
|
||||
IFACEMETHOD(HideZoneWindow)() = 0;
|
||||
IFACEMETHOD(MoveSizeEnter)(HWND window, bool dragEnabled) = 0;
|
||||
IFACEMETHOD(MoveSizeUpdate)(POINT const& ptScreen, bool dragEnabled) = 0;
|
||||
IFACEMETHOD(MoveSizeEnd)(HWND window, POINT const& ptScreen) = 0;
|
||||
IFACEMETHOD(MoveSizeCancel)() = 0;
|
||||
IFACEMETHOD_(bool, IsDragEnabled)() = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, DWORD vkCode) = 0;
|
||||
IFACEMETHOD_(void, CycleActiveZoneSet)(DWORD vkCode) = 0;
|
||||
IFACEMETHOD_(void, SaveWindowProcessToZoneIndex)(HWND window) = 0;
|
||||
IFACEMETHOD_(std::wstring, DeviceId)() = 0;
|
||||
IFACEMETHOD_(std::wstring, UniqueId)() = 0;
|
||||
IFACEMETHOD_(IZoneSet*, ActiveZoneSet)() = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
|
||||
PCWSTR deviceId, PCWSTR virtualDesktopId, bool flashZones) noexcept;
|
||||
BIN
src/modules/fancyzones/lib/fancyzones.rc
Normal file
BIN
src/modules/fancyzones/lib/fancyzones.rc
Normal file
Binary file not shown.
4
src/modules/fancyzones/lib/packages.config
Normal file
4
src/modules/fancyzones/lib/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.190716.2" targetFramework="native" />
|
||||
</packages>
|
||||
5
src/modules/fancyzones/lib/pch.cpp
Normal file
5
src/modules/fancyzones/lib/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
||||
33
src/modules/fancyzones/lib/pch.h
Normal file
33
src/modules/fancyzones/lib/pch.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "resource.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#include <Unknwn.h>
|
||||
#include <winrt/base.h>
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <dwmapi.h>
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <shellapi.h>
|
||||
#include <strsafe.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
#include <wil\resource.h>
|
||||
#include <wil\result.h>
|
||||
#include <windows.foundation.h>
|
||||
#include <psapi.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "Settings.h"
|
||||
#include "FancyZones.h"
|
||||
#include "ZoneWindow.h"
|
||||
#include "ZoneSet.h"
|
||||
#include "Zone.h"
|
||||
#include "util.h"
|
||||
#include "RegistryHelpers.h"
|
||||
|
||||
#pragma comment(lib, "windowsapp")
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
using namespace ::winrt;
|
||||
}
|
||||
13
src/modules/fancyzones/lib/resource.h
Normal file
13
src/modules/fancyzones/lib/resource.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#define IDS_SETTING_DESCRIPTION_SHIFTDRAG 101
|
||||
#define IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS 102
|
||||
#define IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS 103
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS 104
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES 105
|
||||
#define IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS 106
|
||||
#define IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR 107
|
||||
#define IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS 108
|
||||
#define IDS_SETTING_DESCRIPTION_USE_STANDALONE_EDITOR 109
|
||||
#define IDS_SETTING_DESCRIPTION 110
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_LABEL 111
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_BUTTON 112
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION 113
|
||||
164
src/modules/fancyzones/lib/trace.cpp
Normal file
164
src/modules/fancyzones/lib/trace.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
struct ZoneSetInfo
|
||||
{
|
||||
size_t NumberOfZones = 0;
|
||||
size_t NumberOfWindows = 0;
|
||||
ZoneSetLayout Layout = ZoneSetLayout::Custom;
|
||||
};
|
||||
|
||||
ZoneSetInfo GetZoneSetInfo(_In_opt_ winrt::com_ptr<IZoneSet> set) noexcept
|
||||
{
|
||||
ZoneSetInfo info;
|
||||
if (set)
|
||||
{
|
||||
auto zones = set->GetZones();
|
||||
info.NumberOfZones = zones.size();
|
||||
info.Layout = set->GetLayout();
|
||||
info.NumberOfWindows = std::count_if(zones.cbegin(), zones.cend(), [&](winrt::com_ptr<IZone> zone)
|
||||
{
|
||||
return !zone->IsEmpty();
|
||||
});
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
void Trace::RegisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::FancyZones::EnableFancyZones(bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::EnableFancyZones",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::ToggleZoneViewers(bool visible) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::ToggleZoneViewers",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(visible, "Visible"));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::OnKeyDown(DWORD vk, bool win, bool control, bool inMoveSize) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::OnKeyDown",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(vk, "Hotkey"),
|
||||
TraceLoggingBoolean(win, "WindowsKey"),
|
||||
TraceLoggingBoolean(control, "ControlKey"),
|
||||
TraceLoggingBoolean(inMoveSize, "InMoveSize"));
|
||||
}
|
||||
|
||||
void Trace::SettingsChanged(const Settings& settings) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::SettingsChanged",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(settings.shiftDrag, "Shift drag"),
|
||||
TraceLoggingBoolean(settings.displayChange_moveWindows, "Move Windows On Display Change"),
|
||||
TraceLoggingBoolean(settings.virtualDesktopChange_moveWindows, "Move Windows On Virtual Desktop Change"),
|
||||
TraceLoggingBoolean(settings.zoneSetChange_flashZones, "Flash zones On Zone Set Change"),
|
||||
TraceLoggingBoolean(settings.zoneSetChange_moveWindows, "Move Windows On Zone Set Change"),
|
||||
TraceLoggingBoolean(settings.overrideSnapHotkeys, "Override snap hot keys"),
|
||||
TraceLoggingWideString(settings.zoneHightlightColor.c_str(), "Zone highlight color"));
|
||||
}
|
||||
|
||||
void Trace::VirtualDesktopChanged() noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::VirtualDesktopChanged",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::ZoneWindow::KeyUp(WPARAM wParam, bool isEditorMode) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::ZoneWindowKeyUp",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(wParam, "Keyboard value"),
|
||||
TraceLoggingBoolean(isEditorMode, "Editor Mode"));
|
||||
}
|
||||
|
||||
void Trace::ZoneWindow::MoveSizeEnd(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::MoveSizeEnd",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), "Active Set"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, "NumberOfZones"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, "NumberOfWindows"),
|
||||
TraceLoggingValue(static_cast<int>(zoneInfo.Layout), "LayoutKind"));
|
||||
}
|
||||
|
||||
void Trace::ZoneWindow::CycleActiveZoneSet(_In_opt_ winrt::com_ptr<IZoneSet> activeSet, InputMode mode) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones::Event::CycleActiveZoneSet",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), "Active Set"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, "NumberOfZones"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, "NumberOfWindows"),
|
||||
TraceLoggingValue(static_cast<int>(zoneInfo.Layout), "LayoutKind"),
|
||||
TraceLoggingValue(static_cast<int>(mode), "InputMode"));
|
||||
}
|
||||
|
||||
void Trace::ZoneWindow::EditorModeActivity::Start() noexcept
|
||||
{
|
||||
m_activity = TraceLoggingActivity<g_hProvider, PROJECT_KEYWORD_MEASURE>();
|
||||
TraceLoggingWriteStart(
|
||||
m_activity.value(),
|
||||
"FancyZones::Activity::EditorMode",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance));
|
||||
}
|
||||
|
||||
void Trace::ZoneWindow::EditorModeActivity::Stop(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept
|
||||
{
|
||||
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
||||
TraceLoggingWriteStop(
|
||||
m_activity.value(),
|
||||
"FancyZones::Activity::EditorMode",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), "Active Set"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfZones, "NumberOfZones"),
|
||||
TraceLoggingValue(zoneInfo.NumberOfWindows, "NumberOfWindows"),
|
||||
TraceLoggingValue(static_cast<int>(zoneInfo.Layout), "LayoutKind"));
|
||||
m_activity.reset();
|
||||
}
|
||||
45
src/modules/fancyzones/lib/trace.h
Normal file
45
src/modules/fancyzones/lib/trace.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
struct Settings;
|
||||
interface IZoneSet;
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider() noexcept;
|
||||
static void UnregisterProvider() noexcept;
|
||||
|
||||
class FancyZones
|
||||
{
|
||||
public:
|
||||
static void EnableFancyZones(bool enabled) noexcept;
|
||||
static void ToggleZoneViewers(bool visible) noexcept;
|
||||
static void OnKeyDown(DWORD vkCode, bool win, bool control, bool inMoveSize) noexcept;
|
||||
};
|
||||
|
||||
static void SettingsChanged(const Settings& settings) noexcept;
|
||||
static void VirtualDesktopChanged() noexcept;
|
||||
|
||||
class ZoneWindow
|
||||
{
|
||||
public:
|
||||
enum class InputMode
|
||||
{
|
||||
Keyboard,
|
||||
Mouse
|
||||
};
|
||||
|
||||
static void KeyUp(WPARAM wparam, bool isEditorMode) noexcept;
|
||||
static void MoveSizeEnd(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept;
|
||||
static void CycleActiveZoneSet(_In_opt_ winrt::com_ptr<IZoneSet> activeSet, InputMode mode) noexcept;
|
||||
|
||||
class EditorModeActivity
|
||||
{
|
||||
public:
|
||||
void Start() noexcept;
|
||||
void Stop(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept;
|
||||
private:
|
||||
std::optional<TraceLoggingActivity<g_hProvider, PROJECT_KEYWORD_MEASURE>> m_activity{};
|
||||
};
|
||||
};
|
||||
};
|
||||
152
src/modules/fancyzones/lib/util.h
Normal file
152
src/modules/fancyzones/lib/util.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
struct Rect
|
||||
{
|
||||
Rect() {}
|
||||
|
||||
Rect(RECT rect) : m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
Rect(RECT rect, UINT dpi) : m_rect(rect)
|
||||
{
|
||||
m_rect.right = m_rect.left + MulDiv(m_rect.right - m_rect.left, dpi, 96);
|
||||
m_rect.bottom = m_rect.top + MulDiv(m_rect.bottom - m_rect.top, dpi, 96);
|
||||
}
|
||||
|
||||
int x() const { return m_rect.left; }
|
||||
int y() const { return m_rect.top; }
|
||||
int width() const { return m_rect.right - m_rect.left; }
|
||||
int height() const { return m_rect.bottom - m_rect.top; }
|
||||
int left() const { return m_rect.left; }
|
||||
int top() const { return m_rect.top; }
|
||||
int right() const { return m_rect.right; }
|
||||
int bottom() const { return m_rect.bottom; }
|
||||
int aspectRatio() const { return MulDiv(m_rect.bottom - m_rect.top, 100, m_rect.right - m_rect.left); }
|
||||
|
||||
private:
|
||||
RECT m_rect{};
|
||||
};
|
||||
|
||||
inline void MakeWindowTransparent(HWND window)
|
||||
{
|
||||
int const pos = -GetSystemMetrics(SM_CXVIRTUALSCREEN) - 8;
|
||||
if (wil::unique_hrgn hrgn{ CreateRectRgn(pos, 0, (pos + 1), 1) })
|
||||
{
|
||||
DWM_BLURBEHIND bh = { DWM_BB_ENABLE | DWM_BB_BLURREGION, TRUE, hrgn.get(), FALSE };
|
||||
DwmEnableBlurBehindWindow(window, &bh);
|
||||
}
|
||||
}
|
||||
|
||||
inline void InitRGB(_Out_ RGBQUAD *quad, BYTE alpha, COLORREF color)
|
||||
{
|
||||
ZeroMemory(quad, sizeof(*quad));
|
||||
quad->rgbReserved = alpha;
|
||||
quad->rgbRed = GetRValue(color) * alpha / 255;
|
||||
quad->rgbGreen = GetGValue(color) * alpha / 255;
|
||||
quad->rgbBlue = GetBValue(color) * alpha / 255;
|
||||
}
|
||||
|
||||
inline void FillRectARGB(wil::unique_hdc& hdc, RECT const *prcFill, BYTE alpha, COLORREF color, bool blendAlpha)
|
||||
{
|
||||
BITMAPINFO bi;
|
||||
ZeroMemory(&bi, sizeof(bi));
|
||||
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bi.bmiHeader.biWidth = 1;
|
||||
bi.bmiHeader.biHeight = 1;
|
||||
bi.bmiHeader.biPlanes = 1;
|
||||
bi.bmiHeader.biBitCount = 32;
|
||||
bi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
RECT fillRect;
|
||||
CopyRect(&fillRect, prcFill);
|
||||
if ((alpha == 255) || !blendAlpha)
|
||||
{
|
||||
// Opaque or the caller does not want to blend the alpha
|
||||
RGBQUAD bitmapBits;
|
||||
InitRGB(&bitmapBits, alpha, color);
|
||||
StretchDIBits(
|
||||
hdc.get(),
|
||||
fillRect.left,
|
||||
fillRect.top,
|
||||
fillRect.right - fillRect.left,
|
||||
fillRect.bottom - fillRect.top,
|
||||
0, 0, 1, 1, &bitmapBits, &bi, DIB_RGB_COLORS, SRCCOPY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wil::unique_hdc hdcSrc{ CreateCompatibleDC(hdc.get()) })
|
||||
{
|
||||
void* pBitmapBits;
|
||||
if (wil::unique_hbitmap bitmapSource{ CreateDIBSection(hdcSrc.get(), &bi, DIB_RGB_COLORS, &pBitmapBits, nullptr, 0) })
|
||||
{
|
||||
InitRGB(reinterpret_cast<RGBQUAD *>(pBitmapBits), alpha, color);
|
||||
|
||||
wil::unique_select_object bitmapOld{ SelectObject(hdcSrc.get(), bitmapSource.get()) };
|
||||
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
|
||||
GdiAlphaBlend(
|
||||
hdc.get(),
|
||||
fillRect.left,
|
||||
fillRect.top,
|
||||
fillRect.right - fillRect.left,
|
||||
fillRect.bottom - fillRect.top,
|
||||
hdcSrc.get(), 0, 0, 1, 1, bf);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void FrameRectARGB(wil::unique_hdc& hdc, const RECT &rc, BYTE bAlpha, COLORREF clr, int thickness)
|
||||
{
|
||||
RECT sides[] = {
|
||||
{ rc.left, rc.top, (rc.left + thickness), rc.bottom },
|
||||
{ (rc.right - thickness), rc.top, rc.right, rc.bottom },
|
||||
{ (rc.left + thickness), rc.top, (rc.right - thickness), (rc.top + thickness) },
|
||||
{ (rc.left + thickness), (rc.bottom - thickness), (rc.right - thickness), rc.bottom }
|
||||
};
|
||||
|
||||
for (UINT i = 0; i < ARRAYSIZE(sides); i++)
|
||||
{
|
||||
FillRectARGB(hdc, &(sides[i]), bAlpha, clr, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
wchar_t buffer[256];
|
||||
StringCchCopy(buffer, 256, deviceId);
|
||||
|
||||
PWSTR pszStart = wcschr(buffer, L'#');
|
||||
PWSTR pszEnd = wcsrchr(buffer, L'#');
|
||||
if (pszStart && pszEnd && (pszStart != pszEnd))
|
||||
{
|
||||
pszStart++; // skip past the first #
|
||||
*pszEnd = '\0';
|
||||
StringCchCopy(parsedId, size, pszStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchCopy(parsedId, size, L"FallbackDevice");
|
||||
}
|
||||
}
|
||||
|
||||
inline DWORD GetProcessPath(HWND window, LPWSTR processPath, DWORD processPathMaxSize) noexcept
|
||||
{
|
||||
DWORD pid{};
|
||||
GetWindowThreadProcessId(window, &pid);
|
||||
|
||||
DWORD numCopiedChars = 0;
|
||||
wil::unique_handle windowProcessHandle(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, TRUE, pid));
|
||||
if (windowProcessHandle && (windowProcessHandle.get() != INVALID_HANDLE_VALUE))
|
||||
{
|
||||
// numCopiedChars first holds the size of processPath[], will then hold amount of characters returned by QueryFullProcessImageNameW
|
||||
// if QueryFullProcessImageNameW fails, numCopiedChars will be zero.
|
||||
numCopiedChars = processPathMaxSize;
|
||||
QueryFullProcessImageNameW(windowProcessHandle.get(), 0, processPath, &numCopiedChars);
|
||||
}
|
||||
return numCopiedChars;
|
||||
}
|
||||
Reference in New Issue
Block a user