using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
// The 'windowHandle' parameter will contain the window handle for the:
// - Active window when run by hotkey
// - Window Location target when run by a Window Location rule
// - TitleBar Button owner when run by a TitleBar Button
// - Jump List owner when run from a Taskbar Jump List
// - Currently focused window if none of these match
public static class DisplayFusionFunction
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
//some constants
const uint GW_HWNDNEXT = 2;
public static void Run(IntPtr windowHandle)
{
// Get the user to select the monitors we're going to be swapping to and from
// If there are only two monitors, just use those
uint monitor1 = 0;
uint monitor2 = 1;
uint[] ids = BFS.Monitor.GetMonitorIDs();
if(ids.Length == 2)
{
monitor1 = ids[0];
monitor2 = ids[1];
}
else
{
monitor1 = BFS.Monitor.ShowMonitorSelector();
monitor2 = BFS.Monitor.ShowMonitorSelector();
}
// Get a list of each visible window on our monitors, and throw them in a HashSet
HashSet<IntPtr> monitor1Windows = new HashSet<IntPtr>(BFS.Window.GetVisibleWindowHandlesByMonitor(monitor1));
HashSet<IntPtr> monitor2Windows = new HashSet<IntPtr>(BFS.Window.GetVisibleWindowHandlesByMonitor(monitor2));
// Make a stack so we can store the z-order of the windows
Stack<Tuple<IntPtr, uint>> windows = new Stack<Tuple<IntPtr, uint>>();
// Enumerate through the monitors, starting with the focused window, and moving down
for(IntPtr window = BFS.Window.GetFocusedWindow(); ; window = GetWindow(window, GW_HWNDNEXT))
{
// Check to see if there are any windows left
if(window == IntPtr.Zero)
break;
// If it is a window we should ignore, ignore it
if(IsDisplayFusionWindowOrHiddenExplorerWindow(window))
continue;
// If the window is not on this virtual desktop, ignore it
if(!BFS.Window.IsOnCurrentVirtualDesktop(window))
continue;
// Check to see if the window exists in either of our hashsets.
// If it does, store it with the monitorid it should be moved to
if(monitor1Windows.Contains(window))
windows.Push(new Tuple<IntPtr, uint>(window, monitor2));
if(monitor2Windows.Contains(window))
windows.Push(new Tuple<IntPtr, uint>(window, monitor1));
}
// Loop through the stack (first in last out) and move the windows
IntPtr lastWindow = IntPtr.Zero;
foreach(var tuple in windows)
{
BFS.Window.MoveToMonitor(tuple.Item2, tuple.Item1);
lastWindow = tuple.Item1;
}
// Focus the last window. This should be the top of the z-order
BFS.Window.Focus(lastWindow);
}
private static bool IsDisplayFusionWindowOrHiddenExplorerWindow(IntPtr window)
{
// Ignore any DisplayFusion windows (title bar buttons, etc.)
// Ignore pesky hidden explorer.exe windows
string windowClass = BFS.Window.GetClass(window);
if((windowClass.StartsWith("DF", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("EdgeUiInputTopWndClass", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("EdgeUiInputWndClass", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("NativeHWNDHost", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("ModeInputWnd", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("MetroGhostWindow", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("ImmersiveLauncher", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("ApplicationManager_ImmersiveShellWindow", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("Shell_TrayWnd", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("WorkerW", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("Progman", StringComparison.OrdinalIgnoreCase)) ||
(windowClass.Equals("SearchPane", StringComparison.OrdinalIgnoreCase)))
{
return true;
}
return false;
}
}