diff --git a/OBSBlur/POINT.cs b/OBSBlur/POINT.cs new file mode 100644 index 0000000..5c362f1 --- /dev/null +++ b/OBSBlur/POINT.cs @@ -0,0 +1,29 @@ +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +public struct POINT +{ + public int X; + public int Y; + + public POINT(int x, int y) + { + this.X = x; + this.Y = y; + } + + public static implicit operator System.Drawing.Point(POINT p) + { + return new System.Drawing.Point(p.X, p.Y); + } + + public static implicit operator POINT(System.Drawing.Point p) + { + return new POINT(p.X, p.Y); + } + + public override string ToString() + { + return $"{{X={X,-7:####0}, Y={Y,-7:####0}}}"; + } +} \ No newline at end of file diff --git a/OBSBlur/Program.cs b/OBSBlur/Program.cs index 5f28270..f0a1008 100644 --- a/OBSBlur/Program.cs +++ b/OBSBlur/Program.cs @@ -1 +1,79 @@ - \ No newline at end of file +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +new WindowManager(); + +internal class WindowManager +{ + internal Dictionary placements = new(); + internal Dictionary windowTitles = new(); + internal Dictionary processes = new(); + private const int cutoffStr = 17; + public WindowManager() + { + EnumWindows(WindowPlacement, 0); + uint hwndCount = 1; + //Group and print by pid + foreach (int pid in processes.DistinctBy(kv => kv.Value.Id).Select(kv => kv.Value.Id).ToArray()) + { + foreach(IntPtr hwnd in processes.Where(kv => kv.Value.Id == pid).Select(x => x.Key)) + Console.WriteLine($"{hwndCount++,3} {hwnd,8} {processes[hwnd].Id,8} {processes[hwnd].ProcessName.Substring(0, Math.Min(cutoffStr, processes[hwnd].ProcessName.Length)),cutoffStr} {windowTitles[hwnd].Substring(0, Math.Min(cutoffStr, windowTitles[hwnd].Length)),-cutoffStr} {placements[hwnd]}"); + } + } + + private bool WindowPlacement(IntPtr hwnd, IntPtr lParam) + { + WINDOWPLACEMENT placement = new (); + GetWindowPlacement(hwnd, ref placement); + RECT n = placement.NormalPosition; + //Do not add if Window is not a drawable + if (n is { Left: 0, Top: 0, Right: 0, Bottom: 0 }) + return true; + + uint pid; + GetWindowThreadProcessId(hwnd, out pid); + Process process = Process.GetProcessById((int)pid); + //nvcontainer does not have a window name + if (process.ProcessName.Equals("nvcontainer")) + return true; + string name = GetWindowTextRaw(hwnd); + //Do not add, if window does not have a name + if (name.Length < 1) + return true; + + processes.Add(hwnd, process); + windowTitles.Add(hwnd, name); + placements.Add(hwnd, placement); + return true; + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam); + + public static string GetWindowTextRaw(IntPtr hwnd) + { + UInt32 WM_GETTEXTLENGTH = 0x000E; + UInt32 WM_GETTEXT = 0x000D; + // Allocate correct string length first + int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null); + StringBuilder sb = new (length + 1); + SendMessage(hwnd, WM_GETTEXT, sb.Capacity, sb); + return sb.ToString(); + } +} + diff --git a/OBSBlur/RECT.cs b/OBSBlur/RECT.cs new file mode 100644 index 0000000..932c795 --- /dev/null +++ b/OBSBlur/RECT.cs @@ -0,0 +1,97 @@ +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Sequential)] +public struct RECT +{ + public int Left, Top, Right, Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { } + + public int X + { + get { return Left; } + set { Right -= (Left - value); Left = value; } + } + + public int Y + { + get { return Top; } + set { Bottom -= (Top - value); Top = value; } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get { return new System.Drawing.Point(Left, Top); } + set { X = value.X; Y = value.Y; } + } + + public System.Drawing.Size Size + { + get { return new System.Drawing.Size(Width, Height); } + set { Width = value.Width; Height = value.Height; } + } + + public static implicit operator System.Drawing.Rectangle(RECT r) + { + return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + } + + public static implicit operator RECT(System.Drawing.Rectangle r) + { + return new RECT(r); + } + + public static bool operator ==(RECT r1, RECT r2) + { + return r1.Equals(r2); + } + + public static bool operator !=(RECT r1, RECT r2) + { + return !r1.Equals(r2); + } + + public bool Equals(RECT r) + { + return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + } + + public override bool Equals(object obj) + { + if (obj is RECT) + return Equals((RECT)obj); + else if (obj is System.Drawing.Rectangle) + return Equals(new RECT((System.Drawing.Rectangle)obj)); + return false; + } + + public override int GetHashCode() + { + return ((System.Drawing.Rectangle)this).GetHashCode(); + } + + public override string ToString() + { + return $"{{Left={Left,-7:####0}, Top={Top,-7:####0}, Right={Right,-7:####0}, Bottom={Bottom,-7:####0}}}"; + } +} \ No newline at end of file diff --git a/OBSBlur/ShowWindowCommands.cs b/OBSBlur/ShowWindowCommands.cs new file mode 100644 index 0000000..a354855 --- /dev/null +++ b/OBSBlur/ShowWindowCommands.cs @@ -0,0 +1,71 @@ +enum ShowWindowCommands +{ + /// + /// Hides the window and activates another window. + /// + Hide = 0, + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + /// + /// Maximizes the specified window. + /// + Maximize = 3, // is this the right value? + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not activated. + /// + ShowNoActivate = 4, + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11 +} \ No newline at end of file diff --git a/OBSBlur/WINDOWPLACEMENT.cs b/OBSBlur/WINDOWPLACEMENT.cs new file mode 100644 index 0000000..ed24c83 --- /dev/null +++ b/OBSBlur/WINDOWPLACEMENT.cs @@ -0,0 +1,60 @@ +using System.Runtime.InteropServices; + +/// +/// Contains information about the placement of a window on the screen. +/// +[Serializable] +[StructLayout(LayoutKind.Sequential)] +internal struct WINDOWPLACEMENT +{ + /// + /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT). + /// + /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly. + /// + /// + public int Length; + + /// + /// Specifies flags that control the position of the minimized window and the method by which the window is restored. + /// + public int Flags; + + /// + /// The current show state of the window. + /// + public ShowWindowCommands ShowCmd; + + /// + /// The coordinates of the window's upper-left corner when the window is minimized. + /// + public POINT MinPosition; + + /// + /// The coordinates of the window's upper-left corner when the window is maximized. + /// + public POINT MaxPosition; + + /// + /// The window's coordinates when the window is in the restored position. + /// + public RECT NormalPosition; + + /// + /// Gets the default (empty) value. + /// + public static WINDOWPLACEMENT Default + { + get + { + WINDOWPLACEMENT result = new WINDOWPLACEMENT(); + result.Length = Marshal.SizeOf( result ); + return result; + } + } + + public override string ToString() + { + return $"CMD: {ShowCmd,13} Min: {MinPosition} Max: {MaxPosition} Normal: {NormalPosition}"; + } +} \ No newline at end of file