Cleanup, Namespaces, Repeated updates in WindowManager

This commit is contained in:
glax 2024-04-15 21:41:28 +02:00
parent 231b592610
commit 02314701f4
8 changed files with 143 additions and 101 deletions

2
OBSBlur.sln.DotSettings Normal file
View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=GETTEXTLENGTH/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,79 +1,3 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using OBSBlur.Window;
new WindowManager();
internal class WindowManager
{
internal Dictionary<IntPtr, WINDOWPLACEMENT> placements = new();
internal Dictionary<IntPtr, string> windowTitles = new();
internal Dictionary<IntPtr, Process> 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();
}
}

View File

@ -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()

View File

@ -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,7 +15,7 @@ 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
{
@ -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;
}

View File

@ -1,4 +1,6 @@
enum ShowWindowCommands
namespace OBSBlur.Window;
public enum ShowWindowCommands
{
/// <summary>
/// Hides the window and activates another window.

View File

@ -1,11 +1,13 @@
using System.Runtime.InteropServices;
namespace OBSBlur.Window;
/// <summary>
/// Contains information about the placement of a window on the screen.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPLACEMENT
public struct WINDOWPLACEMENT
{
/// <summary>
/// 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
/// <summary>
/// The coordinates of the window's upper-left corner when the window is minimized.
/// </summary>
public POINT MinPosition;
public Point MinPosition;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is maximized.
/// </summary>
public POINT MaxPosition;
public Point MaxPosition;
/// <summary>
/// The window's coordinates when the window is in the restored position.
/// </summary>
public RECT NormalPosition;
public Rectangle NormalPosition;
/// <summary>
/// Gets the default (empty) value.

View File

@ -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}";
}
}

View File

@ -0,0 +1,81 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace OBSBlur.Window;
public class WindowManager : IDisposable
{
public readonly HashSet<WindowInfo> 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;
}
}