Spring – Rev 1
?pathlinks?
// From xDasEinhorn @ https://www.mpgh.net/forum/showthread.php?t=1086623
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using Spring.Utilities;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Factory = SharpDX.Direct2D1.Factory;
using Point = System.Drawing.Point;
namespace Spring.HUD
{
/// <summary>
/// Fix for VS not loading classes derived from abstract classes.
/// </summary>
/// <typeparam name="TAbstract">the abstract class</typeparam>
/// <typeparam name="TBase">the derived base</typeparam>
/// <remarks>https://stackoverflow.com/questions/481305/the-designer-must-create-an-instance-of-cannot-because-the-type-is-declared-ab</remarks>
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
#region Constructors, Destructors and Finalizers
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
#endregion
#region Public Methods
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType == typeof(TAbstract))
{
return typeof(TBase);
}
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider,
Type objectType,
Type[] argTypes,
object[] args)
{
if (objectType == typeof(TAbstract))
{
objectType = typeof(TBase);
}
return base.CreateInstance(provider, objectType, argTypes, args);
}
#endregion
}
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<DirectXOverlay, Form>))]
public abstract partial class DirectXOverlay : Form
{
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private ThreadPriority ThreadPriority { get; }
private bool BackgroundThread { get; }
private WindowRenderTarget _device;
private ManualResetEvent _formLoadManualResetEvent;
private volatile bool _run;
private Thread _sDx;
#endregion
#region Constructors, Destructors and Finalizers
protected DirectXOverlay(ThreadPriority priority, bool backgroundThread) : this()
{
ThreadPriority = priority;
BackgroundThread = backgroundThread;
}
protected DirectXOverlay()
{
_formLoadManualResetEvent = new ManualResetEvent(false);
InitializeComponent();
Show();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
{
_formLoadManualResetEvent?.Dispose();
_formLoadManualResetEvent = null;
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
#region Event Handlers
private void SpringDirectXOverlay_Load(object sender, EventArgs e)
{
_device = Initialize(this);
_formLoadManualResetEvent.Set();
}
#endregion
#region Private Methods
private static WindowRenderTarget Initialize(IWin32Window form)
{
var factory = new Factory();
var renderProperties = new HwndRenderTargetProperties
{
Hwnd = form.Handle,
PixelSize = new Size2(1920, 1080),
PresentOptions = PresentOptions.None
};
// caution DirectX error
//SetLayeredWindowAttributes(this.Handle, 0, 255, Managed.LWA_ALPHA);
//Init DirectX
return new WindowRenderTarget(factory,
new RenderTargetProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied)),
renderProperties);
}
protected void Start(TimeSpan abortTime)
{
StopRenderThread(abortTime);
StartRenderThread();
}
private void StartRenderThread()
{
_run = true;
_sDx = new Thread(SDxThread) { Priority = ThreadPriority, IsBackground = BackgroundThread };
_sDx.Start(_device);
}
private void StopRenderThread(TimeSpan abortTime)
{
_run = false;
try
{
if (_sDx != null && !_sDx.Join(abortTime))
{
_sDx.Abort();
}
}
catch (ThreadStateException)
{
}
catch
{
//TODO: handle unforeseen errors
}
finally
{
_sDx = null;
}
}
protected void Stop(TimeSpan abortTime)
{
StopRenderThread(abortTime);
}
private protected abstract void SDxRendering(WindowRenderTarget render);
protected override void OnPaint(PaintEventArgs e) // create the whole form
{
int[] margins = { 0, 0, Width, Height };
DirectXOverlayNative.DwmExtendFrameIntoClientArea(Handle, ref margins);
// Set click through.
var initialStyle = DirectXOverlayNative.GetWindowLong(Handle, -20);
DirectXOverlayNative.SetWindowLong(Handle, -20, initialStyle | 0x80000 | 0x20);
}
private void SDxThread(object sender)
{
_formLoadManualResetEvent.WaitOne(-1);
do
{
try
{
SDxRendering((WindowRenderTarget) sender);
}
catch (ThreadAbortException)
{
// thread aborted, so quit
}
catch (Exception)
{
// Prevent UAC from breaking the HUD.
StopRenderThread(TimeSpan.FromSeconds(1));
this.InvokeIfRequired(form =>
{
_device = Initialize(form);
StartRenderThread();
});
}
} while (_run);
}
#endregion
#region Nested Types
internal class DirectXOverlayNative
{
#region Public Enums, Properties and Fields
[Flags]
public enum SetWindowPosFlags : uint
{
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues,
/// the system posts the request to the thread that owns the window. This prevents the calling thread from
/// blocking its execution while other threads process the request.
/// </summary>
/// <remarks>SWP_ASYNCWINDOWPOS</remarks>
SynchronousWindowPosition = 0x4000,
/// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
/// <remarks>SWP_DEFERERASE</remarks>
DeferErase = 0x2000,
/// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
/// <remarks>SWP_DRAWFRAME</remarks>
DrawFrame = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
/// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
/// is sent only when the window's size is being changed.
/// </summary>
/// <remarks>SWP_FRAMECHANGED</remarks>
FrameChanged = 0x0020,
/// <summary>Hides the window.</summary>
/// <remarks>SWP_HIDEWINDOW</remarks>
HideWindow = 0x0080,
/// <summary>
/// Does not activate the window. If this flag is not set, the window is activated and moved to the
/// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
/// parameter).
/// </summary>
/// <remarks>SWP_NOACTIVATE</remarks>
DoNotActivate = 0x0010,
/// <summary>
/// Discards the entire contents of the client area. If this flag is not specified, the valid
/// contents of the client area are saved and copied back into the client area after the window is sized or
/// repositioned.
/// </summary>
/// <remarks>SWP_NOCOPYBITS</remarks>
DoNotCopyBits = 0x0100,
/// <summary>Retains the current position (ignores X and Y parameters).</summary>
/// <remarks>SWP_NOMOVE</remarks>
IgnoreMove = 0x0002,
/// <summary>Does not change the owner window's position in the Z order.</summary>
/// <remarks>SWP_NOOWNERZORDER</remarks>
DoNotChangeOwnerZOrder = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
/// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
/// window uncovered as a result of the window being moved. When this flag is set, the application must
/// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
/// <remarks>SWP_NOREDRAW</remarks>
DoNotRedraw = 0x0008,
/// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
/// <remarks>SWP_NOREPOSITION</remarks>
DoNotReposition = 0x0200,
/// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
/// <remarks>SWP_NOSENDCHANGING</remarks>
DoNotSendChangingEvent = 0x0400,
/// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
/// <remarks>SWP_NOSIZE</remarks>
IgnoreResize = 0x0001,
/// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
/// <remarks>SWP_NOZORDER</remarks>
IgnoreZOrder = 0x0004,
/// <summary>Displays the window.</summary>
/// <remarks>SWP_SHOWWINDOW</remarks>
ShowWindow = 0x0040
}
#endregion
#region Public Methods
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
//DllImports
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd,
IntPtr hWndInsertAfter,
int x,
int y,
int cx,
int cy,
uint uFlags);
[DllImport("dwmapi.dll")]
public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref int[] pMargins);
#endregion
#region Private Methods
[DllImport("user32.dll")]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
#endregion
#region Nested Types
[StructLayout(LayoutKind.Sequential)]
public struct WindowArrangement
{
private const int HwndTop = 0;
private const int HwndBottom = 1;
public const int HwndTopmost = -1;
private const int HwndNotopmost = -2;
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
#endregion
}
#endregion
}
}