Spring – Rev 1
?pathlinks?
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.Mathematics.Interop;
using Spring.Utilities;
using FontFactory = SharpDX.DirectWrite.Factory;
using Point = System.Drawing.Point;
using TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode;
namespace Spring.HUD
{
public sealed class OSD : DirectXOverlay
{
#region Public Enums, Properties and Fields
public new string Text { get; set; }
public Configuration.Configuration Configuration { get; }
public RawColor4 SharpColor { get; set; }
public static List<Regex> DirectXRegex { get; set; }
#endregion
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private static ConcurrentDictionary<uint, bool> DirectRenderWindow { get; set; }
private IKeyboardMouseEvents KeyboardMouseEvents { get; }
private HashSet<IntPtr> DesktopWindow { get; }
private FontFactory FactoryWrite { get; }
private TextFormat TextFormat { get; }
private RectangleF TextRectangle { get; set; }
#endregion
#region Constructors, Destructors and Finalizers
public OSD(Configuration.Configuration springConfiguration, IKeyboardMouseEvents keyboardMouseEvents) : this()
{
DirectXRegex = new List<Regex>();
Configuration = springConfiguration;
foreach (var expression in Configuration.HUD.WhiteList.Whitelist)
{
try
{
var regex = new Regex(expression, RegexOptions.Compiled | RegexOptions.IgnoreCase);
DirectXRegex.Add(regex);
}
catch
{
// Skip
}
}
var color = Configuration.HUD.Color;
SharpColor = new RawColor4(((float) color.R).MapValueToRange(0, 255, 0, 1),
((float) color.G).MapValueToRange(0, 255, 0, 1),
((float) color.B).MapValueToRange(0, 255, 0, 1),
((float) color.A).MapValueToRange(0, 255, 0, 1));
KeyboardMouseEvents = keyboardMouseEvents;
Configuration.HUD.PropertyChanged += HUD_PropertyChanged;
KeyboardMouseEvents.MouseClick += KeyboardMouseEvents_MouseClick;
KeyboardMouseEvents.MouseDragStarted += KeyboardMouseEvents_MouseDragStarted;
KeyboardMouseEvents.MouseDragFinished += KeyboardMouseEvents_MouseDragFinished;
}
private OSD()
{
FactoryWrite = new FontFactory();
TextFormat = new TextFormat(FactoryWrite, "Courier New", 36);
DesktopWindow = new HashSet<IntPtr>(GetDesktopWindow());
DirectRenderWindow = new ConcurrentDictionary<uint, bool>();
Start(TimeSpan.FromSeconds(1));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Dispose stuff here
Configuration.HUD.PropertyChanged -= HUD_PropertyChanged;
KeyboardMouseEvents.MouseClick -= KeyboardMouseEvents_MouseClick;
KeyboardMouseEvents.MouseDragStarted -= KeyboardMouseEvents_MouseDragStarted;
KeyboardMouseEvents.MouseDragFinished -= KeyboardMouseEvents_MouseDragFinished;
Stop(TimeSpan.FromSeconds(1));
}
base.Dispose(disposing);
}
#endregion
#region Event Handlers
private void HUD_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Configuration.HUD = (Configuration.HUD) sender;
var color = Configuration.HUD.Color;
SharpColor = new RawColor4(((float) color.R).MapValueToRange(0, 255, 0, 1),
((float) color.G).MapValueToRange(0, 255, 0, 1),
((float) color.B).MapValueToRange(0, 255, 0, 1),
((float) color.A).MapValueToRange(0, 255, 0, 1));
RedrawWindow();
switch (Configuration.HUD.Enable)
{
case true:
Start(TimeSpan.FromSeconds(1));
break;
default:
Stop(TimeSpan.FromSeconds(1));
break;
}
}
private void KeyboardMouseEvents_MouseDragFinished(object sender, MouseEventArgs e)
{
RedrawWindow();
}
private void KeyboardMouseEvents_MouseDragStarted(object sender, MouseEventArgs e)
{
Visible = false;
}
private void KeyboardMouseEvents_MouseClick(object sender, MouseEventArgs e)
{
RedrawWindow();
}
#endregion
#region Private Methods
private void RedrawWindow()
{
var fg = GetForegroundWindow();
if (Configuration.HUD.EnableWhiteList)
{
if (!IsDirectRenderWindow(fg))
{
Visible = false;
return;
}
}
if (DesktopWindow.Contains(fg))
{
Visible = false;
return;
}
OverlayWindow(fg);
Visible = true;
}
private static bool IsDirectRenderWindow(IntPtr fg)
{
OSDNative.GetWindowThreadProcessId(fg, out var pid);
if (DirectRenderWindow.TryGetValue(pid, out var directRender))
{
return directRender;
}
var process = Process.GetProcessById((int) pid);
$"Module count: {process.Modules.Count}".ToDebugConsole();
var expressions = new Regex[DirectXRegex.Count];
DirectXRegex.CopyTo(expressions);
var regexList = new List<Regex>(expressions);
foreach (var module in process.Modules.OfType<ProcessModule>())
{
$"Module: {process.Modules.Count}".ToDebugConsole();
foreach (var regex in expressions)
{
if (regex.IsMatch(module.ModuleName))
{
regexList.Remove(regex);
if (regexList.Count == 0)
{
break;
}
}
}
}
DirectRenderWindow.TryAdd(pid, regexList.Count == 0);
return regexList.Count == 0;
}
private static IEnumerable<IntPtr> GetDesktopWindow()
{
var programManager = OSDNative.GetShellWindow();
var shellDllDefViewParent = programManager;
var shellDllDefView = OSDNative.FindWindowEx(programManager, IntPtr.Zero, "SHELLDLL_DefView", null);
var sysListView32 =
OSDNative.FindWindowEx(shellDllDefView, IntPtr.Zero, "SysListView32", "FolderView");
if (shellDllDefView == IntPtr.Zero)
{
OSDNative.EnumWindows((hWnd, lParam) =>
{
var sb = new StringBuilder();
OSDNative.GetClassName(hWnd, sb, 8);
if (sb.ToString() != "WorkerW")
{
return true;
}
var child = OSDNative.FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (child == IntPtr.Zero)
{
return true;
}
shellDllDefViewParent = hWnd;
shellDllDefView = child;
sysListView32 =
OSDNative.FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView");
return false;
},
0);
}
return new[] { programManager, shellDllDefViewParent, shellDllDefView, sysListView32 };
}
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private protected override void SDxRendering(WindowRenderTarget render)
{
if (Configuration == null || Configuration.HUD == null || !Configuration.HUD.Enable)
{
return;
}
if (render == null)
{
return;
}
render.TextAntialiasMode = TextAntialiasMode.Aliased;
render.AntialiasMode = AntialiasMode.Aliased;
render.BeginDraw();
render.Clear(Color.Transparent);
if (!string.IsNullOrEmpty(Text))
{
using (var textBrush = new SolidColorBrush(render, SharpColor))
{
render.DrawText(Text, TextFormat, TextRectangle, textBrush);
}
}
render.EndDraw();
}
private void OverlayWindow(IntPtr hWnd)
{
OSDNative.GetWindowRect(hWnd, out var fgRect);
var w = fgRect.Right - fgRect.Left;
var h = fgRect.Bottom - fgRect.Top;
TextRectangle = new RectangleF(0, 0, w, 50);
var x = (int) (Configuration.HUD.Position.X * w / 100f);
var y = (int) (Configuration.HUD.Position.Y * h / 100f);
var point = new Point(x, y);
OSDNative.ClientToScreen(hWnd, ref point);
Left = point.X;
Top = (int) (point.Y - TextRectangle.Height);
Width = w;
Height = h;
}
#endregion
#region Nested Types
public class Symbols
{
#region Static Fields and Constants
public static Func<string> HUDShown = () => "☍";
public static Func<string> KeyPressed = () => "(☍)";
public static Func<string> KeyReleased = () => "☍";
public static Func<string, string> KeyDown = arg => $"(☍)x{arg}";
public static Func<string, string> DischargeProgress = arg => $"☍⤳{arg}";
public static Func<string, string> DischargeStop = arg => $"☍x{arg}";
public static Func<string> ChargeStart = () => "☍⬿";
public static Func<string, string> ChargeStop = arg => $"☍x{arg}";
#endregion
}
internal class OSDNative
{
#region Public Events & Delegates
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
#endregion
#region Public Methods
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lPdwProcessId);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
[DllImport("user32.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder buf, int nMaxCount);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out DirectXOverlayNative.Rect lpRect);
[DllImport("user32.DLL")]
public static extern IntPtr GetShellWindow();
[DllImport("user32.DLL")]
public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hWndParent,
IntPtr hWndChildAfter,
string lpszClass,
string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
#endregion
}
#endregion
}
}