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 OBSBlur.Window;
using System.Runtime.InteropServices;
using System.Text;
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();
}
}
new WindowManager();

View File

@ -1,25 +1,27 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace OBSBlur.Window;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct POINT public struct Point
{ {
public int X; public int X;
public int Y; public int Y;
public POINT(int x, int y) public Point(int x, int y)
{ {
this.X = x; this.X = x;
this.Y = y; 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); 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() public override string ToString()

View File

@ -1,11 +1,13 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace OBSBlur.Window;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct RECT public struct Rectangle
{ {
public int Left, Top, Right, Bottom; 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; Left = left;
Top = top; Top = top;
@ -13,10 +15,10 @@ public struct RECT
Bottom = bottom; 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 public int X
{ {
get { return Left; } get { return Left; }
set { Right -= (Left - value); Left = value; } set { Right -= (Left - value); Left = value; }
} }
@ -51,37 +53,37 @@ public struct RECT
set { Width = value.Width; Height = value.Height; } 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); 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); return r1.Equals(r2);
} }
public static bool operator !=(RECT r1, RECT r2) public static bool operator !=(Rectangle r1, Rectangle r2)
{ {
return !r1.Equals(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; return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
} }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is RECT) if (obj is Rectangle)
return Equals((RECT)obj); return Equals((Rectangle)obj);
else if (obj is System.Drawing.Rectangle) 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; return false;
} }

View File

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

View File

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