using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
// DisplayFusion Scripted Function
// Splits the two most recently focused "real" windows across the current monitor:
// - Most recently focused window -> RIGHT half
// - Previously focused window -> LEFT half
// Only considers windows that can be maximized/restored (WS_MAXIMIZEBOX).
// Dialogs and tool windows are ignored.
public static class DisplayFusionFunction
{
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("dwmapi.dll")]
private static extern int DwmGetWindowAttribute(IntPtr hWnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private const int DWMWA_EXTENDED_FRAME_BOUNDS = 9;
private const int GWL_STYLE = -16;
private const int GWL_EXSTYLE = -20;
private const int WS_MAXIMIZEBOX = 0x00010000;
private const int WS_CAPTION = 0x00C00000;
private const int WS_EX_TOOLWINDOW = 0x00000080;
private const int WS_EX_DLGMODALFRAME = 0x00000001;
public static void Run(IntPtr windowHandle)
{
// Handles are returned in Z-order: topmost (last focused) first
IntPtr[] handles = BFS.Window.GetVisibleWindowHandles();
List<IntPtr> candidates = new List<IntPtr>();
foreach (IntPtr h in handles)
{
if (IsEligible(h))
{
candidates.Add(h);
if (candidates.Count == 2)
break;
}
}
if (candidates.Count < 2)
{
BFS.Dialog.ShowMessageInfo("Couldn't find two eligible windows to split.");
return;
}
IntPtr lastFocused = candidates[0];
IntPtr previous = candidates[1];
// Decide which window goes where based on their current horizontal
// centers. Only when the two centers are identical do we fall back to
// focus order (previous -> left, last focused -> right).
IntPtr leftWindow;
IntPtr rightWindow;
int lastCenter = GetHorizontalCenter(lastFocused);
int prevCenter = GetHorizontalCenter(previous);
if (lastCenter < prevCenter)
{
leftWindow = lastFocused;
rightWindow = previous;
}
else if (lastCenter > prevCenter)
{
leftWindow = previous;
rightWindow = lastFocused;
}
else
{
leftWindow = previous;
rightWindow = lastFocused;
}
// Use the monitor of the most recently focused window
Rectangle monitor = BFS.Monitor.GetMonitorWorkAreaByWindow(lastFocused);
int halfWidth = monitor.Width / 2;
Rectangle left = new Rectangle(monitor.X, monitor.Y, halfWidth, monitor.Height);
Rectangle right = new Rectangle(monitor.X + halfWidth, monitor.Y, monitor.Width - halfWidth, monitor.Height);
PlaceWindow(leftWindow, left);
PlaceWindow(rightWindow, right);
// Bring both to front, last focused on top
BFS.Window.Focus(previous);
BFS.Window.Focus(lastFocused);
}
// Horizontal center of the window's visible frame, falling back to the
// standard window rect if the DWM frame isn't available.
private static int GetHorizontalCenter(IntPtr h)
{
RECT frameRect = new RECT();
if (DwmGetWindowAttribute(h, DWMWA_EXTENDED_FRAME_BOUNDS, out frameRect, Marshal.SizeOf(typeof(RECT))) == 0)
return frameRect.Left + ((frameRect.Right - frameRect.Left) / 2);
RECT windowRect = new RECT();
if (GetWindowRect(h, out windowRect))
return windowRect.Left + ((windowRect.Right - windowRect.Left) / 2);
return 0;
}
private static bool IsEligible(IntPtr h)
{
// Skip windows without a title (often background/system windows)
string title = BFS.Window.GetText(h);
if (string.IsNullOrEmpty(title))
return false;
int style = GetWindowLong(h, GWL_STYLE);
int exStyle = GetWindowLong(h, GWL_EXSTYLE);
// Must have a maximize/restore box
if ((style & WS_MAXIMIZEBOX) == 0)
return false;
// Must be a normal captioned window
if ((style & WS_CAPTION) != WS_CAPTION)
return false;
// Skip tool windows and dialog-framed windows
if ((exStyle & WS_EX_TOOLWINDOW) != 0)
return false;
if ((exStyle & WS_EX_DLGMODALFRAME) != 0)
return false;
return true;
}
private static void PlaceWindow(IntPtr h, Rectangle bounds)
{
// Restore first so the size/position actually applies
if (BFS.Window.IsMaximized(h) || BFS.Window.IsMinimized(h))
BFS.Window.Restore(h);
// Compensate for the invisible resize borders (DWM extended frame).
// The visible frame (extended frame bounds) is smaller than the
// window rect, so we expand the target rect by the difference
// on each side to make the visible edges sit flush.
RECT windowRect = new RECT();
RECT frameRect = new RECT();
bool haveRects = false;
if (GetWindowRect(h, out windowRect))
{
if (DwmGetWindowAttribute(h, DWMWA_EXTENDED_FRAME_BOUNDS, out frameRect, Marshal.SizeOf(typeof(RECT))) == 0)
haveRects = true;
}
int leftOffset = 0, topOffset = 0, rightOffset = 0, bottomOffset = 0;
if (haveRects)
{
leftOffset = frameRect.Left - windowRect.Left; // usually positive
topOffset = frameRect.Top - windowRect.Top; // usually 0
rightOffset = windowRect.Right - frameRect.Right; // usually positive
bottomOffset = windowRect.Bottom - frameRect.Bottom; // usually positive
}
BFS.Window.SetSizeAndLocation(
h,
bounds.X - leftOffset,
bounds.Y - topOffset,
bounds.Width + leftOffset + rightOffset,
bounds.Height + topOffset + bottomOffset);
}
}