diff --git a/OBSBlur.sln.DotSettings b/OBSBlur.sln.DotSettings new file mode 100644 index 0000000..af49136 --- /dev/null +++ b/OBSBlur.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/OBSBlur/Program.cs b/OBSBlur/Program.cs index f0a1008..bd46a94 100644 --- a/OBSBlur/Program.cs +++ b/OBSBlur/Program.cs @@ -1,79 +1,3 @@ -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(); - } -} +using OBSBlur.Window; +new WindowManager(); \ No newline at end of file diff --git a/OBSBlur/POINT.cs b/OBSBlur/Window/POINT.cs similarity index 59% rename from OBSBlur/POINT.cs rename to OBSBlur/Window/POINT.cs index 5c362f1..1aefb23 100644 --- a/OBSBlur/POINT.cs +++ b/OBSBlur/Window/POINT.cs @@ -1,25 +1,27 @@ using System.Runtime.InteropServices; +namespace OBSBlur.Window; + [StructLayout(LayoutKind.Sequential)] -public struct POINT +public struct Point { public int X; public int Y; - public POINT(int x, int y) + public Point(int x, int y) { this.X = x; this.Y = y; } - public static implicit operator System.Drawing.Point(POINT p) + 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) + public static implicit operator Point(System.Drawing.Point p) { - return new POINT(p.X, p.Y); + return new Point(p.X, p.Y); } public override string ToString() diff --git a/OBSBlur/RECT.cs b/OBSBlur/Window/Rectangle.cs similarity index 69% rename from OBSBlur/RECT.cs rename to OBSBlur/Window/Rectangle.cs index 932c795..6f69ad7 100644 --- a/OBSBlur/RECT.cs +++ b/OBSBlur/Window/Rectangle.cs @@ -1,11 +1,13 @@ using System.Runtime.InteropServices; +namespace OBSBlur.Window; + [StructLayout(LayoutKind.Sequential)] -public struct RECT +public struct Rectangle { public int Left, Top, Right, Bottom; - public RECT(int left, int top, int right, int bottom) + public Rectangle(int left, int top, int right, int bottom) { Left = left; Top = top; @@ -13,10 +15,10 @@ public struct RECT Bottom = bottom; } - public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { } + public Rectangle(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; } } @@ -51,37 +53,37 @@ public struct RECT set { Width = value.Width; Height = value.Height; } } - public static implicit operator System.Drawing.Rectangle(RECT r) + public static implicit operator System.Drawing.Rectangle(Rectangle r) { return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); } - public static implicit operator RECT(System.Drawing.Rectangle r) + public static implicit operator Rectangle(System.Drawing.Rectangle r) { - return new RECT(r); + return new Rectangle(r); } - public static bool operator ==(RECT r1, RECT r2) + public static bool operator ==(Rectangle r1, Rectangle r2) { return r1.Equals(r2); } - public static bool operator !=(RECT r1, RECT r2) + public static bool operator !=(Rectangle r1, Rectangle r2) { return !r1.Equals(r2); } - public bool Equals(RECT r) + public bool Equals(Rectangle 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); + if (obj is Rectangle) + return Equals((Rectangle)obj); else if (obj is System.Drawing.Rectangle) - return Equals(new RECT((System.Drawing.Rectangle)obj)); + return Equals(new Rectangle((System.Drawing.Rectangle)obj)); return false; } diff --git a/OBSBlur/ShowWindowCommands.cs b/OBSBlur/Window/ShowWindowCommands.cs similarity index 97% rename from OBSBlur/ShowWindowCommands.cs rename to OBSBlur/Window/ShowWindowCommands.cs index a354855..b037a15 100644 --- a/OBSBlur/ShowWindowCommands.cs +++ b/OBSBlur/Window/ShowWindowCommands.cs @@ -1,4 +1,6 @@ -enum ShowWindowCommands +namespace OBSBlur.Window; + +public enum ShowWindowCommands { /// /// Hides the window and activates another window. diff --git a/OBSBlur/WINDOWPLACEMENT.cs b/OBSBlur/Window/WINDOWPLACEMENT.cs similarity index 91% rename from OBSBlur/WINDOWPLACEMENT.cs rename to OBSBlur/Window/WINDOWPLACEMENT.cs index ed24c83..ccc83a1 100644 --- a/OBSBlur/WINDOWPLACEMENT.cs +++ b/OBSBlur/Window/WINDOWPLACEMENT.cs @@ -1,11 +1,13 @@ using System.Runtime.InteropServices; +namespace OBSBlur.Window; + /// /// Contains information about the placement of a window on the screen. /// [Serializable] [StructLayout(LayoutKind.Sequential)] -internal struct WINDOWPLACEMENT +public struct WINDOWPLACEMENT { /// /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT). @@ -28,17 +30,17 @@ internal struct WINDOWPLACEMENT /// /// The coordinates of the window's upper-left corner when the window is minimized. /// - public POINT MinPosition; + public Point MinPosition; /// /// The coordinates of the window's upper-left corner when the window is maximized. /// - public POINT MaxPosition; + public Point MaxPosition; /// /// The window's coordinates when the window is in the restored position. /// - public RECT NormalPosition; + public Rectangle NormalPosition; /// /// Gets the default (empty) value. diff --git a/OBSBlur/Window/WindowInfo.cs b/OBSBlur/Window/WindowInfo.cs new file mode 100644 index 0000000..94d4e5e --- /dev/null +++ b/OBSBlur/Window/WindowInfo.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; + +namespace OBSBlur.Window; + +public struct WindowInfo +{ + internal IntPtr WindowHandle { get; init; } + public WINDOWPLACEMENT WindowPlacement { get; init; } + public string WindowTitle { get; init; } + public Process ProcessInfo { get; init; } + + public WindowInfo(IntPtr windowHandle, string windowTitle, Process processInfo, WINDOWPLACEMENT windowPlacement) + { + this.WindowHandle = windowHandle; + this.ProcessInfo = processInfo; + this.WindowPlacement = windowPlacement; + this.WindowTitle = windowTitle; + } + + public override string ToString() + { + const int cutoffStr = 17; + string processNameStr = ProcessInfo.ProcessName.Substring(0, Math.Min(cutoffStr, ProcessInfo.ProcessName.Length)); + string windowTitleStr = WindowTitle.Substring(0, Math.Min(cutoffStr, WindowTitle.Length)); + return $"{WindowHandle,8} {ProcessInfo.Id,8} {processNameStr,cutoffStr} {windowTitleStr,-cutoffStr} {WindowPlacement}"; + } +} \ No newline at end of file diff --git a/OBSBlur/Window/WindowManager.cs b/OBSBlur/Window/WindowManager.cs new file mode 100644 index 0000000..bfba1ff --- /dev/null +++ b/OBSBlur/Window/WindowManager.cs @@ -0,0 +1,81 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace OBSBlur.Window; + +public class WindowManager : IDisposable +{ + public readonly HashSet Windows = new(); + public int UpdateInterval = 10; + private bool KeepUpdating = true; + public WindowManager() + { + Thread t = new (() => + { + while(KeepUpdating) + EnumWindows(WindowPlacement, 0); + Thread.Sleep(UpdateInterval); + }); + t.Start(); + } + + private bool WindowPlacement(IntPtr windowHandle, IntPtr lParam) + { + WINDOWPLACEMENT windowPlacement = new (); + GetWindowPlacement(windowHandle, ref windowPlacement); + Rectangle n = windowPlacement.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(windowHandle, out pid); + Process processInfo = Process.GetProcessById((int)pid); + //nvcontainer does not have a window name + if (processInfo.ProcessName.Equals("nvcontainer")) + return true; + string windowTitle = GetWindowTextRaw(windowHandle); + //Do not add, if window does not have a name + if (windowTitle.Length < 1) + return true; + + Windows.Add(new WindowInfo(windowHandle, windowTitle, processInfo, windowPlacement)); + return true; + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + private delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr windowHandle, ref WINDOWPLACEMENT lpwndpl); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr windowHandle, out uint lpdwProcessId); + + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + static extern IntPtr SendMessage(IntPtr windowHandle, uint message, IntPtr wParam, [Out] StringBuilder lParam); + + public static string GetWindowTextRaw(IntPtr windowHandle) + { + // ReSharper disable twice InconsistentNaming + const uint WM_GETTEXTLENGTH = 0x000E; + const uint WM_GETTEXT = 0x000D; + // Allocate correct string length first + int length = (int)SendMessage(windowHandle, WM_GETTEXTLENGTH, IntPtr.Zero, null); + StringBuilder sb = new (length + 1); + SendMessage(windowHandle, WM_GETTEXT, sb.Capacity, sb); + return sb.ToString(); + } + + public void Dispose() + { + KeepUpdating = false; + } +} +