2019-09-04 18:26:26 +02:00
# include "pch.h"
2020-05-04 17:29:48 +03:00
# include <common/dpi_aware.h>
# include <common/on_thread_executor.h>
# include <common/window_helpers.h>
2019-09-04 18:26:26 +02:00
2019-12-17 11:21:46 +03:00
# include "FancyZones.h"
# include "lib/Settings.h"
# include "lib/ZoneWindow.h"
2020-02-10 14:59:51 +01:00
# include "lib/JsonHelpers.h"
# include "lib/ZoneSet.h"
2020-04-30 13:16:08 +03:00
# include "lib/WindowMoveHandler.h"
2020-05-04 17:29:48 +03:00
# include "lib/FancyZonesWinHookEventIDs.h"
# include "lib/util.h"
2019-12-17 11:21:46 +03:00
# include "trace.h"
2020-04-21 19:57:21 +02:00
# include "VirtualDesktopUtils.h"
2020-05-31 12:36:45 +02:00
# include "MonitorWorkAreaHandler.h"
2019-12-17 11:21:46 +03:00
2020-05-04 17:29:48 +03:00
# include <interface/win_hook_event_data.h>
2020-06-05 17:53:08 +03:00
# include <lib/SecondaryMouseButtonsHook.h>
2020-07-10 11:06:01 +02:00
# include <lib/GenericKeyHook.h>
2020-02-10 14:59:51 +01:00
enum class DisplayChangeType
{
WorkArea ,
DisplayChange ,
VirtualDesktop ,
Initialization
} ;
2019-12-12 10:10:55 +01:00
2020-07-08 10:37:42 +02:00
namespace
{
constexpr int CUSTOM_POSITIONING_LEFT_TOP_PADDING = 16 ;
}
2019-09-04 18:26:26 +02:00
struct FancyZones : public winrt : : implements < FancyZones , IFancyZones , IFancyZonesCallback , IZoneWindowHost >
{
public :
2020-02-10 14:59:51 +01:00
FancyZones ( HINSTANCE hinstance , const winrt : : com_ptr < IFancyZonesSettings > & settings ) noexcept :
m_hinstance ( hinstance ) ,
2020-04-30 13:16:08 +03:00
m_settings ( settings ) ,
2020-06-05 17:53:08 +03:00
m_mouseHook ( std : : bind ( & FancyZones : : OnMouseDown , this ) ) ,
2020-06-17 16:06:16 +03:00
m_shiftHook ( std : : bind ( & FancyZones : : OnShiftChangeState , this , std : : placeholders : : _1 ) ) ,
2020-07-10 11:06:01 +02:00
m_ctrlHook ( std : : bind ( & FancyZones : : OnCtrlChangeState , this , std : : placeholders : : _1 ) ) ,
m_windowMoveHandler ( settings , & m_mouseHook , & m_shiftHook , & m_ctrlHook )
2019-09-04 18:26:26 +02:00
{
m_settings - > SetCallback ( this ) ;
}
// IFancyZones
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
Run ( ) noexcept ;
IFACEMETHODIMP_ ( void )
Destroy ( ) noexcept ;
2019-09-04 18:26:26 +02:00
2020-06-05 17:53:08 +03:00
void OnMouseDown ( ) noexcept
{
m_windowMoveHandler . OnMouseDown ( ) ;
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , NULL , NULL ) ;
}
2020-06-17 16:06:16 +03:00
void OnShiftChangeState ( bool state ) noexcept
{
m_windowMoveHandler . OnShiftChangeState ( state ) ;
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , NULL , NULL ) ;
}
2020-07-10 11:06:01 +02:00
void OnCtrlChangeState ( bool state ) noexcept
{
m_windowMoveHandler . OnCtrlChangeState ( state ) ;
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , NULL , NULL ) ;
}
2020-05-04 21:10:23 +03:00
void MoveSizeStart ( HWND window , HMONITOR monitor , POINT const & ptScreen ) noexcept
2020-04-30 13:16:08 +03:00
{
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
m_windowMoveHandler . MoveSizeStart ( window , monitor , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
2020-04-30 13:16:08 +03:00
}
2020-05-27 16:52:59 +02:00
2020-05-04 21:10:23 +03:00
void MoveSizeUpdate ( HMONITOR monitor , POINT const & ptScreen ) noexcept
2020-04-30 13:16:08 +03:00
{
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
m_windowMoveHandler . MoveSizeUpdate ( monitor , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
2020-04-30 13:16:08 +03:00
}
2020-05-27 16:52:59 +02:00
2020-05-04 21:10:23 +03:00
void MoveSizeEnd ( HWND window , POINT const & ptScreen ) noexcept
2020-04-30 13:16:08 +03:00
{
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
m_windowMoveHandler . MoveSizeEnd ( window , ptScreen , m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ) ;
2020-05-04 17:29:48 +03:00
}
2020-06-05 17:53:08 +03:00
2020-05-04 17:29:48 +03:00
IFACEMETHODIMP_ ( void )
HandleWinHookEvent ( const WinHookEvent * data ) noexcept
{
const auto wparam = reinterpret_cast < WPARAM > ( data - > hwnd ) ;
const LONG lparam = 0 ;
std : : shared_lock readLock ( m_lock ) ;
switch ( data - > event )
{
case EVENT_SYSTEM_MOVESIZESTART :
PostMessageW ( m_window , WM_PRIV_MOVESIZESTART , wparam , lparam ) ;
break ;
case EVENT_SYSTEM_MOVESIZEEND :
PostMessageW ( m_window , WM_PRIV_MOVESIZEEND , wparam , lparam ) ;
break ;
case EVENT_OBJECT_LOCATIONCHANGE :
PostMessageW ( m_window , WM_PRIV_LOCATIONCHANGE , wparam , lparam ) ;
break ;
case EVENT_OBJECT_NAMECHANGE :
PostMessageW ( m_window , WM_PRIV_NAMECHANGE , wparam , lparam ) ;
break ;
case EVENT_OBJECT_UNCLOAKED :
case EVENT_OBJECT_SHOW :
case EVENT_OBJECT_CREATE :
if ( data - > idObject = = OBJID_WINDOW )
{
PostMessageW ( m_window , WM_PRIV_WINDOWCREATED , wparam , lparam ) ;
}
break ;
}
2020-04-30 13:16:08 +03:00
}
2020-05-04 17:29:48 +03:00
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
VirtualDesktopChanged ( ) noexcept ;
IFACEMETHODIMP_ ( void )
VirtualDesktopInitialize ( ) noexcept ;
IFACEMETHODIMP_ ( bool )
OnKeyDown ( PKBDLLHOOKSTRUCT info ) noexcept ;
IFACEMETHODIMP_ ( void )
ToggleEditor ( ) noexcept ;
IFACEMETHODIMP_ ( void )
SettingsChanged ( ) noexcept ;
2020-05-27 16:52:59 +02:00
2020-05-04 21:10:23 +03:00
void WindowCreated ( HWND window ) noexcept ;
2019-09-04 18:26:26 +02:00
// IZoneWindowHost
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
MoveWindowsOnActiveZoneSetChange ( ) noexcept ;
IFACEMETHODIMP_ ( COLORREF )
2020-03-25 15:38:44 +01:00
GetZoneColor ( ) noexcept
{
// Skip the leading # and convert to long
const auto color = m_settings - > GetSettings ( ) - > zoneColor ;
const auto tmp = std : : stol ( color . substr ( 1 ) , nullptr , 16 ) ;
const auto nR = ( tmp & 0xFF0000 ) > > 16 ;
const auto nG = ( tmp & 0xFF00 ) > > 8 ;
const auto nB = ( tmp & 0xFF ) ;
return RGB ( nR , nG , nB ) ;
}
IFACEMETHODIMP_ ( COLORREF )
GetZoneBorderColor ( ) noexcept
{
// Skip the leading # and convert to long
const auto color = m_settings - > GetSettings ( ) - > zoneBorderColor ;
const auto tmp = std : : stol ( color . substr ( 1 ) , nullptr , 16 ) ;
const auto nR = ( tmp & 0xFF0000 ) > > 16 ;
const auto nG = ( tmp & 0xFF00 ) > > 8 ;
const auto nB = ( tmp & 0xFF ) ;
return RGB ( nR , nG , nB ) ;
}
IFACEMETHODIMP_ ( COLORREF )
2020-02-17 18:28:49 +03:00
GetZoneHighlightColor ( ) noexcept
2019-09-04 18:26:26 +02:00
{
// Skip the leading # and convert to long
2020-05-27 10:55:46 -04:00
const auto color = m_settings - > GetSettings ( ) - > zoneHighlightColor ;
2019-09-04 18:26:26 +02:00
const auto tmp = std : : stol ( color . substr ( 1 ) , nullptr , 16 ) ;
const auto nR = ( tmp & 0xFF0000 ) > > 16 ;
const auto nG = ( tmp & 0xFF00 ) > > 8 ;
const auto nB = ( tmp & 0xFF ) ;
return RGB ( nR , nG , nB ) ;
}
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( int )
GetZoneHighlightOpacity ( ) noexcept
2020-01-06 09:59:18 -08:00
{
2020-03-13 12:55:15 +03:00
return m_settings - > GetSettings ( ) - > zoneHighlightOpacity ;
2020-01-06 09:59:18 -08:00
}
2019-09-04 18:26:26 +02:00
2020-03-25 15:38:44 +01:00
IFACEMETHODIMP_ ( bool )
isMakeDraggedWindowTransparentActive ( ) noexcept
{
return m_settings - > GetSettings ( ) - > makeDraggedWindowTransparent ;
}
2020-05-22 16:42:29 +02:00
IFACEMETHODIMP_ ( bool )
InMoveSize ( ) noexcept
{
std : : shared_lock readLock ( m_lock ) ;
return m_windowMoveHandler . InMoveSize ( ) ;
}
2019-09-04 18:26:26 +02:00
LRESULT WndProc ( HWND , UINT , WPARAM , LPARAM ) noexcept ;
void OnDisplayChange ( DisplayChangeType changeType ) noexcept ;
void AddZoneWindow ( HMONITOR monitor , PCWSTR deviceId ) noexcept ;
2020-03-10 09:59:34 +01:00
2019-09-04 18:26:26 +02:00
protected :
static LRESULT CALLBACK s_WndProc ( HWND , UINT , WPARAM , LPARAM ) noexcept ;
private :
struct require_read_lock
{
template < typename T >
2020-02-10 14:59:51 +01:00
require_read_lock ( const std : : shared_lock < T > & lock )
{
lock ;
}
2019-09-04 18:26:26 +02:00
template < typename T >
2020-02-10 14:59:51 +01:00
require_read_lock ( const std : : unique_lock < T > & lock )
{
lock ;
}
2019-09-04 18:26:26 +02:00
} ;
struct require_write_lock
{
template < typename T >
2020-02-10 14:59:51 +01:00
require_write_lock ( const std : : unique_lock < T > & lock )
{
lock ;
}
2019-09-04 18:26:26 +02:00
} ;
void UpdateZoneWindows ( ) noexcept ;
2020-05-06 17:16:16 +02:00
void UpdateWindowsPositions ( ) noexcept ;
2019-09-04 18:26:26 +02:00
void CycleActiveZoneSet ( DWORD vkCode ) noexcept ;
2020-02-06 13:12:59 +01:00
bool OnSnapHotkey ( DWORD vkCode ) noexcept ;
2020-05-04 17:29:48 +03:00
2020-05-01 16:13:16 +02:00
void RegisterVirtualDesktopUpdates ( std : : vector < GUID > & ids ) noexcept ;
2020-05-27 16:52:59 +02:00
bool IsSplashScreen ( HWND window ) ;
2020-06-17 11:55:14 +02:00
bool ShouldProcessNewWindow ( HWND window ) noexcept ;
std : : vector < int > GetZoneIndexSetFromWorkAreaHistory ( HWND window , winrt : : com_ptr < IZoneWindow > workArea ) noexcept ;
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > GetAppZoneHistoryInfo ( HWND window , HMONITOR monitor , std : : unordered_map < HMONITOR , winrt : : com_ptr < IZoneWindow > > & workAreaMap ) noexcept ;
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > GetAppZoneHistoryInfo ( HWND window , HMONITOR monitor , bool isPrimaryMonitor ) noexcept ;
void MoveWindowIntoZone ( HWND window , winrt : : com_ptr < IZoneWindow > zoneWindow , const std : : vector < int > & zoneIndexSet ) noexcept ;
2020-05-27 16:52:59 +02:00
2020-02-10 14:59:51 +01:00
void OnEditorExitEvent ( ) noexcept ;
2020-06-17 11:55:14 +02:00
bool ShouldProcessSnapHotkey ( ) noexcept ;
2019-09-04 18:26:26 +02:00
2020-03-24 18:50:26 +01:00
std : : vector < std : : pair < HMONITOR , RECT > > GetRawMonitorData ( ) noexcept ;
std : : vector < HMONITOR > GetMonitorsSorted ( ) noexcept ;
2019-09-04 18:26:26 +02:00
const HINSTANCE m_hinstance { } ;
mutable std : : shared_mutex m_lock ;
HWND m_window { } ;
2020-04-30 13:16:08 +03:00
WindowMoveHandler m_windowMoveHandler ;
2020-05-31 12:36:45 +02:00
MonitorWorkAreaHandler m_workAreaHandler ;
2020-06-05 17:53:08 +03:00
SecondaryMouseButtonsHook m_mouseHook ;
2020-06-17 16:06:16 +03:00
ShiftKeyHook m_shiftHook ;
2020-07-10 11:06:01 +02:00
CtrlKeyHook m_ctrlHook ;
2020-05-04 17:29:48 +03:00
2020-02-10 14:59:51 +01:00
winrt : : com_ptr < IFancyZonesSettings > m_settings { } ;
2020-05-31 12:36:45 +02:00
GUID m_previousDesktopId { } ; // UUID of previously active virtual desktop.
GUID m_currentDesktopId { } ; // UUID of the current virtual desktop.
2019-11-18 15:29:42 -08:00
wil : : unique_handle m_terminateEditorEvent ; // Handle of FancyZonesEditor.exe we launch and wait on
2019-12-12 10:10:55 +01:00
wil : : unique_handle m_terminateVirtualDesktopTrackerEvent ;
2019-09-04 18:26:26 +02:00
2019-11-07 21:56:32 +03:00
OnThreadExecutor m_dpiUnawareThread ;
2019-12-12 10:10:55 +01:00
OnThreadExecutor m_virtualDesktopTrackerThread ;
2019-11-07 21:56:32 +03:00
2020-05-04 21:10:23 +03:00
static UINT WM_PRIV_VD_INIT ; // Scheduled when FancyZones is initialized
static UINT WM_PRIV_VD_SWITCH ; // Scheduled when virtual desktop switch occurs
static UINT WM_PRIV_VD_UPDATE ; // Scheduled on virtual desktops update (creation/deletion)
static UINT WM_PRIV_EDITOR ; // Scheduled when the editor exits
static UINT WM_PRIV_LOWLEVELKB ; // Scheduled when we receive a key down press
2019-09-04 18:26:26 +02:00
2019-11-18 15:29:42 -08:00
// Did we terminate the editor or was it closed cleanly?
2019-09-04 18:26:26 +02:00
enum class EditorExitKind : byte
{
Exit ,
Terminate
} ;
} ;
2020-05-01 16:13:16 +02:00
UINT FancyZones : : WM_PRIV_VD_INIT = RegisterWindowMessage ( L " {469818a8-00fa-4069-b867-a1da484fcd9a} " ) ;
UINT FancyZones : : WM_PRIV_VD_SWITCH = RegisterWindowMessage ( L " {128c2cb0-6bdf-493e-abbe-f8705e04aa95} " ) ;
UINT FancyZones : : WM_PRIV_VD_UPDATE = RegisterWindowMessage ( L " {b8b72b46-f42f-4c26-9e20-29336cf2f22e} " ) ;
2019-09-04 18:26:26 +02:00
UINT FancyZones : : WM_PRIV_EDITOR = RegisterWindowMessage ( L " {87543824-7080-4e91-9d9c-0404642fc7b6} " ) ;
2020-05-04 21:10:23 +03:00
UINT FancyZones : : WM_PRIV_LOWLEVELKB = RegisterWindowMessage ( L " {763c03a3-03d9-4cde-8d71-f0358b0b4b52} " ) ;
2019-09-04 18:26:26 +02:00
// IFancyZones
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : Run ( ) noexcept
2019-09-04 18:26:26 +02:00
{
std : : unique_lock writeLock ( m_lock ) ;
WNDCLASSEXW wcex { } ;
wcex . cbSize = sizeof ( WNDCLASSEX ) ;
wcex . lpfnWndProc = s_WndProc ;
wcex . hInstance = m_hinstance ;
wcex . lpszClassName = L " SuperFancyZones " ;
RegisterClassExW ( & wcex ) ;
BufferedPaintInit ( ) ;
m_window = CreateWindowExW ( WS_EX_TOOLWINDOW , L " SuperFancyZones " , L " " , WS_POPUP , 0 , 0 , 0 , 0 , nullptr , nullptr , m_hinstance , this ) ;
2020-02-10 14:59:51 +01:00
if ( ! m_window )
return ;
2019-09-04 18:26:26 +02:00
2020-03-13 12:55:15 +03:00
RegisterHotKey ( m_window , 1 , m_settings - > GetSettings ( ) - > editorHotkey . get_modifiers ( ) , m_settings - > GetSettings ( ) - > editorHotkey . get_code ( ) ) ;
2019-11-18 15:29:42 -08:00
2019-12-24 15:47:28 +01:00
VirtualDesktopInitialize ( ) ;
2019-11-07 21:56:32 +03:00
2020-02-10 14:59:51 +01:00
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ ] {
SetThreadDpiAwarenessContext ( DPI_AWARENESS_CONTEXT_UNAWARE ) ;
SetThreadDpiHostingBehavior ( DPI_HOSTING_BEHAVIOR_MIXED ) ;
} } )
. wait ( ) ;
2019-12-12 10:10:55 +01:00
2020-05-01 16:13:16 +02:00
m_terminateVirtualDesktopTrackerEvent . reset ( CreateEvent ( nullptr , FALSE , FALSE , nullptr ) ) ;
2020-05-04 17:29:48 +03:00
m_virtualDesktopTrackerThread . submit ( OnThreadExecutor : : task_t { [ & ] { VirtualDesktopUtils : : HandleVirtualDesktopUpdates ( m_window , WM_PRIV_VD_UPDATE , m_terminateVirtualDesktopTrackerEvent . get ( ) ) ; } } ) ;
2019-09-04 18:26:26 +02:00
}
// IFancyZones
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : Destroy ( ) noexcept
2019-09-04 18:26:26 +02:00
{
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
m_workAreaHandler . Clear ( ) ;
2019-09-04 18:26:26 +02:00
BufferedPaintUnInit ( ) ;
if ( m_window )
{
DestroyWindow ( m_window ) ;
m_window = nullptr ;
}
2020-02-10 14:59:51 +01:00
if ( m_terminateVirtualDesktopTrackerEvent )
{
2019-12-12 10:10:55 +01:00
SetEvent ( m_terminateVirtualDesktopTrackerEvent . get ( ) ) ;
}
2019-09-04 18:26:26 +02:00
}
// IFancyZonesCallback
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : VirtualDesktopChanged ( ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-05-04 17:29:48 +03:00
// VirtualDesktopChanged is called from a reentrant WinHookProc function, therefore we must postpone the actual logic
// until we're in FancyZones::WndProc, which is not reentrant.
2020-05-01 16:13:16 +02:00
PostMessage ( m_window , WM_PRIV_VD_SWITCH , 0 , 0 ) ;
2019-09-04 18:26:26 +02:00
}
2019-12-24 15:47:28 +01:00
// IFancyZonesCallback
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : VirtualDesktopInitialize ( ) noexcept
2019-12-24 15:47:28 +01:00
{
2020-05-01 16:13:16 +02:00
PostMessage ( m_window , WM_PRIV_VD_INIT , 0 , 0 ) ;
2019-12-24 15:47:28 +01:00
}
2020-06-17 11:55:14 +02:00
bool FancyZones : : ShouldProcessNewWindow ( HWND window ) noexcept
{
// Avoid processing splash screens, already stamped (zoned) windows, or those windows
// that belong to excluded applications list.
if ( IsSplashScreen ( window ) | |
( reinterpret_cast < size_t > ( : : GetProp ( window , MULTI_ZONE_STAMP ) ) ! = 0 ) | |
! IsInterestingWindow ( window , m_settings - > GetSettings ( ) - > excludedAppsArray ) )
{
return false ;
}
return true ;
}
std : : vector < int > FancyZones : : GetZoneIndexSetFromWorkAreaHistory (
HWND window , winrt : : com_ptr < IZoneWindow > workArea ) noexcept
{
const auto activeZoneSet = workArea - > ActiveZoneSet ( ) ;
if ( activeZoneSet )
{
wil : : unique_cotaskmem_string zoneSetId ;
if ( SUCCEEDED ( StringFromCLSID ( activeZoneSet - > Id ( ) , & zoneSetId ) ) )
{
return JSONHelpers : : FancyZonesDataInstance ( ) . GetAppLastZoneIndexSet ( window , workArea - > UniqueId ( ) , zoneSetId . get ( ) ) ;
}
}
return { } ;
}
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > FancyZones : : GetAppZoneHistoryInfo (
HWND window ,
HMONITOR monitor ,
std : : unordered_map < HMONITOR , winrt : : com_ptr < IZoneWindow > > & workAreaMap ) noexcept
{
if ( workAreaMap . contains ( monitor ) )
{
auto workArea = workAreaMap [ monitor ] ;
workAreaMap . erase ( monitor ) ; // monitor processed, remove entry from the map
return { workArea , GetZoneIndexSetFromWorkAreaHistory ( window , workArea ) } ;
}
return { nullptr , { } } ;
}
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > FancyZones : : GetAppZoneHistoryInfo ( HWND window , HMONITOR monitor , bool isPrimaryMonitor ) noexcept
{
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > appZoneHistoryInfo { nullptr , { } } ;
auto workAreaMap = m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ;
// Search application history on currently active monitor.
appZoneHistoryInfo = GetAppZoneHistoryInfo ( window , monitor , workAreaMap ) ;
if ( isPrimaryMonitor & & appZoneHistoryInfo . second . empty ( ) )
{
// No application history on primary monitor, search on remaining monitors.
for ( const auto & [ monitor , workArea ] : workAreaMap )
{
auto zoneIndexSet = GetZoneIndexSetFromWorkAreaHistory ( window , workArea ) ;
if ( ! zoneIndexSet . empty ( ) )
{
return { workArea , zoneIndexSet } ;
}
}
}
return appZoneHistoryInfo ;
}
void FancyZones : : MoveWindowIntoZone ( HWND window , winrt : : com_ptr < IZoneWindow > zoneWindow , const std : : vector < int > & zoneIndexSet ) noexcept
{
auto & fancyZonesData = JSONHelpers : : FancyZonesDataInstance ( ) ;
if ( ! fancyZonesData . IsAnotherWindowOfApplicationInstanceZoned ( window , zoneWindow - > UniqueId ( ) ) )
{
m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , zoneIndexSet , zoneWindow ) ;
fancyZonesData . UpdateProcessIdToHandleMap ( window , zoneWindow - > UniqueId ( ) ) ;
}
}
2020-07-08 10:37:42 +02:00
inline int RectWidth ( const RECT & rect )
{
return rect . right - rect . left ;
}
inline int RectHeight ( const RECT & rect )
{
return rect . bottom - rect . top ;
}
RECT FitOnScreen ( const RECT & windowRect , const RECT & originMonitorRect , const RECT & destMonitorRect )
{
// New window position on active monitor. If window fits the screen, this will be final position.
int left = destMonitorRect . left + ( windowRect . left - originMonitorRect . left ) ;
int top = destMonitorRect . top + ( windowRect . top - originMonitorRect . top ) ;
int W = RectWidth ( windowRect ) ;
int H = RectHeight ( windowRect ) ;
if ( ( left < destMonitorRect . left ) | | ( left + W > destMonitorRect . right ) )
{
// Set left window border to left border of screen (add padding). Resize window width if needed.
left = destMonitorRect . left + CUSTOM_POSITIONING_LEFT_TOP_PADDING ;
W = min ( W , RectWidth ( destMonitorRect ) - CUSTOM_POSITIONING_LEFT_TOP_PADDING ) ;
}
if ( ( top < destMonitorRect . top ) | | ( top + H > destMonitorRect . bottom ) )
{
// Set top window border to top border of screen (add padding). Resize window height if needed.
top = destMonitorRect . top + CUSTOM_POSITIONING_LEFT_TOP_PADDING ;
H = min ( H , RectHeight ( destMonitorRect ) - CUSTOM_POSITIONING_LEFT_TOP_PADDING ) ;
}
return { . left = left ,
. top = top ,
. right = left + W ,
. bottom = top + H } ;
}
void OpenWindowOnActiveMonitor ( HWND window , HMONITOR monitor ) noexcept
{
// By default Windows opens new window on primary monitor.
// Try to preserve window width and height, adjust top-left corner if needed.
HMONITOR origin = MonitorFromWindow ( window , MONITOR_DEFAULTTOPRIMARY ) ;
if ( origin = = monitor )
{
// Certain applications by design open in last known position, regardless of FancyZones.
// If that position is on currently active monitor, skip custom positioning.
return ;
}
WINDOWPLACEMENT placement { } ;
if ( GetWindowPlacement ( window , & placement ) )
{
MONITORINFOEX originMi ;
originMi . cbSize = sizeof ( originMi ) ;
if ( GetMonitorInfo ( origin , & originMi ) )
{
MONITORINFOEX destMi ;
destMi . cbSize = sizeof ( destMi ) ;
if ( GetMonitorInfo ( monitor , & destMi ) )
{
RECT newPosition = FitOnScreen ( placement . rcNormalPosition , originMi . rcWork , destMi . rcWork ) ;
SizeWindowToRect ( window , newPosition ) ;
}
}
}
}
2019-09-04 18:26:26 +02:00
// IFancyZonesCallback
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : WindowCreated ( HWND window ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-04-21 19:57:21 +02:00
std : : shared_lock readLock ( m_lock ) ;
2020-07-20 11:55:19 +02:00
GUID desktopId { } ;
if ( VirtualDesktopUtils : : GetWindowDesktopId ( window , & desktopId ) & & desktopId ! = m_currentDesktopId )
{
// Switch between virtual desktops results with posting same windows messages that also indicate
// creation of new window. We need to check if window being processed is on currently active desktop.
return ;
}
2020-07-08 10:37:42 +02:00
const bool moveToAppLastZone = m_settings - > GetSettings ( ) - > appLastZone_moveWindows ;
const bool openOnActiveMonitor = m_settings - > GetSettings ( ) - > openWindowOnActiveMonitor ;
if ( ( moveToAppLastZone | | openOnActiveMonitor ) & & ShouldProcessNewWindow ( window ) )
2019-09-04 18:26:26 +02:00
{
2020-06-17 11:55:14 +02:00
HMONITOR primary = MonitorFromWindow ( nullptr , MONITOR_DEFAULTTOPRIMARY ) ;
HMONITOR active = primary ;
POINT cursorPosition { } ;
if ( GetCursorPos ( & cursorPosition ) )
2019-09-04 18:26:26 +02:00
{
2020-06-17 11:55:14 +02:00
active = MonitorFromPoint ( cursorPosition , MONITOR_DEFAULTTOPRIMARY ) ;
}
2020-02-10 14:59:51 +01:00
2020-07-08 10:37:42 +02:00
bool windowZoned { false } ;
if ( moveToAppLastZone )
{
const bool primaryActive = ( primary = = active ) ;
std : : pair < winrt : : com_ptr < IZoneWindow > , std : : vector < int > > appZoneHistoryInfo = GetAppZoneHistoryInfo ( window , active , primaryActive ) ;
if ( ! appZoneHistoryInfo . second . empty ( ) )
{
MoveWindowIntoZone ( window , appZoneHistoryInfo . first , appZoneHistoryInfo . second ) ;
windowZoned = true ;
}
}
if ( ! windowZoned & & openOnActiveMonitor )
2020-06-17 11:55:14 +02:00
{
2020-07-08 10:37:42 +02:00
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ & ] { OpenWindowOnActiveMonitor ( window , active ) ; } } ) . wait ( ) ;
2019-09-04 18:26:26 +02:00
}
}
}
// IFancyZonesCallback
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( bool )
FancyZones : : OnKeyDown ( PKBDLLHOOKSTRUCT info ) noexcept
2019-09-04 18:26:26 +02:00
{
// Return true to swallow the keyboard event
bool const shift = GetAsyncKeyState ( VK_SHIFT ) & 0x8000 ;
2020-05-05 08:21:55 +01:00
bool const win = GetAsyncKeyState ( VK_LWIN ) & 0x8000 | | GetAsyncKeyState ( VK_RWIN ) & 0x8000 ;
2019-09-04 18:26:26 +02:00
if ( win & & ! shift )
{
bool const ctrl = GetAsyncKeyState ( VK_CONTROL ) & 0x8000 ;
if ( ctrl )
{
2020-03-25 18:32:33 +01:00
// Temporarily disable Win+Ctrl+Number functionality
//if ((info->vkCode >= '0') && (info->vkCode <= '9'))
//{
// // Win+Ctrl+Number will cycle through ZoneSets
// Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
// CycleActiveZoneSet(info->vkCode);
// return true;
//}
2019-09-04 18:26:26 +02:00
}
else if ( ( info - > vkCode = = VK_RIGHT ) | | ( info - > vkCode = = VK_LEFT ) )
{
2020-06-17 11:55:14 +02:00
if ( ShouldProcessSnapHotkey ( ) )
2019-11-18 15:29:42 -08:00
{
Trace : : FancyZones : : OnKeyDown ( info - > vkCode , win , ctrl , false /*inMoveSize*/ ) ;
2020-05-04 21:10:23 +03:00
// Win+Left, Win+Right will cycle through Zones in the active ZoneSet when WM_PRIV_LOWLEVELKB's handled
PostMessageW ( m_window , WM_PRIV_LOWLEVELKB , 0 , info - > vkCode ) ;
return true ;
2019-11-18 15:29:42 -08:00
}
2019-09-04 18:26:26 +02:00
}
}
2020-03-25 18:32:33 +01:00
// Temporarily disable Win+Ctrl+Number functionality
//else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
//{
// // This allows you to cycle through ZoneSets while dragging a window
// Trace::FancyZones::OnKeyDown(info->vkCode, win, false /*control*/, true /*inMoveSize*/);
// CycleActiveZoneSet(info->vkCode);
// return false;
//}
2020-04-30 13:16:08 +03:00
if ( m_windowMoveHandler . IsDragEnabled ( ) & & shift )
2020-03-05 08:01:58 +01:00
{
return true ;
}
2019-09-04 18:26:26 +02:00
return false ;
}
// IFancyZonesCallback
void FancyZones : : ToggleEditor ( ) noexcept
{
{
std : : shared_lock readLock ( m_lock ) ;
if ( m_terminateEditorEvent )
{
SetEvent ( m_terminateEditorEvent . get ( ) ) ;
return ;
}
}
{
std : : unique_lock writeLock ( m_lock ) ;
m_terminateEditorEvent . reset ( CreateEvent ( nullptr , true , false , nullptr ) ) ;
}
2019-09-28 15:29:29 +01:00
HMONITOR monitor { } ;
2019-11-07 21:56:32 +03:00
HWND foregroundWindow { } ;
2019-09-28 15:29:29 +01:00
2020-03-13 12:55:15 +03:00
const bool use_cursorpos_editor_startupscreen = m_settings - > GetSettings ( ) - > use_cursorpos_editor_startupscreen ;
2019-11-07 21:56:32 +03:00
POINT currentCursorPos { } ;
if ( use_cursorpos_editor_startupscreen )
2019-09-04 18:26:26 +02:00
{
2019-09-28 15:29:29 +01:00
GetCursorPos ( & currentCursorPos ) ;
monitor = MonitorFromPoint ( currentCursorPos , MONITOR_DEFAULTTOPRIMARY ) ;
}
else
{
2019-11-07 21:56:32 +03:00
foregroundWindow = GetForegroundWindow ( ) ;
2019-09-28 15:29:29 +01:00
monitor = MonitorFromWindow ( foregroundWindow , MONITOR_DEFAULTTOPRIMARY ) ;
}
if ( ! monitor )
{
return ;
}
std : : shared_lock readLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
auto zoneWindow = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , monitor ) ;
if ( ! zoneWindow )
2019-09-28 15:29:29 +01:00
{
return ;
}
MONITORINFOEX mi ;
mi . cbSize = sizeof ( mi ) ;
2020-02-10 14:59:51 +01:00
m_dpiUnawareThread . submit ( OnThreadExecutor : : task_t { [ & ] {
GetMonitorInfo ( monitor , & mi ) ;
} } )
. wait ( ) ;
2019-11-07 21:56:32 +03:00
2020-04-07 12:03:57 +03:00
const auto taskbar_x_offset = mi . rcWork . left - mi . rcMonitor . left ;
const auto taskbar_y_offset = mi . rcWork . top - mi . rcMonitor . top ;
2019-11-07 21:56:32 +03:00
const auto x = mi . rcMonitor . left + taskbar_x_offset ;
const auto y = mi . rcMonitor . top + taskbar_y_offset ;
const auto width = mi . rcWork . right - mi . rcWork . left ;
const auto height = mi . rcWork . bottom - mi . rcWork . top ;
2020-02-10 14:59:51 +01:00
const std : : wstring editorLocation =
2019-09-28 15:29:29 +01:00
std : : to_wstring ( x ) + L " _ " +
std : : to_wstring ( y ) + L " _ " +
2019-11-07 21:56:32 +03:00
std : : to_wstring ( width ) + L " _ " +
std : : to_wstring ( height ) ;
2019-09-28 15:29:29 +01:00
2020-06-04 13:01:42 +02:00
const auto & fancyZonesData = JSONHelpers : : FancyZonesDataInstance ( ) ;
2020-02-17 18:28:49 +03:00
const auto deviceInfo = fancyZonesData . FindDeviceInfo ( zoneWindow - > UniqueId ( ) ) ;
if ( ! deviceInfo . has_value ( ) )
{
return ;
}
2020-02-10 14:59:51 +01:00
2020-02-17 18:28:49 +03:00
JSONHelpers : : DeviceInfoJSON deviceInfoJson { zoneWindow - > UniqueId ( ) , * deviceInfo } ;
2020-02-10 14:59:51 +01:00
fancyZonesData . SerializeDeviceInfoToTmpFile ( deviceInfoJson , ZoneWindowUtils : : GetActiveZoneSetTmpPath ( ) ) ;
2019-12-06 15:09:27 +01:00
2019-09-28 15:29:29 +01:00
const std : : wstring params =
2020-06-04 13:01:42 +02:00
/*1*/ editorLocation + L " " +
2020-07-06 17:40:25 +02:00
/*2*/ L " \" " + std : : to_wstring ( GetCurrentProcessId ( ) ) + L " \" " ;
2019-09-28 15:29:29 +01:00
SHELLEXECUTEINFO sei { sizeof ( sei ) } ;
sei . fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI } ;
2020-05-11 21:22:23 +03:00
sei . lpFile = L " modules \\ FancyZones \\ FancyZonesEditor.exe " ;
2019-09-28 15:29:29 +01:00
sei . lpParameters = params . c_str ( ) ;
sei . nShow = SW_SHOWNORMAL ;
ShellExecuteEx ( & sei ) ;
2020-03-26 13:54:12 +03:00
Trace : : FancyZones : : EditorLaunched ( 1 ) ;
2019-09-28 15:29:29 +01:00
// Launch the editor on a background thread
// Wait for the editor's process to exit
// Post back to the main thread to update
2020-02-10 14:59:51 +01:00
std : : thread waitForEditorThread ( [ window = m_window , processHandle = sei . hProcess , terminateEditorEvent = m_terminateEditorEvent . get ( ) ] ( ) {
2019-09-28 15:29:29 +01:00
HANDLE waitEvents [ 2 ] = { processHandle , terminateEditorEvent } ;
auto result = WaitForMultipleObjects ( 2 , waitEvents , false , INFINITE ) ;
if ( result = = WAIT_OBJECT_0 + 0 )
2019-09-04 18:26:26 +02:00
{
2019-09-28 15:29:29 +01:00
// Editor exited
// Update any changes it may have made
PostMessage ( window , WM_PRIV_EDITOR , 0 , static_cast < LPARAM > ( EditorExitKind : : Exit ) ) ;
2019-09-04 18:26:26 +02:00
}
2019-09-28 15:29:29 +01:00
else if ( result = = WAIT_OBJECT_0 + 1 )
{
// User hit Win+~ while editor is already running
// Shut it down
TerminateProcess ( processHandle , 2 ) ;
PostMessage ( window , WM_PRIV_EDITOR , 0 , static_cast < LPARAM > ( EditorExitKind : : Terminate ) ) ;
}
CloseHandle ( processHandle ) ;
} ) ;
waitForEditorThread . detach ( ) ;
2019-09-04 18:26:26 +02:00
}
2019-10-02 17:18:55 +02:00
void FancyZones : : SettingsChanged ( ) noexcept
{
2020-02-10 14:59:51 +01:00
std : : shared_lock readLock ( m_lock ) ;
2019-10-02 17:18:55 +02:00
// Update the hotkey
UnregisterHotKey ( m_window , 1 ) ;
2020-03-13 12:55:15 +03:00
RegisterHotKey ( m_window , 1 , m_settings - > GetSettings ( ) - > editorHotkey . get_modifiers ( ) , m_settings - > GetSettings ( ) - > editorHotkey . get_code ( ) ) ;
2019-10-02 17:18:55 +02:00
}
2019-09-04 18:26:26 +02:00
// IZoneWindowHost
2020-02-17 18:28:49 +03:00
IFACEMETHODIMP_ ( void )
FancyZones : : MoveWindowsOnActiveZoneSetChange ( ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-03-13 12:55:15 +03:00
if ( m_settings - > GetSettings ( ) - > zoneSetChange_moveWindows )
2019-09-04 18:26:26 +02:00
{
2020-05-06 17:16:16 +02:00
UpdateWindowsPositions ( ) ;
2019-09-04 18:26:26 +02:00
}
}
LRESULT FancyZones : : WndProc ( HWND window , UINT message , WPARAM wparam , LPARAM lparam ) noexcept
{
switch ( message )
{
case WM_HOTKEY :
{
if ( wparam = = 1 )
{
2019-11-18 15:29:42 -08:00
ToggleEditor ( ) ;
2019-09-04 18:26:26 +02:00
}
}
break ;
case WM_SETTINGCHANGE :
{
if ( wparam = = SPI_SETWORKAREA )
{
2020-07-06 17:34:28 +02:00
// 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 ( ) ;
2019-09-04 18:26:26 +02:00
OnDisplayChange ( DisplayChangeType : : WorkArea ) ;
}
}
break ;
case WM_DISPLAYCHANGE :
{
2020-07-06 17:34:28 +02:00
// Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information.
m_workAreaHandler . Clear ( ) ;
2019-09-04 18:26:26 +02:00
OnDisplayChange ( DisplayChangeType : : DisplayChange ) ;
}
break ;
default :
{
2020-05-04 17:29:48 +03:00
POINT ptScreen ;
GetPhysicalCursorPos ( & ptScreen ) ;
2020-05-04 21:10:23 +03:00
if ( message = = WM_PRIV_LOWLEVELKB )
{
OnSnapHotkey ( static_cast < DWORD > ( lparam ) ) ;
}
else if ( message = = WM_PRIV_VD_INIT )
2020-05-01 16:13:16 +02:00
{
OnDisplayChange ( DisplayChangeType : : Initialization ) ;
}
else if ( message = = WM_PRIV_VD_SWITCH )
2019-09-04 18:26:26 +02:00
{
OnDisplayChange ( DisplayChangeType : : VirtualDesktop ) ;
}
2020-05-01 16:13:16 +02:00
else if ( message = = WM_PRIV_VD_UPDATE )
2019-12-24 15:47:28 +01:00
{
2020-05-01 16:13:16 +02:00
std : : vector < GUID > ids { } ;
2020-05-05 10:13:50 +02:00
if ( VirtualDesktopUtils : : GetVirtualDesktopIds ( ids ) )
2020-05-01 16:13:16 +02:00
{
RegisterVirtualDesktopUpdates ( ids ) ;
}
2019-12-24 15:47:28 +01:00
}
2019-09-04 18:26:26 +02:00
else if ( message = = WM_PRIV_EDITOR )
{
if ( lparam = = static_cast < LPARAM > ( EditorExitKind : : Exit ) )
{
2020-02-10 14:59:51 +01:00
OnEditorExitEvent ( ) ;
2019-09-04 18:26:26 +02:00
}
{
// Clean up the event either way
std : : unique_lock writeLock ( m_lock ) ;
m_terminateEditorEvent . release ( ) ;
}
}
2020-05-04 17:29:48 +03:00
else if ( message = = WM_PRIV_MOVESIZESTART )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
if ( auto monitor = MonitorFromPoint ( ptScreen , MONITOR_DEFAULTTONULL ) )
{
MoveSizeStart ( hwnd , monitor , ptScreen ) ;
}
}
else if ( message = = WM_PRIV_MOVESIZEEND )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
MoveSizeEnd ( hwnd , ptScreen ) ;
}
else if ( message = = WM_PRIV_LOCATIONCHANGE & & InMoveSize ( ) )
{
if ( auto monitor = MonitorFromPoint ( ptScreen , MONITOR_DEFAULTTONULL ) )
{
MoveSizeUpdate ( monitor , ptScreen ) ;
}
}
else if ( message = = WM_PRIV_WINDOWCREATED )
{
auto hwnd = reinterpret_cast < HWND > ( wparam ) ;
WindowCreated ( hwnd ) ;
}
2019-09-04 18:26:26 +02:00
else
{
return DefWindowProc ( window , message , wparam , lparam ) ;
}
}
break ;
}
return 0 ;
}
void FancyZones : : OnDisplayChange ( DisplayChangeType changeType ) noexcept
{
2019-12-24 15:47:28 +01:00
if ( changeType = = DisplayChangeType : : VirtualDesktop | |
changeType = = DisplayChangeType : : Initialization )
2019-09-04 18:26:26 +02:00
{
2020-05-31 12:36:45 +02:00
m_previousDesktopId = m_currentDesktopId ;
2019-09-04 18:26:26 +02:00
GUID currentVirtualDesktopId { } ;
2020-04-30 11:16:25 +02:00
if ( VirtualDesktopUtils : : GetCurrentVirtualDesktopId ( & currentVirtualDesktopId ) )
2019-09-04 18:26:26 +02:00
{
2020-05-31 12:36:45 +02:00
m_currentDesktopId = currentVirtualDesktopId ;
2020-06-15 19:44:07 +02:00
if ( m_previousDesktopId ! = GUID_NULL & & m_currentDesktopId ! = m_previousDesktopId )
{
Trace : : VirtualDesktopChanged ( ) ;
}
2020-05-05 10:13:50 +02:00
}
if ( changeType = = DisplayChangeType : : Initialization )
{
std : : vector < std : : wstring > ids { } ;
if ( VirtualDesktopUtils : : GetVirtualDesktopIds ( ids ) & & ! ids . empty ( ) )
2020-04-30 11:16:25 +02:00
{
2020-05-05 10:13:50 +02:00
JSONHelpers : : FancyZonesDataInstance ( ) . UpdatePrimaryDesktopData ( ids [ 0 ] ) ;
JSONHelpers : : FancyZonesDataInstance ( ) . RemoveDeletedDesktops ( ids ) ;
2020-04-30 11:16:25 +02:00
}
2019-09-04 18:26:26 +02:00
}
}
UpdateZoneWindows ( ) ;
if ( ( changeType = = DisplayChangeType : : WorkArea ) | | ( changeType = = DisplayChangeType : : DisplayChange ) )
{
2020-03-13 12:55:15 +03:00
if ( m_settings - > GetSettings ( ) - > displayChange_moveWindows )
2019-09-04 18:26:26 +02:00
{
2020-05-06 17:16:16 +02:00
UpdateWindowsPositions ( ) ;
2019-09-04 18:26:26 +02:00
}
}
}
void FancyZones : : AddZoneWindow ( HMONITOR monitor , PCWSTR deviceId ) noexcept
{
std : : unique_lock writeLock ( m_lock ) ;
2020-02-10 14:59:51 +01:00
2020-05-31 12:36:45 +02:00
if ( m_workAreaHandler . IsNewWorkArea ( m_currentDesktopId , monitor ) )
{
wil : : unique_cotaskmem_string virtualDesktopId ;
if ( SUCCEEDED ( StringFromCLSID ( m_currentDesktopId , & virtualDesktopId ) ) )
2019-09-04 18:26:26 +02:00
{
2020-05-31 12:36:45 +02:00
std : : wstring uniqueId = ZoneWindowUtils : : GenerateUniqueId ( monitor , deviceId , virtualDesktopId . get ( ) ) ;
2020-03-05 08:01:58 +01:00
2020-05-31 12:36:45 +02:00
// "Turning FLASHING_ZONE option off"
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones;
const bool flash = false ;
std : : wstring parentId { } ;
auto parentArea = m_workAreaHandler . GetWorkArea ( m_previousDesktopId , monitor ) ;
if ( parentArea )
{
parentId = parentArea - > UniqueId ( ) ;
}
auto workArea = MakeZoneWindow ( this , m_hinstance , monitor , uniqueId , parentId , flash ) ;
if ( workArea )
{
m_workAreaHandler . AddWorkArea ( m_currentDesktopId , monitor , workArea ) ;
JSONHelpers : : FancyZonesDataInstance ( ) . SaveFancyZonesData ( ) ;
}
2020-02-18 11:55:08 +01:00
}
2019-09-04 18:26:26 +02:00
}
}
LRESULT CALLBACK FancyZones : : s_WndProc ( HWND window , UINT message , WPARAM wparam , LPARAM lparam ) noexcept
{
auto thisRef = reinterpret_cast < FancyZones * > ( GetWindowLongPtr ( window , GWLP_USERDATA ) ) ;
if ( ! thisRef & & ( message = = WM_CREATE ) )
{
const auto createStruct = reinterpret_cast < LPCREATESTRUCT > ( lparam ) ;
thisRef = reinterpret_cast < FancyZones * > ( createStruct - > lpCreateParams ) ;
SetWindowLongPtr ( window , GWLP_USERDATA , reinterpret_cast < LONG_PTR > ( thisRef ) ) ;
}
return thisRef ? thisRef - > WndProc ( window , message , wparam , lparam ) :
2020-02-10 14:59:51 +01:00
DefWindowProc ( window , message , wparam , lparam ) ;
2019-09-04 18:26:26 +02:00
}
void FancyZones : : UpdateZoneWindows ( ) noexcept
{
2020-02-10 14:59:51 +01:00
auto callback = [ ] ( HMONITOR monitor , HDC , RECT * , LPARAM data ) - > BOOL {
2019-09-04 18:26:26 +02:00
MONITORINFOEX mi ;
mi . cbSize = sizeof ( mi ) ;
if ( GetMonitorInfo ( monitor , & mi ) )
{
DISPLAY_DEVICE displayDevice = { sizeof ( displayDevice ) } ;
PCWSTR deviceId = nullptr ;
bool validMonitor = true ;
if ( EnumDisplayDevices ( mi . szDevice , 0 , & displayDevice , 1 ) )
{
if ( WI_IsFlagSet ( displayDevice . StateFlags , DISPLAY_DEVICE_MIRRORING_DRIVER ) )
{
validMonitor = FALSE ;
}
else if ( displayDevice . DeviceID [ 0 ] ! = L ' \0 ' )
{
deviceId = displayDevice . DeviceID ;
}
}
if ( validMonitor )
{
if ( ! deviceId )
{
deviceId = GetSystemMetrics ( SM_REMOTESESSION ) ?
2020-02-10 14:59:51 +01:00
L " \\ \\ ? \\ DISPLAY#REMOTEDISPLAY# " :
L " \\ \\ ? \\ DISPLAY#LOCALDISPLAY# " ;
2019-09-04 18:26:26 +02:00
}
auto strongThis = reinterpret_cast < FancyZones * > ( data ) ;
strongThis - > AddZoneWindow ( monitor , deviceId ) ;
}
}
return TRUE ;
} ;
EnumDisplayMonitors ( nullptr , nullptr , callback , reinterpret_cast < LPARAM > ( this ) ) ;
}
2020-05-06 17:16:16 +02:00
void FancyZones : : UpdateWindowsPositions ( ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-02-10 14:59:51 +01:00
auto callback = [ ] ( HWND window , LPARAM data ) - > BOOL {
2020-05-26 16:01:12 +02:00
size_t bitmask = reinterpret_cast < size_t > ( : : GetProp ( window , MULTI_ZONE_STAMP ) ) ;
if ( bitmask ! = 0 )
2019-09-04 18:26:26 +02:00
{
2020-05-26 16:01:12 +02:00
std : : vector < int > indexSet ;
for ( int i = 0 ; i < std : : numeric_limits < size_t > : : digits ; i + + )
{
if ( ( 1ull < < i ) & bitmask )
{
indexSet . push_back ( i ) ;
}
}
2019-09-04 18:26:26 +02:00
auto strongThis = reinterpret_cast < FancyZones * > ( data ) ;
2020-04-30 13:16:08 +03:00
std : : unique_lock writeLock ( strongThis - > m_lock ) ;
2020-05-31 12:36:45 +02:00
auto zoneWindow = strongThis - > m_workAreaHandler . GetWorkArea ( window ) ;
if ( zoneWindow )
{
strongThis - > m_windowMoveHandler . MoveWindowIntoZoneByIndexSet ( window , indexSet , zoneWindow ) ;
}
2019-09-04 18:26:26 +02:00
}
return TRUE ;
} ;
EnumWindows ( callback , reinterpret_cast < LPARAM > ( this ) ) ;
}
void FancyZones : : CycleActiveZoneSet ( DWORD vkCode ) noexcept
{
2020-02-03 09:32:38 +01:00
auto window = GetForegroundWindow ( ) ;
2020-04-30 13:16:08 +03:00
if ( IsInterestingWindow ( window , m_settings - > GetSettings ( ) - > excludedAppsArray ) )
2019-09-04 18:26:26 +02:00
{
2020-02-10 14:59:51 +01:00
const HMONITOR monitor = MonitorFromWindow ( window , MONITOR_DEFAULTTONULL ) ;
if ( monitor )
2019-09-04 18:26:26 +02:00
{
std : : shared_lock readLock ( m_lock ) ;
2020-02-10 14:59:51 +01:00
2020-05-31 12:36:45 +02:00
auto zoneWindow = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , monitor ) ;
if ( zoneWindow )
2019-09-04 18:26:26 +02:00
{
2020-05-31 12:36:45 +02:00
zoneWindow - > CycleActiveZoneSet ( vkCode ) ;
2019-09-04 18:26:26 +02:00
}
}
}
}
2020-02-06 13:12:59 +01:00
bool FancyZones : : OnSnapHotkey ( DWORD vkCode ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-02-03 09:32:38 +01:00
auto window = GetForegroundWindow ( ) ;
2020-04-30 13:16:08 +03:00
if ( IsInterestingWindow ( window , m_settings - > GetSettings ( ) - > excludedAppsArray ) )
2019-09-04 18:26:26 +02:00
{
2020-03-24 18:50:26 +01:00
const HMONITOR current = MonitorFromWindow ( window , MONITOR_DEFAULTTONULL ) ;
if ( current )
2019-09-04 18:26:26 +02:00
{
2020-03-24 18:50:26 +01:00
std : : vector < HMONITOR > monitorInfo = GetMonitorsSorted ( ) ;
2020-05-01 16:17:16 +02:00
if ( monitorInfo . size ( ) > 1 & & m_settings - > GetSettings ( ) - > moveWindowAcrossMonitors )
2019-09-04 18:26:26 +02:00
{
2020-03-24 18:50:26 +01:00
// Multi monitor environment.
auto currMonitorInfo = std : : find ( std : : begin ( monitorInfo ) , std : : end ( monitorInfo ) , current ) ;
do
{
2020-04-30 13:16:08 +03:00
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
if ( m_windowMoveHandler . MoveWindowIntoZoneByDirection ( window , vkCode , false /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , * currMonitorInfo ) ) )
2020-03-24 18:50:26 +01:00
{
return true ;
}
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
if ( vkCode = = VK_RIGHT )
{
currMonitorInfo = std : : next ( currMonitorInfo ) ;
if ( currMonitorInfo = = std : : end ( monitorInfo ) )
{
currMonitorInfo = std : : begin ( monitorInfo ) ;
}
}
else if ( vkCode = = VK_LEFT )
{
if ( currMonitorInfo = = std : : begin ( monitorInfo ) )
{
currMonitorInfo = std : : end ( monitorInfo ) ;
}
currMonitorInfo = std : : prev ( currMonitorInfo ) ;
}
} while ( * currMonitorInfo ! = current ) ;
}
else
{
// Single monitor environment.
2020-04-30 13:16:08 +03:00
std : : unique_lock writeLock ( m_lock ) ;
2020-07-01 15:36:05 +02:00
if ( m_settings - > GetSettings ( ) - > restoreSize )
{
bool moved = m_windowMoveHandler . MoveWindowIntoZoneByDirection ( window , vkCode , false /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
if ( ! moved )
{
RestoreWindowOrigin ( window ) ;
RestoreWindowSize ( window ) ;
}
return true ;
}
else
{
return m_windowMoveHandler . MoveWindowIntoZoneByDirection ( window , vkCode , true /* cycle through zones */ , m_workAreaHandler . GetWorkArea ( m_currentDesktopId , current ) ) ;
}
2019-09-04 18:26:26 +02:00
}
}
}
2020-02-06 13:12:59 +01:00
return false ;
2019-09-04 18:26:26 +02:00
}
2020-05-01 16:13:16 +02:00
void FancyZones : : RegisterVirtualDesktopUpdates ( std : : vector < GUID > & ids ) noexcept
2020-02-18 11:55:08 +01:00
{
std : : unique_lock writeLock ( m_lock ) ;
2020-05-31 12:36:45 +02:00
2020-06-05 17:25:52 +02:00
m_workAreaHandler . RegisterUpdates ( ids ) ;
std : : vector < std : : wstring > active { } ;
if ( VirtualDesktopUtils : : GetVirtualDesktopIds ( active ) )
2020-02-18 11:55:08 +01:00
{
2020-06-05 17:25:52 +02:00
JSONHelpers : : FancyZonesDataInstance ( ) . RemoveDeletedDesktops ( active ) ;
2020-02-18 11:55:08 +01:00
}
2019-12-12 10:10:55 +01:00
}
2020-05-27 16:52:59 +02:00
bool FancyZones : : IsSplashScreen ( HWND window )
{
wchar_t splashClassName [ ] = L " MsoSplash " ; // shouldn't be localized
wchar_t className [ MAX_PATH ] ;
if ( GetClassName ( window , className , MAX_PATH ) = = 0 )
{
return false ;
}
return wcscmp ( splashClassName , className ) = = 0 ;
}
2020-02-10 14:59:51 +01:00
void FancyZones : : OnEditorExitEvent ( ) noexcept
{
2020-05-26 10:56:25 -04:00
// Collect information about changes in zone layout after editor exited.
2020-02-10 14:59:51 +01:00
JSONHelpers : : FancyZonesDataInstance ( ) . ParseDeviceInfoFromTmpFile ( ZoneWindowUtils : : GetActiveZoneSetTmpPath ( ) ) ;
2020-07-06 17:40:25 +02:00
JSONHelpers : : FancyZonesDataInstance ( ) . ParseDeletedCustomZoneSetsFromTmpFile ( ZoneWindowUtils : : GetDeletedCustomZoneSetsTmpPath ( ) ) ;
2020-02-10 14:59:51 +01:00
JSONHelpers : : FancyZonesDataInstance ( ) . ParseCustomZoneSetFromTmpFile ( ZoneWindowUtils : : GetAppliedZoneSetTmpPath ( ) ) ;
JSONHelpers : : FancyZonesDataInstance ( ) . SaveFancyZonesData ( ) ;
2020-05-31 12:36:45 +02:00
for ( auto workArea : m_workAreaHandler . GetAllWorkAreas ( ) )
2020-05-06 17:16:16 +02:00
{
2020-05-31 12:36:45 +02:00
workArea - > UpdateActiveZoneSet ( ) ;
2020-05-06 17:16:16 +02:00
}
if ( m_settings - > GetSettings ( ) - > zoneSetChange_moveWindows )
{
UpdateWindowsPositions ( ) ;
}
2020-02-10 14:59:51 +01:00
}
2020-06-17 11:55:14 +02:00
bool FancyZones : : ShouldProcessSnapHotkey ( ) noexcept
2020-05-25 10:15:42 +02:00
{
if ( m_settings - > GetSettings ( ) - > overrideSnapHotkeys )
{
const HMONITOR monitor = MonitorFromWindow ( GetForegroundWindow ( ) , MONITOR_DEFAULTTONULL ) ;
if ( monitor )
{
2020-05-31 12:36:45 +02:00
auto zoneWindow = m_workAreaHandler . GetWorkArea ( m_currentDesktopId , monitor ) ;
if ( zoneWindow - > ActiveZoneSet ( ) ! = nullptr )
2020-05-25 10:15:42 +02:00
{
return true ;
}
}
}
return false ;
}
2020-03-24 18:50:26 +01:00
std : : vector < HMONITOR > FancyZones : : GetMonitorsSorted ( ) noexcept
{
std : : shared_lock readLock ( m_lock ) ;
auto monitorInfo = GetRawMonitorData ( ) ;
OrderMonitors ( monitorInfo ) ;
std : : vector < HMONITOR > output ;
std : : transform ( std : : begin ( monitorInfo ) , std : : end ( monitorInfo ) , std : : back_inserter ( output ) , [ ] ( const auto & info ) { return info . first ; } ) ;
return output ;
}
std : : vector < std : : pair < HMONITOR , RECT > > FancyZones : : GetRawMonitorData ( ) noexcept
{
std : : shared_lock readLock ( m_lock ) ;
std : : vector < std : : pair < HMONITOR , RECT > > monitorInfo ;
2020-05-31 12:36:45 +02:00
const auto & activeWorkAreaMap = m_workAreaHandler . GetWorkAreasByDesktopId ( m_currentDesktopId ) ;
for ( const auto & [ monitor , workArea ] : activeWorkAreaMap )
2020-03-24 18:50:26 +01:00
{
2020-05-31 12:36:45 +02:00
if ( workArea - > ActiveZoneSet ( ) ! = nullptr )
2020-03-24 18:50:26 +01:00
{
MONITORINFOEX mi ;
mi . cbSize = sizeof ( mi ) ;
GetMonitorInfo ( monitor , & mi ) ;
monitorInfo . push_back ( { monitor , mi . rcMonitor } ) ;
}
}
return monitorInfo ;
}
2020-02-10 14:59:51 +01:00
winrt : : com_ptr < IFancyZones > MakeFancyZones ( HINSTANCE hinstance , const winrt : : com_ptr < IFancyZonesSettings > & settings ) noexcept
2019-09-04 18:26:26 +02:00
{
2020-02-10 14:59:51 +01:00
if ( ! settings )
{
return nullptr ;
}
2019-09-04 18:26:26 +02:00
return winrt : : make_self < FancyZones > ( hinstance , settings ) ;
2020-03-24 17:17:25 +03:00
}