2021-06-23 20:48:54 +08:00
# include "pch.h"
2021-07-07 18:18:52 +08:00
# include "FancyZones.h"
2021-06-23 20:48:54 +08:00
# include <common/display/dpi_aware.h>
# include <common/interop/shared_constants.h>
# include <common/logger/logger.h>
# include <common/utils/EventWaiter.h>
# include <common/utils/resources.h>
# include <common/utils/winapi_error.h>
# include <common/utils/window.h>
2021-06-29 18:06:12 +08:00
# include <common/SettingsAPI/FileWatcher.h>
2021-06-23 20:48:54 +08:00
2021-07-07 18:18:52 +08:00
# include <FancyZonesLib/FancyZonesData.h>
2021-08-11 22:05:03 +08:00
# include <FancyZonesLib/FancyZonesWindowProperties.h>
2021-07-07 18:18:52 +08:00
# include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
# include <FancyZonesLib/MonitorUtils.h>
# include <FancyZonesLib/Settings.h>
# include <FancyZonesLib/ZoneSet.h>
# include <FancyZonesLib/WorkArea.h>
# include <FancyZonesLib/WindowMoveHandler.h>
# include <FancyZonesLib/util.h>
2021-06-23 20:48:54 +08:00
# include "on_thread_executor.h"
# include "trace.h"
2021-07-07 18:18:52 +08:00
# include "VirtualDesktop.h"
2021-06-23 20:48:54 +08:00
# include "MonitorWorkAreaHandler.h"
# include "util.h"
# include "CallTracer.h"
# include <FancyZonesLib/SecondaryMouseButtonsHook.h>
enum class DisplayChangeType
{
WorkArea ,
DisplayChange ,
VirtualDesktop ,
Initialization
} ;
// Non-localizable strings
namespace NonLocalizable
{
const wchar_t ToolWindowClassName [ ] = L " SuperFancyZones " ;
const wchar_t FZEditorExecutablePath [ ] = L " modules \\ FancyZones \\ FancyZonesEditor.exe " ;
}
2021-07-07 18:18:52 +08:00
struct FancyZones : public winrt : : implements < FancyZones , IFancyZones , IFancyZonesCallback >
2021-06-23 20:48:54 +08:00
{
public :
FancyZones ( HINSTANCE hinstance , const winrt : : com_ptr < IFancyZonesSettings > & settings , std : : function < void ( ) > disableModuleCallback ) noexcept :
m_hinstance ( hinstance ) ,
m_settings ( settings ) ,
m_windowMoveHandler ( settings , [ this ] ( ) {
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , NULL , NULL ) ;
} ) ,
m_zonesSettingsFileWatcher ( FancyZonesDataInstance ( ) . GetZonesSettingsFileName ( ) , [ this ] ( ) {
PostMessageW ( m_window , WM_PRIV_FILE_UPDATE , NULL , NULL ) ;
} ) ,
m_settingsFileWatcher ( FancyZonesDataInstance ( ) . GetSettingsFileName ( ) , [ this ] ( ) {
PostMessageW ( m_window , WM_PRIV_SETTINGS_CHANGED , NULL , NULL ) ;
2021-07-07 18:18:52 +08:00
} ) ,
m_virtualDesktop ( [ this ] ( ) {
PostMessage ( m_window , WM_PRIV_VD_INIT , 0 , 0 ) ;
} ,
[ this ] ( ) {
PostMessage ( m_window , WM_PRIV_VD_UPDATE , 0 , 0 ) ;
2021-06-23 20:48:54 +08:00
} )
{
this - > disableModuleCallback = std : : move ( disableModuleCallback ) ;
}
// IFancyZones
IFACEMETHODIMP_ ( void )
Run ( ) noexcept ;
IFACEMETHODIMP_ ( void )
Destroy ( ) noexcept ;
void MoveSizeStart ( HWND window , HMONITOR monitor , POINT const & ptScreen ) noexcept
{
if ( m_settings - > GetSettings ( ) - > spanZonesAcrossMonitors )
{
monitor = NULL ;
}
m_windowMoveHandler . MoveSizeStart ( window , monitor , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
}
void MoveSizeUpdate ( HMONITOR monitor , POINT const & ptScreen ) noexcept
{
if ( m_settings - > GetSettings ( ) - > spanZonesAcrossMonitors )
{
monitor = NULL ;
}
m_windowMoveHandler . MoveSizeUpdate ( monitor , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
}
void MoveSizeEnd ( HWND window , POINT const & ptScreen ) noexcept
{
_TRACER_ ;
m_windowMoveHandler . MoveSizeEnd ( window , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
}
IFACEMETHODIMP_ ( void )
HandleWinHookEvent ( const WinHookEvent * data ) noexcept
{
const auto wparam = reinterpret_cast < WPARAM > ( data - > hwnd ) ;
const LONG lparam = 0 ;
switch ( data - > event )
{
case EVENT_SYSTEM_MOVESIZESTART :
PostMessageW ( m_window , WM_PRIV_MOVESIZESTART , wparam , lparam ) ;
break ;
case EVENT_SYSTEM_MOVESIZEEND :
PostMessageW ( m_window , WM_PRIV_MOVESIZEEND , wparam , lparam ) ;
break ;
case EVENT_OBJECT_LOCATIONCHANGE :
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , wparam , lparam ) ;
break ;
case EVENT_OBJECT_NAMECHANGE :
PostMessageW ( m_window , WM_PRIV_NAMECHANGE , wparam , lparam ) ;
break ;
case EVENT_OBJECT_UNCLOAKED :
case EVENT_OBJECT_SHOW :
case EVENT_OBJECT_CREATE :
if ( data - > idObject = = OBJID_WINDOW )
{
PostMessageW ( m_window , WM_PRIV_WINDOWCREATED , wparam , lparam ) ;
}
break ;
}
}
IFACEMETHODIMP_ ( void )
VirtualDesktopChanged ( ) noexcept ;
IFACEMETHODIMP_ ( bool )
OnKeyDown ( PKBDLLHOOKSTRUCT info ) noexcept ;
void WindowCreated ( HWND window ) noexcept ;
2021-07-07 18:18:52 +08:00
void ToggleEditor ( ) noexcept ;
2021-06-23 20:48:54 +08:00
LRESULT WndProc ( HWND , UINT , WPARAM , LPARAM ) noexcept ;
2021-07-07 18:18:52 +08:00
void OnDisplayChange ( DisplayChangeType changeType ) noexcept ;
void AddZoneWindow ( HMONITOR monitor , const std : : wstring & deviceId ) noexcept ;
2021-06-23 20:48:54 +08:00
protected :
static LRESULT CALLBACK s_WndProc ( HWND , UINT , WPARAM , LPARAM ) noexcept ;
private :
2021-07-07 18:18:52 +08:00
void UpdateZoneWindows ( ) noexcept ;
void UpdateWindowsPositions ( ) noexcept ;
2021-06-23 20:48:54 +08:00
bool OnSnapHotkeyBasedOnZoneNumber ( HWND window , DWORD vkCode ) noexcept ;
bool OnSnapHotkeyBasedOnPosition ( HWND window , DWORD vkCode ) noexcept ;
bool OnSnapHotkey ( DWORD vkCode ) noexcept ;
2021-07-07 18:18:52 +08:00
bool ProcessDirectedSnapHotkey ( HWND window , DWORD vkCode , bool cycle , winrt : : com_ptr < IWorkArea > zoneWindow ) noexcept ;
void RegisterVirtualDesktopUpdates ( ) noexcept ;
2021-06-23 20:48:54 +08:00
2021-07-07 18:18:52 +08:00
void OnSettingsChanged ( ) noexcept ;
2021-06-23 20:48:54 +08:00
2021-08-27 01:47:27 +08:00
std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > GetAppZoneHistoryInfo ( HWND window , HMONITOR monitor , const std : : unordered_map < HMONITOR , winrt : : com_ptr < IWorkArea > > & workAreaMap ) noexcept ;
2021-08-11 22:05:03 +08:00
void MoveWindowIntoZone ( HWND window , winrt : : com_ptr < IWorkArea > zoneWindow , const ZoneIndexSet & zoneIndexSet ) noexcept ;
2021-08-27 01:47:27 +08:00
bool MoveToAppLastZone ( HWND window , HMONITOR active , HMONITOR primary ) noexcept ;
2021-06-23 20:48:54 +08:00
2021-07-07 18:18:52 +08:00
void OnEditorExitEvent ( ) noexcept ;
void UpdateZoneSets ( ) noexcept ;
2021-06-23 20:48:54 +08:00
bool ShouldProcessSnapHotkey ( DWORD vkCode ) noexcept ;
void ApplyQuickLayout ( int key ) noexcept ;
2021-07-07 18:18:52 +08:00
void FlashZones ( ) noexcept ;
2021-06-23 20:48:54 +08:00
std : : vector < std : : pair < HMONITOR , RECT > > GetRawMonitorData ( ) noexcept ;
std : : vector < HMONITOR > GetMonitorsSorted ( ) noexcept ;
HMONITOR WorkAreaKeyFromWindow ( HWND window ) noexcept ;
2021-07-07 18:18:52 +08:00
ZoneColors GetZoneColors ( ) const noexcept ;
2021-06-23 20:48:54 +08:00
const HINSTANCE m_hinstance { } ;
HWND m_window { } ;
WindowMoveHandler m_windowMoveHandler ;
MonitorWorkAreaHandler m_workAreaHandler ;
2021-07-07 18:18:52 +08:00
VirtualDesktop m_virtualDesktop ;
2021-06-23 20:48:54 +08:00
FileWatcher m_zonesSettingsFileWatcher ;
FileWatcher m_settingsFileWatcher ;
winrt : : com_ptr < IFancyZonesSettings > m_settings { } ;
GUID m_previousDesktopId { } ; // UUID of previously active virtual desktop.
GUID m_currentDesktopId { } ; // UUID of the current virtual desktop.
wil : : unique_handle m_terminateEditorEvent ; // Handle of FancyZonesEditor.exe we launch and wait on
OnThreadExecutor m_dpiUnawareThread ;
EventWaiter m_toggleEditorEventWaiter ;
// If non-recoverable error occurs, trigger disabling of entire FancyZones.
static std : : function < void ( ) > disableModuleCallback ;
// Did we terminate the editor or was it closed cleanly?
enum class EditorExitKind : byte
{
Exit ,
Terminate
} ;
} ;
std : : function < void ( ) > FancyZones : : disableModuleCallback = { } ;
// IFancyZones
IFACEMETHODIMP_ ( void )
FancyZones : : Run ( ) noexcept
{
WNDCLASSEXW wcex { } ;
wcex . cbSize = sizeof ( WNDCLASSEX ) ;
wcex . lpfnWndProc = s_WndProc ;
wcex . hInstance = m_hinstance ;
wcex . lpszClassName = NonLocalizable : : ToolWindowClassName ;
RegisterClassExW ( & wcex ) ;
BufferedPaintInit ( ) ;
m_window = CreateWindowExW ( WS_EX_TOOLWINDOW , NonLocalizable : : ToolWindowClassName , L " " , WS_POPUP , 0 , 0 , 0 , 0 , nullptr , nullptr , m_hinstance , this ) ;
if ( ! m_window )
{
return ;
}
RegisterHotKey ( m_window , 1 , m_settings - > GetSettings ( ) - > editorHotkey . get_modifiers ( ) , m_settings - > GetSettings ( ) - > editorHotkey . get_code ( ) ) ;
2021-07-07 18:18:52 +08:00
m_virtualDesktop . Init ( ) ;
2021-06-23 20:48:54 +08:00
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ ] {
SetThreadDpiAwarenessContext ( DPI_AWARENESS_CONTEXT_UNAWARE ) ;
SetThreadDpiHostingBehavior ( DPI_HOSTING_BEHAVIOR_MIXED ) ;
} } )
. wait ( ) ;
m_toggleEditorEventWaiter = EventWaiter ( CommonSharedConstants : : FANCY_ZONES_EDITOR_TOGGLE_EVENT , [ & ] ( int err ) {
if ( err = = ERROR_SUCCESS )
{
Logger : : trace ( L " {} event was signaled " , CommonSharedConstants : : FANCY_ZONES_EDITOR_TOGGLE_EVENT ) ;
PostMessage ( m_window , WM_HOTKEY , 1 , 0 ) ;
}
} ) ;
}
// IFancyZones
IFACEMETHODIMP_ ( void )
FancyZones : : Destroy ( ) noexcept
{
m_workAreaHandler . Clear ( ) ;
BufferedPaintUnInit ( ) ;
if ( m_window )
{
DestroyWindow ( m_window ) ;
m_window = nullptr ;
}
2021-07-07 18:18:52 +08:00
m_virtualDesktop . UnInit ( ) ;
2021-06-23 20:48:54 +08:00
}
// IFancyZonesCallback
IFACEMETHODIMP_ ( void )
FancyZones : : VirtualDesktopChanged ( ) noexcept
{
// VirtualDesktopChanged is called from a reentrant WinHookProc function, therefore we must postpone the actual logic
// until we're in FancyZones::WndProc, which is not reentrant.
PostMessage ( m_window , WM_PRIV_VD_SWITCH , 0 , 0 ) ;
}
2021-08-27 01:47:27 +08:00
std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > FancyZones : : GetAppZoneHistoryInfo ( HWND window , HMONITOR monitor , const std : : unordered_map < HMONITOR , winrt : : com_ptr < IWorkArea > > & workAreaMap ) noexcept
2021-06-23 20:48:54 +08:00
{
2021-08-27 01:47:27 +08:00
if ( monitor )
2021-07-10 01:18:21 +08:00
{
2021-08-27 01:47:27 +08:00
if ( workAreaMap . contains ( monitor ) )
{
auto workArea = workAreaMap . at ( monitor ) ;
return std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > { workArea , workArea - > GetWindowZoneIndexes ( window ) } ;
}
else
{
Logger : : debug ( L " No work area for the currently active monitor. " ) ;
}
2021-07-10 01:18:21 +08:00
}
else
{
2021-06-23 20:48:54 +08:00
for ( const auto & [ monitor , workArea ] : workAreaMap )
{
2021-07-07 18:18:52 +08:00
auto zoneIndexSet = workArea - > GetWindowZoneIndexes ( window ) ;
2021-06-23 20:48:54 +08:00
if ( ! zoneIndexSet . empty ( ) )
{
2021-08-27 01:47:27 +08:00
return std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > { workArea , zoneIndexSet } ;
2021-06-23 20:48:54 +08:00
}
}
}
2021-08-27 01:47:27 +08:00
return std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > { nullptr , { } } ;
2021-06-23 20:48:54 +08:00
}
2021-08-11 22:05:03 +08:00
void FancyZones : : MoveWindowIntoZone ( HWND window , winrt : : com_ptr < IWorkArea > zoneWindow , const ZoneIndexSet & zoneIndexSet ) noexcept
2021-06-23 20:48:54 +08:00
{
_TRACER_ ;
auto & fancyZonesData = FancyZonesDataInstance ( ) ;
if ( ! fancyZonesData . IsAnotherWindowOfApplicationInstanceZoned ( window , zoneWindow - > UniqueId ( ) ) )
{
m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , zoneIndexSet , zoneWindow ) ;
fancyZonesData . UpdateProcessIdToHandleMap ( window , zoneWindow - > UniqueId ( ) ) ;
}
}
2021-08-27 01:47:27 +08:00
bool FancyZones : : MoveToAppLastZone ( HWND window , HMONITOR active , HMONITOR primary ) noexcept
{
auto workAreaMap = m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ;
if ( workAreaMap . empty ( ) )
{
Logger : : trace ( L " No work area for the current desktop. " ) ;
return false ;
}
// Search application history on currently active monitor.
std : : pair < winrt : : com_ptr < IWorkArea > , ZoneIndexSet > appZoneHistoryInfo = GetAppZoneHistoryInfo ( window , active , workAreaMap ) ;
// No application history on currently active monitor
if ( appZoneHistoryInfo . second . empty ( ) )
{
// Search application history on primary monitor.
appZoneHistoryInfo = GetAppZoneHistoryInfo ( window , primary , workAreaMap ) ;
}
// No application history on currently active and primary monitors
if ( appZoneHistoryInfo . second . empty ( ) )
{
// Search application history on remaining monitors.
appZoneHistoryInfo = GetAppZoneHistoryInfo ( window , nullptr , workAreaMap ) ;
}
if ( ! appZoneHistoryInfo . second . empty ( ) )
{
MoveWindowIntoZone ( window , appZoneHistoryInfo . first , appZoneHistoryInfo . second ) ;
return true ;
}
else
{
Logger : : trace ( L " App zone history is empty for the processing window on a current virtual desktop " ) ;
}
return false ;
}
2021-07-07 18:18:52 +08:00
void FancyZones : : WindowCreated ( HWND window ) noexcept
2021-06-23 20:48:54 +08:00
{
2021-08-27 01:47:27 +08:00
const bool moveToAppLastZone = m_settings - > GetSettings ( ) - > appLastZone_moveWindows ;
const bool openOnActiveMonitor = m_settings - > GetSettings ( ) - > openWindowOnActiveMonitor ;
if ( ! moveToAppLastZone & & ! openOnActiveMonitor )
{
// Nothing to do here then.
return ;
}
2021-08-27 21:59:04 +08:00
auto desktopId = m_virtualDesktop . GetDesktopId ( window ) ;
if ( desktopId . has_value ( ) & & * desktopId ! = m_currentDesktopId )
2021-06-23 20:48:54 +08:00
{
// Switch between virtual desktops results with posting same windows messages that also indicate
// creation of new window. We need to check if window being processed is on currently active desktop.
return ;
}
2021-07-07 18:18:52 +08:00
// Avoid processing splash screens, already stamped (zoned) windows, or those windows
// that belong to excluded applications list.
const bool isSplashScreen = FancyZonesUtils : : IsSplashScreen ( window ) ;
2021-08-27 01:47:27 +08:00
if ( isSplashScreen )
{
return ;
}
const bool windowMinimized = IsIconic ( window ) ;
if ( windowMinimized )
{
return ;
}
2021-08-11 22:05:03 +08:00
const bool isZoned = reinterpret_cast < ZoneIndex > ( : : GetProp ( window , ZonedWindowProperties : : PropertyMultipleZoneID ) ) ! = 0 ;
2021-08-27 01:47:27 +08:00
if ( isZoned )
{
return ;
}
2021-07-07 18:18:52 +08:00
2021-08-27 01:47:27 +08:00
const bool isCandidateForLastKnownZone = FancyZonesUtils : : IsCandidateForLastKnownZone ( window , m_settings - > GetSettings ( ) - > excludedAppsArray ) ;
if ( ! isCandidateForLastKnownZone )
2021-06-23 20:48:54 +08:00
{
2021-08-27 01:47:27 +08:00
return ;
}
2021-06-23 20:48:54 +08:00
2021-08-27 01:47:27 +08:00
HMONITOR primary = MonitorFromWindow ( nullptr , MONITOR_DEFAULTTOPRIMARY ) ;
HMONITOR active = primary ;
2021-06-23 20:48:54 +08:00
2021-08-27 01:47:27 +08:00
POINT cursorPosition { } ;
if ( GetCursorPos ( & cursorPosition ) )
{
active = MonitorFromPoint ( cursorPosition , MONITOR_DEFAULTTOPRIMARY ) ;
}
2021-07-10 01:18:21 +08:00
2021-08-27 01:47:27 +08:00
bool movedToAppLastZone = false ;
if ( moveToAppLastZone )
{
movedToAppLastZone = MoveToAppLastZone ( window , active , primary ) ;
}
// Open on active monitor if window wasn't zoned
if ( openOnActiveMonitor & & ! movedToAppLastZone )
{
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ & ] { MonitorUtils : : OpenWindowOnActiveMonitor ( window , active ) ; } } ) . wait ( ) ;
2021-06-23 20:48:54 +08:00
}
}
// IFancyZonesCallback
IFACEMETHODIMP_ ( bool )
FancyZones : : OnKeyDown ( PKBDLLHOOKSTRUCT info ) noexcept
{
// Return true to swallow the keyboard event
bool const shift = GetAsyncKeyState ( VK_SHIFT ) & 0x8000 ;
bool const win = GetAsyncKeyState ( VK_LWIN ) & 0x8000 | | GetAsyncKeyState ( VK_RWIN ) & 0x8000 ;
bool const alt = GetAsyncKeyState ( VK_MENU ) & 0x8000 ;
bool const ctrl = GetAsyncKeyState ( VK_CONTROL ) & 0x8000 ;
if ( ( win & & ! shift & & ! ctrl ) | | ( win & & ctrl & & alt ) )
{
if ( ( info - > vkCode = = VK_RIGHT ) | | ( info - > vkCode = = VK_LEFT ) | | ( info - > vkCode = = VK_UP ) | | ( info - > vkCode = = VK_DOWN ) )
{
if ( ShouldProcessSnapHotkey ( info - > vkCode ) )
{
Trace : : FancyZones : : OnKeyDown ( info - > vkCode , win , ctrl , false /*inMoveSize*/ ) ;
// Win+Left, Win+Right will cycle through Zones in the active ZoneSet when WM_PRIV_SNAP_HOTKEY's handled
PostMessageW ( m_window , WM_PRIV_SNAP_HOTKEY , 0 , info - > vkCode ) ;
return true ;
}
}
}
if ( m_settings - > GetSettings ( ) - > quickLayoutSwitch )
{
int digitPressed = - 1 ;
if ( ' 0 ' < = info - > vkCode & & info - > vkCode < = ' 9 ' )
{
digitPressed = info - > vkCode - ' 0 ' ;
}
else if ( VK_NUMPAD0 < = info - > vkCode & & info - > vkCode < = VK_NUMPAD9 )
{
digitPressed = info - > vkCode - VK_NUMPAD0 ;
}
bool dragging = m_windowMoveHandler . InMoveSize ( ) ;
bool changeLayoutWhileNotDragging = ! dragging & & ! shift & & win & & ctrl & & alt & & digitPressed ! = - 1 ;
bool changeLayoutWhileDragging = dragging & & digitPressed ! = - 1 ;
if ( changeLayoutWhileNotDragging | | changeLayoutWhileDragging )
{
auto quickKeysMap = FancyZonesDataInstance ( ) . GetLayoutQuickKeys ( ) ;
if ( std : : any_of ( quickKeysMap . begin ( ) , quickKeysMap . end ( ) , [ = ] ( auto item ) { return item . second = = digitPressed ; } ) )
{
PostMessageW ( m_window , WM_PRIV_QUICK_LAYOUT_KEY , 0 , static_cast < LPARAM > ( digitPressed ) ) ;
Trace : : FancyZones : : QuickLayoutSwitched ( changeLayoutWhileNotDragging ) ;
return true ;
}
}
}
if ( m_windowMoveHandler . IsDragEnabled ( ) & & shift )
{
return true ;
}
return false ;
}
void FancyZones : : ToggleEditor ( ) noexcept
{
_TRACER_ ;
2021-07-07 18:18:52 +08:00
if ( m_terminateEditorEvent )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
SetEvent ( m_terminateEditorEvent . get ( ) ) ;
return ;
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
m_terminateEditorEvent . reset ( CreateEvent ( nullptr , true , false , nullptr ) ) ;
2021-06-23 20:48:54 +08:00
HMONITOR targetMonitor { } ;
const bool use_cursorpos_editor_startupscreen = m_settings - > GetSettings ( ) - > use_cursorpos_editor_startupscreen ;
if ( use_cursorpos_editor_startupscreen )
{
POINT currentCursorPos { } ;
GetCursorPos ( & currentCursorPos ) ;
targetMonitor = MonitorFromPoint ( currentCursorPos , MONITOR_DEFAULTTOPRIMARY ) ;
}
else
{
targetMonitor = MonitorFromWindow ( GetForegroundWindow ( ) , MONITOR_DEFAULTTOPRIMARY ) ;
}
if ( ! targetMonitor )
{
return ;
}
wil : : unique_cotaskmem_string virtualDesktopId ;
if ( ! SUCCEEDED ( StringFromCLSID ( m_currentDesktopId , & virtualDesktopId ) ) )
{
return ;
}
/*
* Divider : /
* Parts :
* ( 1 ) Process id
* ( 2 ) Span zones across monitors
* ( 3 ) Monitor id where the Editor should be opened
* ( 4 ) Monitors count
*
* Data for each monitor :
* ( 5 ) Monitor id
* ( 6 ) DPI
2021-06-23 23:28:23 +08:00
* ( 7 ) work area left
* ( 8 ) work area top
* ( 9 ) work area width
* ( 10 ) work area height
2021-06-23 20:48:54 +08:00
* . . .
*/
std : : wstring params ;
const std : : wstring divider = L " / " ;
params + = std : : to_wstring ( GetCurrentProcessId ( ) ) + divider ; /* Process id */
const bool spanZonesAcrossMonitors = m_settings - > GetSettings ( ) - > spanZonesAcrossMonitors ;
params + = std : : to_wstring ( spanZonesAcrossMonitors ) + divider ; /* Span zones */
std : : vector < std : : pair < HMONITOR , MONITORINFOEX > > allMonitors ;
2021-06-23 23:28:23 +08:00
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ & ] {
allMonitors = FancyZonesUtils : : GetAllMonitorInfo < & MONITORINFOEX : : rcWork > ( ) ;
} } ) . wait ( ) ;
2021-06-23 20:48:54 +08:00
if ( spanZonesAcrossMonitors )
{
params + = FancyZonesUtils : : GenerateUniqueIdAllMonitorsArea ( virtualDesktopId . get ( ) ) + divider ; /* Monitor id where the Editor should be opened */
}
// device id map
std : : unordered_map < std : : wstring , DWORD > displayDeviceIdxMap ;
bool showDpiWarning = false ;
2021-06-23 23:28:23 +08:00
int prevDpi = - 1 ;
2021-06-23 20:48:54 +08:00
std : : wstring monitorsDataStr ;
2021-06-23 23:28:23 +08:00
2021-06-23 20:48:54 +08:00
for ( auto & monitorData : allMonitors )
{
HMONITOR monitor = monitorData . first ;
auto monitorInfo = monitorData . second ;
std : : wstring deviceId = FancyZonesUtils : : GetDisplayDeviceId ( monitorInfo . szDevice , displayDeviceIdxMap ) ;
std : : wstring monitorId = FancyZonesUtils : : GenerateUniqueId ( monitor , deviceId , virtualDesktopId . get ( ) ) ;
if ( monitor = = targetMonitor & & ! spanZonesAcrossMonitors )
{
params + = monitorId + divider ; /* Monitor id where the Editor should be opened */
}
2021-06-23 23:28:23 +08:00
UINT dpi = 0 ;
if ( DPIAware : : GetScreenDPIForMonitor ( monitor , dpi ) ! = S_OK )
2021-06-23 20:48:54 +08:00
{
2021-06-23 23:28:23 +08:00
continue ;
}
if ( spanZonesAcrossMonitors & & prevDpi ! = - 1 & & prevDpi ! = dpi )
{
showDpiWarning = true ;
2021-06-23 20:48:54 +08:00
}
2021-06-23 23:28:23 +08:00
monitorsDataStr + = std : : move ( monitorId ) + divider ; /* Monitor id */
monitorsDataStr + = std : : to_wstring ( dpi ) + divider ; /* DPI */
monitorsDataStr + = std : : to_wstring ( monitorInfo . rcWork . left ) + divider ; /* Top coordinate */
monitorsDataStr + = std : : to_wstring ( monitorInfo . rcWork . top ) + divider ; /* Left coordinate */
monitorsDataStr + = std : : to_wstring ( monitorInfo . rcWork . right - monitorInfo . rcWork . left ) + divider ; /* Width */
monitorsDataStr + = std : : to_wstring ( monitorInfo . rcWork . bottom - monitorInfo . rcWork . top ) + divider ; /* Height */
2021-06-23 20:48:54 +08:00
}
params + = std : : to_wstring ( allMonitors . size ( ) ) + divider ; /* Monitors count */
params + = monitorsDataStr ;
2021-06-23 23:28:23 +08:00
FancyZonesDataInstance ( ) . SaveFancyZonesEditorParameters ( spanZonesAcrossMonitors , virtualDesktopId . get ( ) , targetMonitor , allMonitors ) ; /* Write parameters to json file */
2021-06-23 20:48:54 +08:00
if ( showDpiWarning )
{
// We must show the message box in a separate thread, since this code is called from a low-level
// keyboard hook callback, and launching messageboxes from it has unexpected side effects
//std::thread{ [] {
// MessageBoxW(nullptr,
// GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
// GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
// MB_OK | MB_ICONWARNING);
//} }.detach();
}
SHELLEXECUTEINFO sei { sizeof ( sei ) } ;
sei . fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI } ;
sei . lpFile = NonLocalizable : : FZEditorExecutablePath ;
sei . lpParameters = params . c_str ( ) ;
sei . nShow = SW_SHOWDEFAULT ;
ShellExecuteEx ( & sei ) ;
Trace : : FancyZones : : EditorLaunched ( 1 ) ;
// Launch the editor on a background thread
// Wait for the editor's process to exit
// Post back to the main thread to update
std : : thread waitForEditorThread ( [ window = m_window , processHandle = sei . hProcess , terminateEditorEvent = m_terminateEditorEvent . get ( ) ] ( ) {
HANDLE waitEvents [ 2 ] = { processHandle , terminateEditorEvent } ;
auto result = WaitForMultipleObjects ( 2 , waitEvents , false , INFINITE ) ;
if ( result = = WAIT_OBJECT_0 + 0 )
{
// Editor exited
// Update any changes it may have made
PostMessage ( window , WM_PRIV_EDITOR , 0 , static_cast < 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 ( ) ;
}
LRESULT FancyZones : : WndProc ( HWND window , UINT message , WPARAM wparam , LPARAM lparam ) noexcept
{
switch ( message )
{
case WM_HOTKEY :
{
if ( wparam = = 1 )
{
ToggleEditor ( ) ;
}
}
break ;
case WM_SETTINGCHANGE :
{
if ( wparam = = SPI_SETWORKAREA )
{
// Changes in taskbar position resulted in different size of work area.
// Invalidate cached work-areas so they can be recreated with latest information.
m_workAreaHandler . Clear ( ) ;
2021-07-07 18:18:52 +08:00
OnDisplayChange ( DisplayChangeType : : WorkArea ) ;
2021-06-23 20:48:54 +08:00
}
}
break ;
case WM_DISPLAYCHANGE :
{
// Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information.
m_workAreaHandler . Clear ( ) ;
2021-07-07 18:18:52 +08:00
OnDisplayChange ( DisplayChangeType : : DisplayChange ) ;
2021-06-23 20:48:54 +08:00
}
break ;
default :
{
POINT ptScreen ;
GetPhysicalCursorPos ( & ptScreen ) ;
if ( message = = WM_PRIV_SNAP_HOTKEY )
{
OnSnapHotkey ( static_cast < DWORD > ( lparam ) ) ;
}
else if ( message = = WM_PRIV_VD_INIT )
{
2021-07-07 18:18:52 +08:00
OnDisplayChange ( DisplayChangeType : : Initialization ) ;
2021-06-23 20:48:54 +08:00
}
else if ( message = = WM_PRIV_VD_SWITCH )
{
2021-07-07 18:18:52 +08:00
OnDisplayChange ( DisplayChangeType : : VirtualDesktop ) ;
2021-06-23 20:48:54 +08:00
}
else if ( message = = WM_PRIV_VD_UPDATE )
{
2021-07-07 18:18:52 +08:00
RegisterVirtualDesktopUpdates ( ) ;
2021-06-23 20:48:54 +08:00
}
else if ( message = = WM_PRIV_EDITOR )
{
if ( lparam = = static_cast < LPARAM > ( EditorExitKind : : Exit ) )
{
2021-07-07 18:18:52 +08:00
OnEditorExitEvent ( ) ;
2021-06-23 20:48:54 +08:00
}
{
// Clean up the event either way
m_terminateEditorEvent . release ( ) ;
}
}
else if ( message = = WM_PRIV_MOVESIZESTART )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
if ( auto monitor = MonitorFromPoint ( ptScreen , MONITOR_DEFAULTTONULL ) )
{
MoveSizeStart ( hwnd , monitor , ptScreen ) ;
}
}
else if ( message = = WM_PRIV_MOVESIZEEND )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
MoveSizeEnd ( hwnd , ptScreen ) ;
}
2021-07-07 18:18:52 +08:00
else if ( message = = WM_PRIV_LOCATIONCHANGE )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
if ( m_windowMoveHandler . InMoveSize ( ) )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
if ( auto monitor = MonitorFromPoint ( ptScreen , MONITOR_DEFAULTTONULL ) )
{
MoveSizeUpdate ( monitor , ptScreen ) ;
}
2021-06-23 20:48:54 +08:00
}
}
else if ( message = = WM_PRIV_WINDOWCREATED )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
WindowCreated ( hwnd ) ;
}
else if ( message = = WM_PRIV_FILE_UPDATE )
{
FancyZonesDataInstance ( ) . LoadFancyZonesData ( ) ;
2021-07-07 18:18:52 +08:00
UpdateZoneSets ( ) ;
2021-06-23 20:48:54 +08:00
}
else if ( message = = WM_PRIV_QUICK_LAYOUT_KEY )
{
ApplyQuickLayout ( static_cast < int > ( lparam ) ) ;
}
else if ( message = = WM_PRIV_SETTINGS_CHANGED )
{
2021-07-07 18:18:52 +08:00
OnSettingsChanged ( ) ;
2021-06-23 20:48:54 +08:00
}
else
{
return DefWindowProc ( window , message , wparam , lparam ) ;
}
}
break ;
}
return 0 ;
}
2021-07-07 18:18:52 +08:00
void FancyZones : : OnDisplayChange ( DisplayChangeType changeType ) noexcept
2021-06-23 20:48:54 +08:00
{
_TRACER_ ;
if ( changeType = = DisplayChangeType : : VirtualDesktop | |
changeType = = DisplayChangeType : : Initialization )
{
m_previousDesktopId = m_currentDesktopId ;
2021-07-07 18:18:52 +08:00
auto currentVirtualDesktopId = m_virtualDesktop . GetCurrentVirtualDesktopId ( ) ;
if ( currentVirtualDesktopId . has_value ( ) )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
m_currentDesktopId = * currentVirtualDesktopId ;
2021-06-23 20:48:54 +08:00
if ( m_previousDesktopId ! = GUID_NULL & & m_currentDesktopId ! = m_previousDesktopId )
{
Trace : : VirtualDesktopChanged ( ) ;
}
2021-08-18 19:00:28 +08:00
if ( m_currentDesktopId = = GUID_NULL )
{
Logger : : warn ( " Couldn't retrieve virtual desktop id " ) ;
}
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
2021-06-23 20:48:54 +08:00
if ( changeType = = DisplayChangeType : : Initialization )
{
2021-07-07 18:18:52 +08:00
RegisterVirtualDesktopUpdates ( ) ;
2021-06-23 20:48:54 +08:00
}
}
2021-07-07 18:18:52 +08:00
UpdateZoneWindows ( ) ;
2021-06-23 20:48:54 +08:00
if ( ( changeType = = DisplayChangeType : : WorkArea ) | | ( changeType = = DisplayChangeType : : DisplayChange ) )
{
if ( m_settings - > GetSettings ( ) - > displayChange_moveWindows )
{
2021-07-07 18:18:52 +08:00
UpdateWindowsPositions ( ) ;
2021-06-23 20:48:54 +08:00
}
}
}
2021-07-07 18:18:52 +08:00
void FancyZones : : AddZoneWindow ( HMONITOR monitor , const std : : wstring & deviceId ) noexcept
2021-06-23 20:48:54 +08:00
{
_TRACER_ ;
if ( m_workAreaHandler . IsNewWorkArea ( m_currentDesktopId , monitor ) )
{
wil : : unique_cotaskmem_string virtualDesktopId ;
if ( SUCCEEDED ( StringFromCLSID ( m_currentDesktopId , & virtualDesktopId ) ) )
{
std : : wstring uniqueId ;
if ( monitor )
{
uniqueId = FancyZonesUtils : : GenerateUniqueId ( monitor , deviceId , virtualDesktopId . get ( ) ) ;
}
else
{
uniqueId = FancyZonesUtils : : GenerateUniqueIdAllMonitorsArea ( virtualDesktopId . get ( ) ) ;
}
std : : wstring parentId { } ;
auto parentArea = m_workAreaHandler . GetWorkArea ( m_previousDesktopId , monitor ) ;
if ( parentArea )
{
parentId = parentArea - > UniqueId ( ) ;
}
2021-07-07 18:18:52 +08:00
auto workArea = MakeWorkArea ( m_hinstance , monitor , uniqueId , parentId , GetZoneColors ( ) , m_settings - > GetSettings ( ) - > overlappingZonesAlgorithm ) ;
2021-06-23 20:48:54 +08:00
if ( workArea )
{
m_workAreaHandler . AddWorkArea ( m_currentDesktopId , monitor , workArea ) ;
FancyZonesDataInstance ( ) . SaveZoneSettings ( ) ;
}
}
}
}
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 ) ;
}
2021-07-07 18:18:52 +08:00
void FancyZones : : UpdateZoneWindows ( ) noexcept
2021-06-23 20:48:54 +08:00
{
// Mapping between display device name and device index (operating system identifies each display device with an index value).
std : : unordered_map < std : : wstring , DWORD > displayDeviceIdxMap ;
struct capture
{
FancyZones * fancyZones ;
std : : unordered_map < std : : wstring , DWORD > * displayDeviceIdx ;
} ;
auto callback = [ ] ( HMONITOR monitor , HDC , RECT * , LPARAM data ) - > BOOL {
capture * params = reinterpret_cast < capture * > ( data ) ;
MONITORINFOEX mi { { . cbSize = sizeof ( mi ) } } ;
if ( GetMonitorInfoW ( monitor , & mi ) )
{
auto & displayDeviceIdxMap = * ( params - > displayDeviceIdx ) ;
FancyZones * fancyZones = params - > fancyZones ;
std : : wstring deviceId = FancyZonesUtils : : GetDisplayDeviceId ( mi . szDevice , displayDeviceIdxMap ) ;
2021-07-07 18:18:52 +08:00
fancyZones - > AddZoneWindow ( monitor , deviceId ) ;
2021-06-23 20:48:54 +08:00
}
return TRUE ;
} ;
if ( m_settings - > GetSettings ( ) - > spanZonesAcrossMonitors )
{
2021-07-07 18:18:52 +08:00
AddZoneWindow ( nullptr , { } ) ;
2021-06-23 20:48:54 +08:00
}
else
{
2021-07-07 18:18:52 +08:00
capture capture { this , & displayDeviceIdxMap } ;
2021-06-23 20:48:54 +08:00
EnumDisplayMonitors ( nullptr , nullptr , callback , reinterpret_cast < LPARAM > ( & capture ) ) ;
}
}
2021-07-07 18:18:52 +08:00
void FancyZones : : UpdateWindowsPositions ( ) noexcept
2021-06-23 20:48:54 +08:00
{
auto callback = [ ] ( HWND window , LPARAM data ) - > BOOL {
2021-08-11 22:05:03 +08:00
auto zoneIndexSet = GetZoneIndexSet ( window ) ;
auto strongThis = reinterpret_cast < FancyZones * > ( data ) ;
auto desktopId = strongThis - > m_virtualDesktop . GetWindowDesktopId ( window ) ;
if ( desktopId . has_value ( ) )
2021-06-23 20:48:54 +08:00
{
2021-08-11 22:05:03 +08:00
auto zoneWindow = strongThis - > m_workAreaHandler . GetWorkArea ( window , * desktopId ) ;
if ( zoneWindow )
2021-06-23 20:48:54 +08:00
{
2021-08-11 22:05:03 +08:00
strongThis - > m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , zoneIndexSet , zoneWindow ) ;
2021-06-23 20:48:54 +08:00
}
}
2021-08-11 22:05:03 +08:00
2021-06-23 20:48:54 +08:00
return TRUE ;
} ;
EnumWindows ( callback , reinterpret_cast < LPARAM > ( this ) ) ;
}
bool FancyZones : : OnSnapHotkeyBasedOnZoneNumber ( HWND window , DWORD vkCode ) noexcept
{
_TRACER_ ;
HMONITOR current = WorkAreaKeyFromWindow ( window ) ;
std : : vector < HMONITOR > monitorInfo = GetMonitorsSorted ( ) ;
if ( current & & monitorInfo . size ( ) > 1 & & m_settings - > GetSettings ( ) - > moveWindowAcrossMonitors )
{
// Multi monitor environment.
auto currMonitorInfo = std : : find ( std : : begin ( monitorInfo ) , std : : end ( monitorInfo ) , current ) ;
do
{
if ( m_windowMoveHandler . MoveWindowIntoZoneByDirectionAndIndex ( window , vkCode , false /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , * currMonitorInfo ) ) )
{
return true ;
}
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
if ( vkCode = = VK_RIGHT )
{
currMonitorInfo = std : : next ( currMonitorInfo ) ;
if ( currMonitorInfo = = std : : end ( monitorInfo ) )
{
currMonitorInfo = std : : begin ( monitorInfo ) ;
}
}
else if ( vkCode = = VK_LEFT )
{
if ( currMonitorInfo = = std : : begin ( monitorInfo ) )
{
currMonitorInfo = std : : end ( monitorInfo ) ;
}
currMonitorInfo = std : : prev ( currMonitorInfo ) ;
}
} while ( * currMonitorInfo ! = current ) ;
}
else
{
// Single monitor environment, or combined multi-monitor environment.
if ( m_settings - > GetSettings ( ) - > restoreSize )
{
bool moved = m_windowMoveHandler . MoveWindowIntoZoneByDirectionAndIndex ( window , vkCode , false /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
if ( ! moved )
{
FancyZonesUtils : : RestoreWindowOrigin ( window ) ;
FancyZonesUtils : : RestoreWindowSize ( window ) ;
}
return true ;
}
else
{
return m_windowMoveHandler . MoveWindowIntoZoneByDirectionAndIndex ( window , vkCode , true /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
}
}
return false ;
}
bool FancyZones : : OnSnapHotkeyBasedOnPosition ( HWND window , DWORD vkCode ) noexcept
{
HMONITOR current = WorkAreaKeyFromWindow ( window ) ;
auto allMonitors = FancyZonesUtils : : GetAllMonitorRects < & MONITORINFOEX : : rcWork > ( ) ;
if ( current & & allMonitors . size ( ) > 1 & & m_settings - > GetSettings ( ) - > moveWindowAcrossMonitors )
{
// Multi monitor environment.
// First, try to stay on the same monitor
bool success = ProcessDirectedSnapHotkey ( window , vkCode , false , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
if ( success )
{
return true ;
}
// If that didn't work, extract zones from all other monitors and target one of them
std : : vector < RECT > zoneRects ;
2021-08-11 22:05:03 +08:00
std : : vector < std : : pair < ZoneIndex , winrt : : com_ptr < IWorkArea > > > zoneRectsInfo ;
2021-06-23 20:48:54 +08:00
RECT currentMonitorRect { . top = 0 , . bottom = - 1 } ;
for ( const auto & [ monitor , monitorRect ] : allMonitors )
{
if ( monitor = = current )
{
currentMonitorRect = monitorRect ;
}
else
{
auto workArea = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , monitor ) ;
if ( workArea )
{
auto zoneSet = workArea - > ActiveZoneSet ( ) ;
if ( zoneSet )
{
const auto zones = zoneSet - > GetZones ( ) ;
for ( const auto & [ zoneId , zone ] : zones )
{
RECT zoneRect = zone - > GetZoneRect ( ) ;
zoneRect . left + = monitorRect . left ;
zoneRect . right + = monitorRect . left ;
zoneRect . top + = monitorRect . top ;
zoneRect . bottom + = monitorRect . top ;
zoneRects . emplace_back ( zoneRect ) ;
zoneRectsInfo . emplace_back ( zoneId , workArea ) ;
}
}
}
}
}
// Ensure we can get the windowRect, if not, just quit
RECT windowRect ;
if ( ! GetWindowRect ( window , & windowRect ) )
{
return false ;
}
2021-08-11 22:05:03 +08:00
auto chosenIdx = FancyZonesUtils : : ChooseNextZoneByPosition ( vkCode , windowRect , zoneRects ) ;
2021-06-23 20:48:54 +08:00
if ( chosenIdx < zoneRects . size ( ) )
{
// Moving to another monitor succeeded
const auto & [ trueZoneIdx , zoneWindow ] = zoneRectsInfo [ chosenIdx ] ;
m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , { trueZoneIdx } , zoneWindow ) ;
return true ;
}
// We reached the end of all monitors.
// Try again, cycling on all monitors.
// First, add zones from the origin monitor to zoneRects
// Sanity check: the current monitor is valid
if ( currentMonitorRect . top < = currentMonitorRect . bottom )
{
auto workArea = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ;
if ( workArea )
{
auto zoneSet = workArea - > ActiveZoneSet ( ) ;
if ( zoneSet )
{
const auto zones = zoneSet - > GetZones ( ) ;
for ( const auto & [ zoneId , zone ] : zones )
{
RECT zoneRect = zone - > GetZoneRect ( ) ;
zoneRect . left + = currentMonitorRect . left ;
zoneRect . right + = currentMonitorRect . left ;
zoneRect . top + = currentMonitorRect . top ;
zoneRect . bottom + = currentMonitorRect . top ;
zoneRects . emplace_back ( zoneRect ) ;
zoneRectsInfo . emplace_back ( zoneId , workArea ) ;
}
}
}
}
else
{
return false ;
}
RECT combinedRect = FancyZonesUtils : : GetAllMonitorsCombinedRect < & MONITORINFOEX : : rcWork > ( ) ;
windowRect = FancyZonesUtils : : PrepareRectForCycling ( windowRect , combinedRect , vkCode ) ;
chosenIdx = FancyZonesUtils : : ChooseNextZoneByPosition ( vkCode , windowRect , zoneRects ) ;
if ( chosenIdx < zoneRects . size ( ) )
{
// Moving to another monitor succeeded
const auto & [ trueZoneIdx , zoneWindow ] = zoneRectsInfo [ chosenIdx ] ;
m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , { trueZoneIdx } , zoneWindow ) ;
return true ;
}
else
{
// Giving up
return false ;
}
}
else
{
// Single monitor environment, or combined multi-monitor environment.
return ProcessDirectedSnapHotkey ( window , vkCode , true , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
}
}
bool FancyZones : : OnSnapHotkey ( DWORD vkCode ) noexcept
{
// We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning
auto window = GetForegroundWindow ( ) ;
if ( m_settings - > GetSettings ( ) - > moveWindowsBasedOnPosition )
{
return OnSnapHotkeyBasedOnPosition ( window , vkCode ) ;
}
else
{
return ( vkCode = = VK_LEFT | | vkCode = = VK_RIGHT ) & & OnSnapHotkeyBasedOnZoneNumber ( window , vkCode ) ;
}
return false ;
}
2021-07-07 18:18:52 +08:00
bool FancyZones : : ProcessDirectedSnapHotkey ( HWND window , DWORD vkCode , bool cycle , winrt : : com_ptr < IWorkArea > zoneWindow ) noexcept
2021-06-23 20:48:54 +08:00
{
// Check whether Alt is used in the shortcut key combination
if ( GetAsyncKeyState ( VK_MENU ) & 0x8000 )
{
return m_windowMoveHandler . ExtendWindowByDirectionAndPosition ( window , vkCode , zoneWindow ) ;
}
else
{
return m_windowMoveHandler . MoveWindowIntoZoneByDirectionAndPosition ( window , vkCode , cycle , zoneWindow ) ;
}
}
2021-07-07 18:18:52 +08:00
void FancyZones : : RegisterVirtualDesktopUpdates ( ) noexcept
2021-06-23 20:48:54 +08:00
{
_TRACER_ ;
2021-07-07 18:18:52 +08:00
auto guids = m_virtualDesktop . GetVirtualDesktopIds ( ) ;
std : : vector < std : : wstring > guidStrings { } ;
if ( guids . has_value ( ) )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
m_workAreaHandler . RegisterUpdates ( * guids ) ;
for ( auto & guid : * guids )
{
auto guidString = FancyZonesUtils : : GuidToString ( guid ) ;
if ( guidString . has_value ( ) )
{
guidStrings . push_back ( * guidString ) ;
}
}
if ( ! guidStrings . empty ( ) )
{
FancyZonesDataInstance ( ) . UpdatePrimaryDesktopData ( guidStrings [ 0 ] ) ;
}
FancyZonesDataInstance ( ) . RemoveDeletedDesktops ( guidStrings ) ;
2021-06-23 20:48:54 +08:00
}
}
2021-07-07 18:18:52 +08:00
void FancyZones : : OnSettingsChanged ( ) noexcept
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
_TRACER_ ;
m_settings - > ReloadSettings ( ) ;
// Update the hotkey
UnregisterHotKey ( m_window , 1 ) ;
auto modifiers = m_settings - > GetSettings ( ) - > editorHotkey . get_modifiers ( ) ;
auto code = m_settings - > GetSettings ( ) - > editorHotkey . get_code ( ) ;
auto result = RegisterHotKey ( m_window , 1 , modifiers , code ) ;
if ( ! result )
2021-06-23 20:48:54 +08:00
{
2021-07-07 18:18:52 +08:00
Logger : : error ( L " Failed to register hotkey: {} " , get_last_error_or_default ( GetLastError ( ) ) ) ;
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
// Needed if we toggled spanZonesAcrossMonitors
m_workAreaHandler . Clear ( ) ;
// update zone colors
m_workAreaHandler . UpdateZoneColors ( GetZoneColors ( ) ) ;
// update overlapping algorithm
m_workAreaHandler . UpdateOverlappingAlgorithm ( m_settings - > GetSettings ( ) - > overlappingZonesAlgorithm ) ;
PostMessageW ( m_window , WM_PRIV_VD_INIT , NULL , NULL ) ;
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
void FancyZones : : OnEditorExitEvent ( ) noexcept
2021-06-23 20:48:54 +08:00
{
// Collect information about changes in zone layout after editor exited.
FancyZonesDataInstance ( ) . LoadFancyZonesData ( ) ;
2021-07-07 18:18:52 +08:00
UpdateZoneSets ( ) ;
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
void FancyZones : : UpdateZoneSets ( ) noexcept
2021-06-23 20:48:54 +08:00
{
for ( auto workArea : m_workAreaHandler . GetAllWorkAreas ( ) )
{
workArea - > UpdateActiveZoneSet ( ) ;
}
if ( m_settings - > GetSettings ( ) - > zoneSetChange_moveWindows )
{
2021-07-07 18:18:52 +08:00
UpdateWindowsPositions ( ) ;
2021-06-23 20:48:54 +08:00
}
}
bool FancyZones : : ShouldProcessSnapHotkey ( DWORD vkCode ) noexcept
{
auto window = GetForegroundWindow ( ) ;
if ( m_settings - > GetSettings ( ) - > overrideSnapHotkeys & & FancyZonesUtils : : IsCandidateForZoning ( window , m_settings - > GetSettings ( ) - > excludedAppsArray ) )
{
HMONITOR monitor = WorkAreaKeyFromWindow ( window ) ;
auto zoneWindow = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , monitor ) ;
if ( zoneWindow & & zoneWindow - > ActiveZoneSet ( ) & & zoneWindow - > ActiveZoneSet ( ) - > LayoutType ( ) ! = FancyZonesDataTypes : : ZoneSetLayoutType : : Blank )
{
if ( vkCode = = VK_UP | | vkCode = = VK_DOWN )
{
return m_settings - > GetSettings ( ) - > moveWindowsBasedOnPosition ;
}
else
{
return true ;
}
}
}
return false ;
}
void FancyZones : : ApplyQuickLayout ( int key ) noexcept
{
std : : wstring uuid ;
for ( auto [ zoneUuid , hotkey ] : FancyZonesDataInstance ( ) . GetLayoutQuickKeys ( ) )
{
if ( hotkey = = key )
{
uuid = zoneUuid ;
}
}
auto workArea = m_workAreaHandler . GetWorkAreaFromCursor ( m_currentDesktopId ) ;
// Find a custom zone set with this uuid and apply it
auto customZoneSets = FancyZonesDataInstance ( ) . GetCustomZoneSetsMap ( ) ;
if ( ! customZoneSets . contains ( uuid ) )
{
return ;
}
FancyZonesDataTypes : : ZoneSetData data { . uuid = uuid , . type = FancyZonesDataTypes : : ZoneSetLayoutType : : Custom } ;
FancyZonesDataInstance ( ) . SetActiveZoneSet ( workArea - > UniqueId ( ) , data ) ;
FancyZonesDataInstance ( ) . SaveZoneSettings ( ) ;
2021-07-07 18:18:52 +08:00
UpdateZoneSets ( ) ;
FlashZones ( ) ;
2021-06-23 20:48:54 +08:00
}
2021-07-07 18:18:52 +08:00
void FancyZones : : FlashZones ( ) noexcept
2021-06-23 20:48:54 +08:00
{
if ( m_settings - > GetSettings ( ) - > flashZonesOnQuickSwitch & & ! m_windowMoveHandler . IsDragEnabled ( ) )
{
for ( auto [ monitor , workArea ] : m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) )
{
workArea - > FlashZones ( ) ;
}
}
}
std : : vector < HMONITOR > FancyZones : : GetMonitorsSorted ( ) noexcept
{
auto monitorInfo = GetRawMonitorData ( ) ;
FancyZonesUtils : : OrderMonitors ( monitorInfo ) ;
std : : vector < HMONITOR > output ;
std : : transform ( std : : begin ( monitorInfo ) , std : : end ( monitorInfo ) , std : : back_inserter ( output ) , [ ] ( const auto & info ) { return info . first ; } ) ;
return output ;
}
std : : vector < std : : pair < HMONITOR , RECT > > FancyZones : : GetRawMonitorData ( ) noexcept
{
_TRACER_ ;
2021-07-07 18:18:52 +08:00
2021-06-23 20:48:54 +08:00
std : : vector < std : : pair < HMONITOR , RECT > > monitorInfo ;
const auto & activeWorkAreaMap = m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ;
for ( const auto & [ monitor , workArea ] : activeWorkAreaMap )
{
if ( workArea - > ActiveZoneSet ( ) ! = nullptr )
{
MONITORINFOEX mi ;
mi . cbSize = sizeof ( mi ) ;
GetMonitorInfo ( monitor , & mi ) ;
monitorInfo . push_back ( { monitor , mi . rcMonitor } ) ;
}
}
return monitorInfo ;
}
HMONITOR FancyZones : : WorkAreaKeyFromWindow ( HWND window ) noexcept
{
if ( m_settings - > GetSettings ( ) - > spanZonesAcrossMonitors )
{
return NULL ;
}
else
{
return MonitorFromWindow ( window , MONITOR_DEFAULTTONULL ) ;
}
}
2021-07-07 18:18:52 +08:00
ZoneColors FancyZones : : GetZoneColors ( ) const noexcept
{
return ZoneColors {
. primaryColor = FancyZonesUtils : : HexToRGB ( m_settings - > GetSettings ( ) - > zoneColor ) ,
. borderColor = FancyZonesUtils : : HexToRGB ( m_settings - > GetSettings ( ) - > zoneBorderColor ) ,
. highlightColor = FancyZonesUtils : : HexToRGB ( m_settings - > GetSettings ( ) - > zoneHighlightColor ) ,
. highlightOpacity = m_settings - > GetSettings ( ) - > zoneHighlightOpacity
} ;
}
2021-06-23 20:48:54 +08:00
winrt : : com_ptr < IFancyZones > MakeFancyZones ( HINSTANCE hinstance ,
const winrt : : com_ptr < IFancyZonesSettings > & settings ,
std : : function < void ( ) > disableCallback ) noexcept
{
if ( ! settings )
{
return nullptr ;
}
return winrt : : make_self < FancyZones > ( hinstance , settings , disableCallback ) ;
}